Compare commits

..

438 Commits

Author SHA1 Message Date
gemini-cli-robot 61227ea90a chore(release): v0.18.4 2025-11-26 21:37:13 +00:00
gemini-cli-robot 2e8d7831c6 fix(patch): cherry-pick 030a5ac to release/v0.18.3-pr-13565 [CONFLICTS] (#13869)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-11-26 21:22:14 +00:00
gemini-cli-robot 3f4d5c07b3 chore(release): v0.18.3 2025-11-26 19:27:05 +00:00
gemini-cli-robot 4b19a83388 fix(patch): cherry-pick d2a6cff to release/v0.18.2-pr-13827 [CONFLICTS] (#13863)
Co-authored-by: Billy Biggs <bbiggs@google.com>
Co-authored-by: Sandy Tao <sandytao520@icloud.com>
2025-11-26 11:13:05 -08:00
gemini-cli-robot 8c9b49ce8a chore(release): v0.18.2 2025-11-26 18:22:06 +00:00
gemini-cli-robot 9b6d47fd52 fix(patch): cherry-pick fe67ef6 to release/v0.18.1-pr-13596 to patch version v0.18.1 and create version 0.18.2 (#13862)
Co-authored-by: joshualitt <joshualitt@google.com>
2025-11-26 17:59:45 +00:00
gemini-cli-robot 236af8bb0a chore(release): v0.18.1 2025-11-26 17:39:38 +00:00
gemini-cli-robot 013f984842 fix(patch): cherry-pick bdf80ea to release/v0.18.0-pr-13600 to patch version v0.18.0 and create version 0.18.1 (#13861)
Co-authored-by: christine betts <chrstn@uw.edu>
Co-authored-by: jacob314 <jacob314@gmail.com>
2025-11-26 09:26:00 -08:00
gemini-cli-robot f01890b073 chore(release): v0.18.0 2025-11-26 02:50:41 +00:00
gemini-cli-robot 7beccfa03c chore(release): v0.18.0-preview.4 2025-11-25 22:02:36 +00:00
gemini-cli-robot a640766f10 fix(patch): cherry-pick d8a3d08 to release/v0.18.0-preview.3-pr-13791 to patch version v0.18.0-preview.3 and create version 0.18.0-preview.4 (#13826)
Co-authored-by: Sehoon Shon <sshon@google.com>
2025-11-25 21:42:55 +00:00
gemini-cli-robot 9f55fb5010 chore(release): v0.18.0-preview.3 2025-11-25 19:31:29 +00:00
gemini-cli-robot ea3d022c8b fix(patch): cherry-pick 3e50be1 to release/v0.18.0-preview.2-pr-13428 to patch version v0.18.0-preview.2 and create version 0.18.0-preview.3 (#13821)
Co-authored-by: Adib234 <30782825+Adib234@users.noreply.github.com>
2025-11-25 11:10:54 -08:00
gemini-cli-robot 313688fd18 chore(release): v0.18.0-preview.2 2025-11-25 18:14:40 +00:00
gemini-cli-robot 843b019cef fix(patch): cherry-pick d351f07 to release/v0.18.0-preview.1-pr-12535 to patch version v0.18.0-preview.1 and create version 0.18.0-preview.2 (#13813)
Co-authored-by: Jack Wotherspoon <jackwoth@google.com>
2025-11-25 17:20:20 +00:00
gemini-cli-robot 80ef6f854b chore(release): v0.18.0-preview.1 2025-11-22 02:14:04 +00:00
gemini-cli-robot 316349ca61 fix(patch): cherry-pick 5e218a5 to release/v0.18.0-preview.0-pr-13623 to patch version v0.18.0-preview.0 and create version 0.18.0-preview.1 (#13626)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-11-22 01:58:57 +00:00
gemini-cli-robot 179010eb3b chore(release): v0.18.0-preview.0 2025-11-20 22:19:18 +00:00
joshualitt 8e531dc029 feat(core): Incorporate Gemini 3 into model config hierarchy. (#13447) 2025-11-20 21:07:14 +00:00
Ben Brandt 83d0bdc32e feat(zed-integration): Use default model routing for Zed integration (#13398)
Co-authored-by: Shreya Keshive <shreyakeshive@google.com>
2025-11-20 20:08:37 +00:00
Tommaso Sciortino d15970e12c remove duplicated mouse code (#13525) 2025-11-20 11:46:53 -08:00
Tommaso Sciortino c7b5dcd28f Change default compress threshold to 0.5 for api key users (#13517) 2025-11-20 11:43:35 -08:00
joshualitt 8c07ad2ab9 feat(core): Use thinking level for Gemini 3 (#13445) 2025-11-20 19:19:52 +00:00
Adib234 ade9dfeebb Enable switching preview features on/off without restart (#13515) 2025-11-20 19:00:16 +00:00
Jacob Richman d1e35f8660 Protect stdout and stderr so JavaScript code can't accidentally write to stdout corrupting ink rendering (#13247)
Bypassing rules as link checker failure is spurious.
2025-11-20 10:44:02 -08:00
Adib234 e20d282088 Update banner design (#13420) 2025-11-20 16:52:15 +00:00
Gaurav Sehgal 4adfdad47f Copy commands as part of setup-github (#13464) 2025-11-20 16:05:15 +00:00
Ben Brandt 6c126b9e58 fix(zed-integration): Ensure that the zed integration is classified as interactive (#13394)
Co-authored-by: Shreya Keshive <shreyakeshive@google.com>
2025-11-20 15:28:37 +00:00
lifefloating 1d2e27a698 Fix: Update system instruction when GEMINI.md memory is loaded or refreshed (#12136) 2025-11-20 13:22:06 +00:00
Adam Weidman 049a299b92 Revert "feat(core): Fix bug with incorrect model overriding." (#13483) 2025-11-20 06:39:21 +00:00
Sandy Tao b1258dd52c fix(cli): prevent race condition when restoring prompt after context overflow (#13473) 2025-11-20 05:54:16 +00:00
Greg Shikhman 98cdaa01b8 Revert "build bun"
This reverts commit b0938f9102.
2025-11-20 06:02:54 +00:00
Greg Shikhman 3476a97acc build bun 2025-11-20 06:02:54 +00:00
Tommaso Sciortino 9ebf321717 Use synchronous writes when detecting keyboard modes (#13478) 2025-11-20 05:50:05 +00:00
joshualitt ff725dea41 feat(core): Fix bug with incorrect model overriding. (#13477) 2025-11-20 05:48:08 +00:00
gemini-cli-robot c3f1b29c1e chore/release: bump version to 0.18.0-nightly.20251120.2231497b1 (#13476) 2025-11-20 05:05:32 +00:00
joshualitt 257cd07a3a feat(core): Wire up chat code path for model configs. (#12850) 2025-11-20 04:41:16 +00:00
Preston Holmes 43d6dc3668 Add User email detail to about box (#13459) 2025-11-20 03:46:21 +00:00
Gal Zahavi 2231497b1f feat: add click-to-focus support for interactive shell (#13341) 2025-11-19 23:49:39 +00:00
Tommaso Sciortino d0a845b6e6 remove unneeded log (#13456) 2025-11-19 23:42:21 +00:00
kevin-ramdass e4c4bb265e feat: update thinking mode support to exclude gemini-2.0 models and simplify logic. (#13454) 2025-11-19 23:35:35 +00:00
Smetalo 0f845407f1 Fix typo in write_todos methodology instructions (#13411) 2025-11-19 23:29:12 +00:00
Tommaso Sciortino b2a2ea3633 Fix Copyright line in LICENSE (#13449) 2025-11-19 22:53:34 +00:00
niladri das f8a862738d [cli-ui] Keep header ASCII art colored on non-gradient terminals (#13373) (#13374)
Co-authored-by: Niladri Das <bniladridas@users.noreply.github.com>
2025-11-19 22:04:58 +00:00
joshualitt 25f8452127 feat(core, cli): Bump genai version to 1.30.0 (#13435) 2025-11-19 20:52:27 +00:00
Tommaso Sciortino 84573992b4 Restore keyboard mode when exiting the editor (#13350) 2025-11-19 19:37:30 +00:00
Holt Skinner 3f8d636501 docs: Add Code Wiki link to README (#13289) 2025-11-19 18:15:41 +00:00
Ben Brandt 300205b07c fix(zed-integration): Correctly handle cancellation errors (#13399) 2025-11-19 17:25:52 +00:00
bl-ue e1c711f5ba feat(sessions): record interactive-only errors and warnings to chat recording JSON files (#13300) 2025-11-19 16:32:13 +00:00
Jainam M 282654e7b8 Capturing github action workflow name if present and send it to clearcut (#13132) 2025-11-19 16:30:03 +00:00
bl-ue 0d89ac7406 Followup from #10719 (#13243) 2025-11-19 16:22:17 +00:00
davidmcwherter 5c47592159 Refactor createTransport to duplicate less code (#13010)
Co-authored-by: David McWherter <davidmcw@gmail.com>
2025-11-19 15:23:01 +00:00
Jack Wotherspoon 61f0f3c243 fix: allow MCP prompts with spaces in name (#12910)
Co-authored-by: Abhi <43648792+abhipatel12@users.noreply.github.com>
2025-11-19 14:44:30 +00:00
avilladsen 1e8ae5b9d7 fix(cli): fix crash on startup in NO_COLOR mode (#13343) due to ungua… (#13352) 2025-11-19 04:18:18 +00:00
Jacob Richman e8d0e0d342 bug(ui) showLineNumbers had the wrong default value. (#13356) 2025-11-19 03:33:40 +00:00
Sandy Tao b644f037a3 fix(ui): Clear input prompt on Escape key press (#13335) 2025-11-19 03:11:36 +00:00
Gal Zahavi c5498bbb07 Improve pty resize error handling for Windows (#13353) 2025-11-19 02:08:01 +00:00
Tommaso Sciortino 90c764ce13 Support 3-parameter modifyOtherKeys sequences (#13342) 2025-11-19 00:40:40 +00:00
joshualitt 10003a6490 fix(core): Ensure read_many_files tool is available to zed. (#13338) 2025-11-18 22:58:12 +00:00
Tommaso Sciortino b916d79fe2 Improve keyboard code parsing (#13307) 2025-11-18 19:49:08 +00:00
Gray Mackall 36b0a86c69 Fix links in Gemini 3 Pro documentation (#13312) 2025-11-18 19:39:51 +00:00
Jenna Inouye 7cc5234b9c Docs: Access clarification. (#13304) 2025-11-18 19:31:51 +00:00
Shreya Keshive 379f09d92f chore(release): v0.18.0-nightly.20251118.86828bb56 (#13309) 2025-11-18 19:04:29 +00:00
Aswin Ashok fd9d3e19a9 Remove obsolete reference to "help wanted" label in CONTRIBUTING.md (#13291) 2025-11-18 18:17:07 +00:00
Shreya Keshive 86828bb561 feat: launch Gemini 3 in Gemini CLI 🚀🚀🚀 (in main) (#13287)
Co-authored-by: Adam Weidman <65992621+adamfweidman@users.noreply.github.com>
Co-authored-by: Sehoon Shon <sshon@google.com>
Co-authored-by: Adib234 <30782825+Adib234@users.noreply.github.com>
Co-authored-by: Sandy Tao <sandytao520@icloud.com>
Co-authored-by: Abhi <43648792+abhipatel12@users.noreply.github.com>
Co-authored-by: Aishanee Shah <aishaneeshah@gmail.com>
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
Co-authored-by: Gal Zahavi <38544478+galz10@users.noreply.github.com>
Co-authored-by: Jacob Richman <jacob314@gmail.com>
Co-authored-by: joshualitt <joshualitt@google.com>
Co-authored-by: Jenna Inouye <jinouye@google.com>
2025-11-18 09:01:16 -08:00
Jenna Inouye 78075c8a37 Docs: Add changelog for v.0.15.0 (#13276) 2025-11-18 16:09:55 +00:00
JAYADITYA 7d33baabe4 feat :uninstall multiple extensions (#13016)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-11-18 15:20:25 +00:00
JAYADITYA ecf8fba10e feat: Show tip on first request and refactor phrases (#12952)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-11-18 07:02:31 +00:00
Tommaso Sciortino 2f8b68dffd update glob version (#13242) 2025-11-18 00:31:17 +00:00
Tommaso Sciortino 8877c85278 Right click to paste in Alternate Buffer mode (#13234) 2025-11-17 23:48:33 +00:00
Riddhi Dutta 1d1bdc57ce Refactored 4 files of tools package (#13235)
Co-authored-by: riddhi <duttariddhi@google.com>
2025-11-17 19:31:29 +00:00
Tommaso Sciortino ba88707b1e Fix test to not leave terminal in mouse mode (#13232) 2025-11-17 17:47:08 +00:00
cornmander 8c78fe4f10 rework MCP tool discovery and invocation (#13160)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-11-17 17:03:48 +00:00
Riddhi Dutta 394a7ea019 Refactored 3 files of tools package (#13231)
Co-authored-by: riddhi <duttariddhi@google.com>
2025-11-17 16:54:09 +00:00
Jacob Richman 78a28bfc01 Fix: Animated scrollbar renders black in NO_COLOR mode (#13188) 2025-11-17 04:45:07 +00:00
gemini-cli-robot cf8de02c68 chore/release: bump version to 0.17.0-nightly.20251116.e650a4ee5 (#13154) 2025-11-16 02:32:39 +00:00
Riddhi Dutta e650a4ee59 Refactored core package ut (#13139)
Co-authored-by: riddhi <duttariddhi@google.com>
2025-11-15 17:23:21 +00:00
Samuel Soto c6b6dcbe95 Docs: Clarify Project-Scoped Behavior of Chat Sub-commands (#10458)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-11-15 15:50:30 +00:00
shrutip90 9786c4dcff Check folder trust before allowing add directory (#12652) 2025-11-15 03:06:30 +00:00
Tommaso Sciortino d03496b710 Increase paste timeout + add warning. (#13099) 2025-11-15 00:02:28 +00:00
Jacob Richman ab6b2293e1 Only warn about mouse movement when the mouse is down. (#13101) 2025-11-14 22:55:05 +00:00
shrutip90 472e775a13 feat: Update permissions command to support modifying trust for other… (#11642) 2025-11-14 22:41:53 +00:00
Adib234 ce56b4ee1b Change flag name to flag id for existing flags (#13073) 2025-11-14 21:34:54 +00:00
Jacob Richman ba15eeb55f bug(ui) make it clear when users need to enter selection mode and fix clear issue. (#13083) 2025-11-14 20:02:15 +00:00
shrutip90 d683e1c0db fix(cli): Exit CLI when trust save unsuccessful during launch (#11968) 2025-11-14 19:56:39 +00:00
Tommaso Sciortino 6d83d3440c Change default compress threshold to 0.7 for api key users (#13079) 2025-11-14 17:13:59 +00:00
Caroline Rose 9d74b7c0e8 feat(auth): Add option for metadata server application default credentials without project override (#12948) 2025-11-14 16:39:11 +00:00
Stone Sun 016b5b42e2 docs: remove references to deprecated --checkpointing flag (#12477)
Co-authored-by: Jack Wotherspoon <jackwoth@google.com>
2025-11-14 16:19:12 +00:00
YongSung Na a591505bf9 docs: remove references to deprecated flags (--checkpointing, --sandbox-image) (#12578)
Co-authored-by: Jack Wotherspoon <jackwoth@google.com>
2025-11-14 15:28:14 +00:00
Megha Bansal 638dd2f6c0 Improve test code coverage for cli/command/extensions package (#12994) 2025-11-14 05:28:01 +00:00
Tommaso Sciortino 555e25e633 slightly adjust model message formatting (#13043) 2025-11-14 05:13:39 +00:00
gemini-cli-robot 3174573b64 chore/release: bump version to 0.17.0-nightly.20251114.0fcbff506 (#13040) 2025-11-14 04:07:27 +00:00
Tommaso Sciortino 2c5e09e1c3 Update comment and undo unnecessary logging (#13025) 2025-11-14 03:11:13 +00:00
Tommaso Sciortino ab11b2c27f Show model in history (#13034) 2025-11-14 03:11:06 +00:00
gemini-cli-robot 0fcbff506e chore(release): bump version to 0.17.0-nightly.20251113.48e3932f6 (#13027) 2025-11-14 00:19:33 +00:00
joshualitt 48e3932f65 feat(core, cli): Add auth type to history checkpoint. (#13023) 2025-11-13 23:13:39 +00:00
Jacob Richman d304216300 Polish sticky headers (#13024) 2025-11-13 22:33:48 +00:00
Jacob Richman 2b8adf8cf3 jacob314/drag scrollbar (#12998) 2025-11-13 22:13:18 +00:00
Jacob Richman fb99b95372 Switch back to truncating headers. Unfortunately we have too many headers that are 10+ lines for no good reason so we need to add back this truncation until we fix them. (#13018) 2025-11-13 19:38:19 +00:00
Jacob Richman 60fe5acd60 feat(ui) support animated page up/down, fn-up/down and end+home (#13012) 2025-11-13 19:16:23 +00:00
Jacob Richman eb9ff72b5a Support incremental update experiment flag. (#12926) 2025-11-13 17:45:03 +00:00
Tommaso Sciortino fb03242950 Improve MALFORMED_FUNCTION_CALL handling (#12965) 2025-11-13 16:07:49 +00:00
Adam Weidman 54c1e13853 chore: update package lock (#12974) 2025-11-13 14:50:14 +00:00
N. Taylor Mullen 102905bbc7 feat: normalize verticality (#12991) 2025-11-13 08:20:14 +00:00
N. Taylor Mullen fe1bfc64f7 feat: disengage surface adhesion protocols (#12989) 2025-11-13 07:36:18 +00:00
Jacob Richman 0f9ec2735c feat(ui) Make useAlternateBuffer the default (#12976) 2025-11-13 05:17:46 +00:00
Abhi 13d8d9477c fix(editor): ensure preferred editor setting updates immediately (#12981) 2025-11-13 04:02:53 +00:00
Tommaso Sciortino 43916b98aa Don't clear buffers on cleanup. (#12979) 2025-11-13 03:20:21 +00:00
Abhi 5d27a62bec refactor: remove read-many-files tool from agent (#12796) 2025-11-13 02:56:37 +00:00
gemini-cli-robot a05e0ea3a4 chore/release: bump version to 0.16.0-nightly.20251113.ad1f0d99 (#12977) 2025-11-13 02:03:52 +00:00
anthony bushong 2987b473dd feat(core): set default chat base model configs (#12951) 2025-11-13 01:01:31 +00:00
Jacob Richman ee7065f665 Sticky headers where the top rounded border is sticky. (#12971) 2025-11-13 01:01:16 +00:00
anthony bushong d26b828ab3 feat(core): update default model config in client.ts (#12960) 2025-11-13 00:52:04 +00:00
Sehoon Shon a810ca80bc Allow users to reset to auto when it's in fallback mode (#12623) 2025-11-13 00:30:43 +00:00
Allen Hutchison ad1f0d995d refactor: move toml-loader.test.ts to use real filesystem (#12969) 2025-11-12 23:17:38 +00:00
Allen Hutchison 1ed163a666 feat(safety): Introduce safety checker framework (#12504) 2025-11-12 21:18:34 +00:00
cornmander aa9922bc98 feat: autogenerate keyboard shortcut docs (#12944) 2025-11-12 21:07:14 +00:00
Tommaso Sciortino 0075b4f118 Always show the tool internal name in /tools (#12964) 2025-11-12 20:45:18 +00:00
Jacob Richman 4d85ce40be Turns out the node console.clear() clears the buffer. (#12959) 2025-11-12 20:15:25 +00:00
anj-s 7ec78452ec Enable write_todo tool and fix output function schema (#12905) 2025-11-12 18:18:15 +00:00
Abhi 540f60696a fix(docs): Release version for read many files removal (#12949) 2025-11-12 18:05:16 +00:00
Shardul Natu 1ffb9c4188 fix(FileCommandLoader): Remove error logs if the operation was aborted (#12927)
Co-authored-by: Shnatu <snatu@google.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-11-12 16:33:28 +00:00
Shreya Keshive 570ccc7da0 feat: Update client metadata for consistency (#12947) 2025-11-12 16:11:48 +00:00
Edilmo Palencia 1c8fe92d0f feat(hooks): Hook Result Aggregation (#9095) 2025-11-12 07:14:09 +00:00
gemini-cli-robot 396b427cc9 chore/release: bump version to 0.16.0-nightly.20251112.c961f274 (#12923) 2025-11-12 06:13:46 +00:00
gemini-cli-robot c961f27401 chore(release): bump version to 0.16.0-nightly.20251112.2abc288c (#12917)
Co-authored-by: Sandy Tao <sandytao520@icloud.com>
2025-11-12 05:14:20 +00:00
Abhi 1c87e7cd25 feat: enhance RipGrep tool with advanced search options and improved defaults (#12677) 2025-11-12 05:11:19 +00:00
Sandy Tao 408b885689 feat(core): enhance loop detection with 2-stage check (#12902) 2025-11-12 04:49:00 +00:00
N. Taylor Mullen cab9b1f370 Fix extensions disable/enable commands not awaiting handler (#12915) 2025-11-12 04:31:46 +00:00
Tommaso Sciortino d3cf28eb4b Use PascalCase for all tool display names (#12918) 2025-11-12 04:28:13 +00:00
Megha Bansal 11a0a9b911 Increase code coverage for core packages (#12872) 2025-11-12 04:06:43 +00:00
Tommaso Sciortino e8038c727f fix test to use faketimer (#12913) 2025-11-12 03:03:10 +00:00
Jacob Richman 2abc288c52 Make useFullWidth the default. (#12911) 2025-11-12 00:14:08 +00:00
Tommaso Sciortino 2e2b066713 Move temp dir from system prompt to first user msg (#12895) 2025-11-11 21:22:22 +00:00
Abhi c1076512d1 Deprecate read_many_files tool (#12861) 2025-11-11 20:35:20 +00:00
Abhi 3154c06dc3 fix(ci): pre-download ripgrep in global setup to prevent race conditions (#12886) 2025-11-11 18:50:17 +00:00
kevinjwang1 b248ec6dfb Add implementation for setting to disable Github extensions (#12838) 2025-11-11 18:37:01 +00:00
Shardul Natu cc2c48d59e fix(extension-uninstallation): Fix uninstalling extensions named differently than their directory name (#12852)
Co-authored-by: Shnatu <snatu@google.com>
2025-11-11 18:19:06 +00:00
Tommaso Sciortino ac733d40b3 Add expected_replacements to smart-edit tool (#12885) 2025-11-11 18:17:45 +00:00
joshualitt a4415f15d3 feat(core): Migrate generateContent to model configs. (#12834) 2025-11-11 16:10:50 +00:00
Jacob Richman cbbf565121 Support ink scrolling final pr (#12567) 2025-11-11 15:50:11 +00:00
N. Taylor Mullen 7bb13d1c41 telemetry: track interactive session state (#12856)
Co-authored-by: owenofbrien <86964623+owenofbrien@users.noreply.github.com>
2025-11-11 10:03:32 +00:00
gemini-cli-robot e79f62694a chore/release: bump version to 0.15.0-nightly.20251111.51f952e7 (#12860) 2025-11-11 07:04:14 +00:00
cornmander 4fbeac8b32 Add experiment logging and add caching experiment (#12862) 2025-11-11 04:15:38 +00:00
Megha Bansal 22b0550520 Issue#9751 - fix the gemini crash on startup in tmux environments (#11637) 2025-11-11 03:57:43 +00:00
Mark Thompson 5ba6bc7139 fix(prompt): Add Angular support to base prompt (#10252) 2025-11-11 03:42:22 +00:00
bl-ue 6893d27441 feat(sessions): add resuming to geminiChat and add CLI flags for session management (#10719)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-11-11 01:31:00 +00:00
Abhi 51f952e700 fix(core): use ripgrep --json output for robust cross-platform parsing (#12853) 2025-11-11 00:01:01 +00:00
davidmcwherter 4af4f8644d Fixes #12841: Plumb headers through google_credentials transport crea… (#12849)
Co-authored-by: David McWherter <davidmcw@gmail.com>
2025-11-10 22:06:31 +00:00
cornmander 37ca643a64 Fix external editor diff drift (#12846) 2025-11-10 21:19:32 +00:00
Jacob Richman 8f4b1b582d Switch to alternate buffer mode before rendering Ink. (#12833) 2025-11-10 20:30:38 +00:00
Tommaso Sciortino 3c9052a751 Stop printing garbage characters for F1,F2.. keys (#12835) 2025-11-10 18:56:05 +00:00
cornmander 2136598e87 Harden modifiable tool temp workspace (#12837)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-11-10 18:48:44 +00:00
owenofbrien 3f90001f83 Added active experiment ids to Clearcut log events (#12745) 2025-11-10 17:21:49 +00:00
Edilmo Palencia 4ef4bd6f09 feat(hooks): Hook Execution Engine (#9092) 2025-11-10 16:08:21 +00:00
Adib234 331dbd5638 Preserve tabs on paste (#12735) 2025-11-10 15:37:43 +00:00
Hriday 4ab94dec5e test: fix flaky file system integration test (#12724)
Co-authored-by: Hriday Taneja <hridayt@google.com>
2025-11-10 06:20:30 +00:00
Tommaso Sciortino c0b766ad74 Simplify switch case (#12798) 2025-11-09 22:36:13 +00:00
Tommaso Sciortino fd59d9dd92 Fix shift+return in vscode. (#12799) 2025-11-09 22:08:29 +00:00
Tommaso Sciortino 9e4ae214a8 Revamp KeypressContext (#12746) 2025-11-09 16:45:04 +00:00
Jacob Richman f649948713 Branch batch scroll (#12680) 2025-11-09 00:09:22 +00:00
christine betts 43b8731241 Fix extensions logging race condition and slash command logging (#12732) 2025-11-08 15:29:36 +00:00
Jacob MacDonald 9116cf2bab [cleanup] rename info message property 'icon' to 'prefix' (#12743) 2025-11-08 00:07:33 +00:00
Jacob MacDonald bafbcbbe8b [feat]: Add /extensions restart command (#12739) 2025-11-07 23:17:23 +00:00
joshualitt fdb6088603 feat(core): Migrate generateJson to resolved model configs. (#12626) 2025-11-07 22:18:45 +00:00
Sandy Tao f3a8b73717 fix(ci): ensure correct version calculation and git ls-remote filtering in release workflow (#12683) 2025-11-07 21:06:55 +00:00
Jacob MacDonald c883403147 [feat] Extension Reloading - respect updates to exclude tools (#12728) 2025-11-07 20:18:35 +00:00
joshualitt 2077521f84 Trivial yaml fixes for linter. (#12725) 2025-11-07 18:37:36 +00:00
Shreya Keshive 6d90b7ddb3 feat(issue-templates): Refine issue template labels and types
Updated  and  to use a  field instead of  and  labels.
Modified  to use  instead of  and removed .

This change streamlines issue categorization and aligns with updated labeling conventions.
2025-11-07 12:46:18 -05:00
Jacob Richman 6cf1c98524 Update ink version (#12720) 2025-11-07 17:32:46 +00:00
Allen Hutchison 5ff7cdc9e8 test(policy): add extreme priority value tests (follow-up to #12617) (#12679) 2025-11-07 17:28:05 +00:00
Jacob MacDonald 47603ef8e1 Reload gemini memory on extension load/unload + memory refresh refactor (#12651) 2025-11-07 17:07:25 +00:00
Lê Nam Khánh ef4030331a docs: fix typos in some files (#12706)
Co-authored-by: Jack Wotherspoon <jackwoth@google.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-11-07 15:51:56 +00:00
Haroon Chaudhry a0a6828265 fix: Downloading release assets from private GitHub repository #11928 (#12682) 2025-11-07 15:50:44 +00:00
cocosheng-g 69339f08a6 Adds listCommands endpoint to a2a server (#12604)
Co-authored-by: Juanda <jdgarrido@google.com>
Co-authored-by: Shreya Keshive <shreyakeshive@google.com>
2025-11-07 15:10:29 +00:00
gemini-cli-robot cd27cae848 chore(release): bump version to 0.15.0-nightly.20251107.b8eeb553 (#12687)
Co-authored-by: Sandy Tao <sandytao520@icloud.com>
2025-11-07 04:29:38 +00:00
Tommaso Sciortino 475e92da5d Fix test in windows (#12681) 2025-11-07 04:01:30 +00:00
Shreya Keshive 054497c7a6 fix(core): Handle null command in VSCode IDE detection (#12689) 2025-11-07 03:01:53 +00:00
Allen Hutchison c81a02f8d2 fix: integrate DiscoveredTool with Policy Engine (#12646) 2025-11-06 23:51:16 +00:00
Richie Foreman 445a5eac33 fix(patch workflow): Ensure that the environment is listed on patch comments (#12538) 2025-11-06 23:30:20 +00:00
Tommaso Sciortino f05d937f39 Use consistent param names (#12517) 2025-11-06 23:03:52 +00:00
HugoMurillo 5f1208ad81 chore: disable flaky test (#12670) 2025-11-06 22:43:55 +00:00
Adam Weidman 1e42fdf6c2 fix(cli): handle flash model errors gracefully (#12667) 2025-11-06 22:13:38 +00:00
Jenna Inouye 98055d0989 Docs: Add /model documentation (#12654) 2025-11-06 21:12:52 +00:00
christine betts c13ec85d7d Update keychain storage name to be more user-friendly (#12644) 2025-11-06 20:55:20 +00:00
Sandy Tao 36feb73bfd Revert "Enable WriteTodos tool by default (#12500)" (#12658) 2025-11-06 20:21:53 +00:00
HugoMurillo 77614eff5b fix(#11707): should replace multiple instances of a string test (#12647) 2025-11-06 19:12:06 +00:00
Sehoon Shon 31b34b11ab Let users know when falling back to flash, and update the error messa… (#12640) 2025-11-06 18:43:21 +00:00
Jainam M c585470a71 refactor(cli): consolidate repetitive tests in InputPrompt using it.each (#12524) 2025-11-06 17:41:50 +00:00
Gal Zahavi 9ba1cd0336 feat(shell): include cwd in shell command description (#12558) 2025-11-06 16:51:07 +00:00
Allen Hutchison 5f6453a1e0 feat(policy): Add comprehensive priority range validation tests (#12617) 2025-11-06 07:19:05 +00:00
joshualitt 956ab94452 feat(core): Add ModelConfigService. (#12556) 2025-11-06 01:18:42 +00:00
Allen Hutchison 0f5dd2229c chore: remove unused CLI policy TOML files (#12620) 2025-11-06 01:17:44 +00:00
Jacob Richman 224a33db2e Improve tracking of animated components. (#12618) 2025-11-06 00:20:04 +00:00
Jenna Inouye fb0768f007 Docs: Added newest changelog: v0.12.0 (#12611) 2025-11-05 23:44:24 +00:00
Tommaso Sciortino 9787108532 List tools in a consistent order. (#12615) 2025-11-05 23:38:44 +00:00
Andrew Riggs 44b8c62db9 fix(core) Path reader method readPathFromWorkspace does not respect git/gemini ignore config. (#10073)
Co-authored-by: Tommaso Sciortino <sciortino@gmail.com>
2025-11-05 21:40:54 +00:00
Silvio Junior 1d2f90c7e7 Add compression mechanism to subagent (#12506) 2025-11-05 21:15:28 +00:00
Adam Weidman c951f9fdcd fix: add line breaks in quota/capacity msgs (#12603) 2025-11-05 21:12:06 +00:00
Jacob MacDonald fa93b56243 [Extension Reloading]: Update custom commands, add enable/disable command (#12547) 2025-11-05 19:36:07 +00:00
Tommaso Sciortino ca6cfaaf4e Update auto compression message. (#12605) 2025-11-05 19:08:57 +00:00
Shreya Keshive 400da30a8d fix(triage-workflow): Pass environment variables directly into prompt (#12602) 2025-11-05 18:33:33 +00:00
Allen Hutchison f5bd474e51 fix(core): prevent server name spoofing in policy engine (#12511) 2025-11-05 18:10:23 +00:00
Bryan Morgan 16113647de Fix/windows pty crash (#12587)
Co-authored-by: LayorX <yor31117@gmail.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-11-05 16:53:03 +00:00
Gaurav f51d74586c refactor: parse string for retryInfo (#12586) 2025-11-05 16:42:58 +00:00
Shreya Keshive c743631148 chore(release): bump version to 0.14.0-nightly.20251104.da3da198 (#12564) 2025-11-05 16:09:33 +00:00
shishu314 b445db3d46 fix(infra) - Make list dir less flaky (#12554)
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
2025-11-05 16:00:06 +00:00
Adam Weidman 21dd9bbf7d fix: allow user to set pro model even in fallback (#12566) 2025-11-05 14:38:45 +00:00
Jacob Richman 3937461272 Scrollable support (#12544) 2025-11-05 00:21:00 +00:00
Shreya Keshive da3da19844 feat(core): Integrate remote experiments configuration (#12539) 2025-11-04 23:09:53 +00:00
Silvio Junior 53c7646ed7 enable codebase investigator by default for preview (#12555) 2025-11-04 22:30:01 +00:00
Sehoon Shon b6524e410a migrate console.error to coreEvents/debugger for sandbox, logger, chatRecordingService (#12253) 2025-11-04 21:02:31 +00:00
shishu314 75c2769b32 Ss/fix ext (#12540)
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
2025-11-04 20:01:30 +00:00
Tommaso Sciortino f796650122 Fix shift+tab keybinding when not in kitty mode (#12552) 2025-11-04 19:45:22 +00:00
christine betts 460c3debf5 Fix flicker in screen reader nudge (#12541) 2025-11-04 16:57:15 +00:00
cornmander b8b6620365 Tighten bash shell option handling (#12532) 2025-11-04 16:11:29 +00:00
shishu314 96d7eb2966 fix(infra) - Use canned response for flicker test (#12377)
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
2025-11-04 16:08:46 +00:00
Jacob MacDonald da4fa5ad75 Extensions MCP refactor (#12413) 2025-11-04 15:51:18 +00:00
anj-s 2b77c1ded4 SI prompt nudge for the todo tool (#12159) 2025-11-04 14:25:15 +00:00
Jainam M 6ab1b239ca refactor(core): Refactored and removed redundant test lines in telemetry (#12356) 2025-11-04 08:42:31 +00:00
Chris Coutinho ab73051298 fix(mcp): replace hardcoded port 7777 with dynamic port allocation for OAuth (#12520) 2025-11-04 07:16:56 +00:00
Edilmo Palencia cb2880cb93 feat(hooks): Hook Execution Planning and Matching (#9090) 2025-11-04 06:25:43 +00:00
Tommaso Sciortino b5315bfc20 Fix alt+left on ghostty (#12503) 2025-11-04 04:30:42 +00:00
Edilmo Palencia b259153403 feat(hooks): Hook Input/Output Contracts (#9080) 2025-11-04 02:47:23 +00:00
Sandy Tao f0c3c81e94 fix(core): Improve loop detection for longer repeating patterns (#12505) 2025-11-04 02:06:22 +00:00
christine betts fc42c4613f Only show screen reader notice once (#12247) 2025-11-04 02:04:48 +00:00
cornmander bd06e5b161 chore: bump vite to 7.1.12 (#12512) 2025-11-04 01:21:18 +00:00
Jacob Richman ad33c22374 Modify navigation and completion keyboard shortcuts to not use scroll. (#12502) 2025-11-04 00:22:04 +00:00
anthony bushong f3759381b1 feat(core): add timeout to llm edit fix (#12393) 2025-11-04 00:04:51 +00:00
Edilmo Palencia 80673a0c0c feat(hooks): Hook Type Decoupling and Translation (#9078) 2025-11-03 23:56:53 +00:00
Jacob Richman 1671bf77fb Alt buffer default (#12507) 2025-11-03 23:48:24 +00:00
Allen Hutchison ffc5e4d048 Refactor PolicyEngine to Core Package (#12325)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-11-03 23:41:00 +00:00
anj-s 60d2c2cc90 Enable WriteTodos tool by default (#12500) 2025-11-03 23:40:20 +00:00
Gal Zahavi 2144d25885 fix(auth): Return empty map if token file does not exits, and refacto… (#12332)
Co-authored-by: Sandy Tao <sandytao520@icloud.com>
2025-11-03 23:07:22 +00:00
Silvio Junior 7a515339ea Log recovery events (nudges) that happens inside the subagent (#12408) 2025-11-03 22:53:43 +00:00
Shreya Keshive be1dc13bb1 feat(core): Add support for listing experiments (#12495) 2025-11-03 21:51:22 +00:00
anj-s 1c044ba8af (fix): Respect ctrl+c signal for aborting execution in NonInteractive mode (#11478) 2025-11-03 21:49:01 +00:00
Jacob Richman 4fc9b1cde2 alternate buffer support (#12471) 2025-11-03 21:41:58 +00:00
Silvio Junior 60973aacd9 Grants subagent a recovery turn for when it hits TIMEOUT, MAX_TURNS or NO_TOOL_CALL failures. (#12344) 2025-11-03 21:22:12 +00:00
Silvio Junior 1c18552426 Enforce timeout for subagents (#12232) 2025-11-03 20:33:04 +00:00
Shehab c4377c1b1a fix(settings): persist restart-required changes when exiting with ESC (#12443) 2025-11-03 20:04:53 +00:00
Abhi 265f24e5d7 fix(ui): ensure model changes update the UI immediately (#12412) 2025-11-03 19:59:51 +00:00
Abhi 59e0b10e6c Cap Thinking Budget to prevent runaway thought loops (#12416) 2025-11-03 18:39:06 +00:00
Allen Hutchison 9d642f3bb1 refactor(core): improve error handling for setGlobalProxy (#12437) 2025-11-03 18:13:52 +00:00
Riddhi Dutta 19ea68b838 Refactoring packages/cli/src/ui tests (#12482)
Co-authored-by: riddhi <duttariddhi@google.com>
2025-11-03 18:10:57 +00:00
anj-s 93f14ce626 refactor: split core system prompt into multiple parts (#12461) 2025-11-03 17:08:56 +00:00
cornmander 1ef34261e0 chore: bump tar to 7.5.2 (#12466) 2025-11-03 07:36:35 +00:00
cornmander 5062fadf87 chore: autogenerate settings documentation (#12451) 2025-11-03 01:42:49 +00:00
cornmander 462c7d3502 feat(ui): add response semantic color (#12450)
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: shambhu-hegde <143840542+shambhu-hegde@users.noreply.github.com>
2025-11-03 00:21:26 +00:00
Chris Coutinho 9187f6f6d1 fix: preserve path components in OAuth issuer URLs (#12448)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Gregory Shikhman <cornmander@cornmander.com>
2025-11-02 21:19:46 +00:00
Edilmo Palencia c0495ce2f9 feat(hooks): Hook Configuration Schema and Types (#9074)
Co-authored-by: Abhi <43648792+abhipatel12@users.noreply.github.com>
2025-11-02 19:49:16 +00:00
Allen Hutchison 02518d2927 docs: update command-line flag documentation (#12452) 2025-11-02 17:59:03 +00:00
Jacob Richman d7243fb81f Add DarkGray to the ColorTheme. (#12420) 2025-11-01 23:50:51 +00:00
Eric Rahm e3262f8766 fix(core): combine .gitignore and .geminiignore logic for correct precedence (#11587)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-11-01 17:06:34 +00:00
Tommaso Sciortino caf2ca1438 Add kitty support for function keys. (#12415) 2025-10-31 23:19:55 +00:00
Jacob Richman f8ce3585eb Jacob314/jrichman ink (#12414) 2025-10-31 23:14:12 +00:00
Jacob Richman 61207fc2cb further incremental steps. Update the string width version to align with upstream ink (#12411) 2025-10-31 22:01:04 +00:00
Shreya Keshive b3cc397a31 feat(triage): overhaul automated issue triage workflow (#12365) 2025-10-31 21:21:05 +00:00
Shreya Keshive f9df415392 feat(core): Introduce release channel detection (#12257)
Co-authored-by: shishu314 <shishu_1998@yahoo.com>
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
Co-authored-by: Shardul Natu <43422294+kiranani@users.noreply.github.com>
Co-authored-by: Shnatu <snatu@google.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-10-31 21:18:39 +00:00
Abhi fd2cbacae9 fix(core): prevent model router from overriding explicit model choice (#12399) 2025-10-31 21:02:22 +00:00
Jacob Richman 8b93a5f27d I think the package lock was added in error to .gitignore. (#12405) 2025-10-31 20:29:42 +00:00
Jenna Inouye 9478bca67d Adding the Policy Engine docs to indexes. (#12404) 2025-10-31 20:23:56 +00:00
Jenna Inouye 4b53b3a6e6 Update telemetry.md to remove references to flags. (#12397) 2025-10-31 20:23:45 +00:00
Jacob Richman d72f8453cb Remove unused jsdom dep (#12394) 2025-10-31 20:03:57 +00:00
shishu314 6ee7165e39 feat(infra) - Add logging for slow rendering (#11147)
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
2025-10-31 19:51:05 +00:00
kevinjwang1 adddafe6d0 Handle untrusted folders on extension install and link (#12322)
Co-authored-by: Jacob MacDonald <jakemac@google.com>
2025-10-31 19:17:01 +00:00
shishu314 35f091bb01 feat(telemetry) - Add metric for slow rendering (#12391)
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
2025-10-31 18:57:47 +00:00
Sehoon Shon e9c7a80b12 migrate console.error to coreEvents for mcp-client-manager and google-auth-provider (#12342) 2025-10-31 18:52:56 +00:00
Sehoon Shon ab013fb7e9 migrating console.error to debugger for installationManager, oauth-provider, modifiable-tool (#12279) 2025-10-31 18:17:51 +00:00
Allen Hutchison c158923b27 docs: Add policy engine documentation and update sidebar (#12240) 2025-10-31 18:11:19 +00:00
Gaurav 31c5761e06 refactor: simplify daily quota error messages (#12386) 2025-10-31 17:49:32 +00:00
Jacob Richman f4ee245bf9 Switch to ink@. version 6.4.0 (#12381) 2025-10-31 16:40:51 +00:00
Tommaso Sciortino d13482e802 Mark model.compressionThreshold as requiring a restart (#12378) 2025-10-31 16:06:28 +00:00
Sandy Tao 11e1e98022 fix(core): ensure loop detection respects session disable flag (#12347) 2025-10-31 16:06:10 +00:00
Hriday b31f6804fe chore: migrate console.error to debugLogger in usePromptCompletion (#12208)
Co-authored-by: Hriday Taneja <hridayt@google.com>
2025-10-31 15:39:03 +00:00
Jerop Kipruto 01ad74a870 docs(cli): user.email attribute is only available for Google auth (#12372) 2025-10-31 15:13:38 +00:00
Jacob Richman f875911af7 Remove testing-library/react dep now that it is unused. (#12355) 2025-10-31 14:57:29 +00:00
Richard Michael f8ff921c42 Update mcp-server.md (#12310)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Jack Wotherspoon <jackwoth@google.com>
2025-10-31 14:46:51 +00:00
Jacob Richman ab8c24f5ea Fixes for Ink 6.4.0 (#12352) 2025-10-31 14:43:12 +00:00
Jerop Kipruto 9da3cb7e43 fix(core): remove duplicate session_id in GCP log exporter (#12370) 2025-10-31 14:42:13 +00:00
Jack Wotherspoon 6f69cdcc31 chore: make clear that --model is for choosing model on startup (#12367) 2025-10-31 14:01:56 +00:00
Shardul Natu 236334d015 feat(telemetry): Add extension name to ToolCallEvent telemetry (#12343)
Co-authored-by: Shnatu <snatu@google.com>
2025-10-31 13:50:22 +00:00
Jainam M 12472ce9b2 refactor(core): Refactored and removed redundant test lines in teleme… (#12284) 2025-10-31 07:33:33 +00:00
matt korwel e762cda55d fix: Address silent failure in release-patch-1-create-pr workflow (#12339) 2025-10-30 20:09:22 -07:00
matt korwel f566df91c8 feat: add dynamic run-names to patch release workflows (#12336) 2025-10-31 02:33:27 +00:00
gemini-cli-robot db37c715c0 chore/release: bump version to 0.13.0-nightly.20251031.c89bc30d (#12330) 2025-10-31 01:55:09 +00:00
Jacob Richman c89bc30d66 Code review script to catch common package/cli regressions (#12316)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-10-31 00:04:52 +00:00
Abhi 59e00eeda6 Remove context percentage in footer by default (#12326) 2025-10-30 23:18:25 +00:00
Tommaso Sciortino 3332703fca Make compression threshold editable in the UI. (#12317) 2025-10-30 23:03:58 +00:00
Victor May 643f2c0958 Enable model routing for all users (#12300) 2025-10-30 22:29:24 +00:00
Hyang-Ah Hana Kim c6a7107f78 fixing minor formatting issues in quota-and-pricing.md (#11340) 2025-10-30 21:46:23 +00:00
Sandy Tao 322feaafa6 refactor(core): decouple GeminiChat from uiTelemetryService via Usage events (#12196) 2025-10-30 21:05:38 +00:00
Tommaso Sciortino 68afb7200e Change default compression threshold (#12306) 2025-10-30 20:46:41 +00:00
matt korwel b382ae6803 feat: Prevent self-imports and fix build loop (#12309) 2025-10-30 20:15:49 +00:00
Jerop Kipruto 135d981e3c Create line change metrics (#12299) 2025-10-30 18:55:47 +00:00
Jacob Richman 54fa26ef0e Fix tests to wrap all calls changing the UI with act. (#12268) 2025-10-30 18:50:26 +00:00
Jacob MacDonald cc081337b7 Initial support for reloading extensions in the CLI - mcp servers only (#12239) 2025-10-30 18:05:49 +00:00
shishu314 d4cad0cdcc fix(test) - Make JSON output error test use canned response (#12250)
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
2025-10-30 17:25:01 +00:00
Pascal Birchler b8969cceff fix(docs): remove incorrect extension install method (#11194) 2025-10-30 17:24:48 +00:00
kevinjwang1 a3370ac86b Add validate command (#12186) 2025-10-30 16:08:13 +00:00
Jacob MacDonald 7d03151cd5 fix output messages for install and link (#12168) 2025-10-30 15:32:33 +00:00
matt korwel 054b4307ed Revert "fix(core): Add rootDir to tsconfig.json to resolve TS5055 error" (#12293) 2025-10-30 08:25:57 -07:00
Adib234 b8330b626e Fix misreported number of lines being removed by model (#12076) 2025-10-30 14:29:39 +00:00
christine betts 2a3244b1bb Log extension ID with tool call/slash command invocation (#12254)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-10-30 13:47:36 +00:00
Jenna Inouye 4d2a2de557 Docs: add v.0.11.0 to changelog (#12256) 2025-10-30 13:26:07 +00:00
Riddhi Dutta c583b510e0 Refactoring unit tests in packages/cli/src/ui (#12251)
Co-authored-by: riddhi <duttariddhi@google.com>
2025-10-30 07:18:45 +00:00
matt korwel 42c79c64d7 fix(core): Add rootDir to tsconfig.json to resolve TS5055 error (#12274) 2025-10-29 23:17:10 -07:00
Abhi 1d9e6870be feat(core): Implement granular memory loaders for JIT architecture (#12195) 2025-10-30 04:09:12 +00:00
Hriday 167b6ff8a1 chore: migrate console.error to debugLogger in useSlashCompletion (#12218)
Co-authored-by: Hriday Taneja <hridayt@google.com>
2025-10-30 03:59:15 +00:00
Jack Wotherspoon 77df6d48e2 docs: update keyboard shortcuts with missing shortcuts (#12024)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: cornmander <shikhman@google.com>
2025-10-30 02:39:15 +00:00
Gal Zahavi 06035d5d43 feat(auth): improve API key authentication flow (#11760)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-10-30 01:58:08 +00:00
Miguel Solorio 6c8a48db13 feat(ui): Fix Todo item text color not propagating for custom themes (#12265) 2025-10-29 23:39:31 +00:00
Sehoon Shon 523274dbf3 Standardize error logging with coreEvents.emitFeedback (#12199) 2025-10-29 22:27:17 +00:00
Shardul Natu 36207abec2 feat(telemetry): Add extensions to StartSessionEvent telemetry (#12261)
Co-authored-by: Shnatu <snatu@google.com>
2025-10-29 22:21:32 +00:00
shishu314 99f75f3218 Fix(noninteractive) - Add message when user uses deprecated flag (#11682)
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
2025-10-29 21:54:40 +00:00
Tommaso Sciortino 82c10421a0 Fix alt key mappings for mac (#12231) 2025-10-29 21:32:02 +00:00
Miguel Solorio 2e003ad8cf refactor(todo): improve performance and readability of todo component (#12238) 2025-10-29 21:19:44 +00:00
Tommaso Sciortino 42a265d290 Fix atprocessor test on windows (#12252) 2025-10-29 21:11:39 +00:00
Tommaso Sciortino 121732dde6 Hide collapsed Todo tray when they're all done. (#12242) 2025-10-29 20:41:20 +00:00
Shreya Keshive 3e9701861e refactor(core): Move getPackageJson utility to core package (#12224) 2025-10-29 20:23:35 +00:00
Gal Zahavi b31b786db7 refactor: Replace console.error with structured logging and feedback (#12175) 2025-10-29 20:20:11 +00:00
Shardul Natu 66e981ed1e feat: Add auth_type to StartSessionEvent OTel logging (#12034)
Co-authored-by: Shnatu <snatu@google.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-10-29 19:28:28 +00:00
shishu314 c60d8ef5a8 fix(infra) - Unskip read many file test (#12181)
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
2025-10-29 18:56:09 +00:00
shishu314 6e026bd950 fix(security) - Use emitFeedback instead of console error (#11954)
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
2025-10-29 14:49:46 +00:00
gemini-cli-robot 372b588778 chore(release): bump version to 0.13.0-nightly.20251029.cca41edc (#12191)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-10-28 21:53:10 -07:00
Tommaso Sciortino 5d87a7f9c7 Remove Todo Icon (#12190) 2025-10-29 03:48:38 +00:00
lifefloating 706834ecd3 fix: enhance path handling in handleAtCommand to support relative paths (#9065)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-10-29 02:13:04 +00:00
Richie Foreman cca41edc6e feat(docs): Symlink CONTRIBUTING.md in the docs folder so that the site can pick it up. (#12178) 2025-10-28 23:24:04 +00:00
Victor May 601a639f95 Disable model routing for oauth users (#11889)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-10-28 15:25:53 -07:00
Tommaso Sciortino f6423ea45a Remove obsolete snapshots (#12180) 2025-10-28 20:31:36 +00:00
Christie Warwick (Wilson) 70996bfdee feat: Add Open Telemetric semantic standard compliant log (#11975) 2025-10-28 20:02:46 +00:00
Jacob MacDonald 44bdd3ad11 Record model responses with --record-responses (for use in testing) (#11894) 2025-10-28 19:13:45 +00:00
Tommaso Sciortino ab1f195508 Change debug drawer keybinding to F12 (#12171) 2025-10-28 19:10:40 +00:00
hritan 39eb6ed9cc chore: migrate console.error in workspaceContext (#12167)
Co-authored-by: Hriday Taneja <hridayt@google.com>
2025-10-28 19:06:13 +00:00
hritan e750da987d chore: migrate console.error in useGeminiStream (#12157)
Co-authored-by: Hriday Taneja <hridayt@google.com>
2025-10-28 19:05:48 +00:00
christine betts 7e987113a2 Add support for sensitive keychain-stored per-extension settings (#11953) 2025-10-28 18:48:50 +00:00
shishu314 7a238bd938 fix(infra) - Continue workflow when merge queue skipper fail (#10509)
Co-authored-by: Shi Shu <shii@google.com>
2025-10-28 18:30:05 +00:00
JAYADITYA c2d60d61ac feat: Add explore subcommand for extension (#11846)
Co-authored-by: christine betts <chrstn@uw.edu>
2025-10-28 18:16:26 +00:00
Jacob Richman 13aa0148e7 Migrate tests to use avoid jsdom (#12118) 2025-10-28 17:32:15 +00:00
Allen Hutchison 5d61adf804 feat: Add message bus setting guard for tool confirmation (#12169) 2025-10-28 17:18:34 +00:00
Allen Hutchison 4e6eef5882 refactor: Migrate console.error to debugLogger.warn in atCommandProcessor.ts (#12134) 2025-10-28 17:16:47 +00:00
Allen Hutchison 064edc52f5 feat(policy): Introduce config-based policy engine with TOML configuration (#11992) 2025-10-28 16:20:57 +00:00
Jacob MacDonald 1b302deeff Add ExtensionLoader interface, use that on Config object (#12116) 2025-10-28 16:04:30 +00:00
Shreya Keshive 25f27509c0 revert nightly schedule (#11653) 2025-10-28 15:59:39 +00:00
Adam Weidman d465a26e30 chore(console): change console errors in sa-impersontation (#12165) 2025-10-28 15:26:39 +00:00
Adam Weidman 034ca93907 Revert "feat(ID token support): Add ID token support for authenticating to MC…" (#12162) 2025-10-28 15:07:57 +00:00
Tekkali Rahul Lakshmi Subrahmanyam cca5a12839 docs: update installation section in README (#12035) 2025-10-28 14:36:41 +00:00
Jainam M ecf0a24873 refactor(core): Parameterize tests in glob.test.ts (#12061) 2025-10-28 14:32:55 +00:00
Pavel Jbanov cb208f53d8 Added a a script to setup and run genkit telemetry and dev ui (#12120) 2025-10-28 03:37:43 +00:00
Tommaso Sciortino 85f3a8c210 Migrate to coreEvents/debugLogger (#12107) 2025-10-27 23:46:35 +00:00
Jacob Richman e9f8ccd51a Fix config test so it passes even if the user running the test happens to have set GEMINI_MODEL to flash (#12114) 2025-10-27 23:31:01 +00:00
Pavel Jbanov a9cb8f4958 feat: added basic dev otel trace instrumentation (#11690)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-10-27 23:16:44 +00:00
Pyush Sinha 2dfb813c90 (fix): appcontainer should not poll and footer should use currentModel from ui state (#11923) 2025-10-27 22:33:12 +00:00
matt korwel cb0947c501 fix(ci): tsc build for package/core is idempodent (#12112) 2025-10-27 14:39:09 -07:00
Sandy Tao 2a87d663d2 refactor(core): extract ChatCompressionService from GeminiClient (#12001) 2025-10-27 21:29:39 +00:00
Sandy Tao 6db64aab2b fix(telemetry): Prevent duplicate StartSessionEvent logging (#12090) 2025-10-27 20:40:03 +00:00
Ruchika Goel abd22a753d feat(ID token support): Add ID token support for authenticating to MC… (#12031)
Co-authored-by: Adam Weidman <adamfweidman@google.com>
2025-10-27 20:34:38 +00:00
cocosheng-g 9e8f7c074c Create BYOID auth client when detecting BYOID credentials (#11592) 2025-10-27 20:05:11 +00:00
Jenna Inouye 44c62c8e5d Docs: Contributing guide (#12012) 2025-10-27 19:52:32 +00:00
Sandy Tao 4ef3c09332 fix(core): update loop detection LLM schema fields (#12091) 2025-10-27 19:16:25 +00:00
Alisa 29efebe38f Implementing support for recitations events in responses from A2A Server (#12067)
Co-authored-by: Alisa Novikova <alisanovikova@google.com>
2025-10-27 18:56:08 +00:00
Tommaso Sciortino 0e4dce23b2 use debugLogger instead of console (#12095) 2025-10-27 18:35:16 +00:00
Jerop Kipruto e115083fac docs(github): revamp pull request template (#11949) 2025-10-27 17:33:29 +00:00
Riddhi Dutta 5ded674ad6 Refactor vim.test.ts: Use Parameterized Tests (#11969) 2025-10-27 17:13:17 +00:00
joshualitt 541eeb7a50 feat(core, cli): Implement sequential approval. (#11593) 2025-10-27 16:59:08 +00:00
Jack Wotherspoon 23c906b085 fix: user configured oauth scopes should take precedence over discovered scopes (#12088) 2025-10-27 16:57:12 +00:00
Krishna Bajpai c7817aee30 fix(cli): Add delimiter before printing tool response in non-interactive mode (#11351) 2025-10-27 14:57:54 +00:00
Gaurav Sehgal 2fa13420ae add absolute file path description for windows (#12007) 2025-10-27 04:17:13 +00:00
Jacob Richman ee66732ad2 First batch of fixing tests to use best practices. (#11964) 2025-10-25 21:41:53 +00:00
Tommaso Sciortino 8352980f01 Remove non-existent parallel flag. (#12018) 2025-10-25 16:07:35 +00:00
Sandy Tao a2d7f82b49 fix(core): Prepend user message to loop detection history if it starts with a function call (#11860) 2025-10-25 04:22:26 +00:00
ph-sp 73570f1c86 Fix the shortenPath function to correctly insert ellipsis. (#12004)
Co-authored-by: Greg Shikhman <shikhman@google.com>
2025-10-25 04:10:00 +00:00
Sandy Tao 51578397a5 refactor(cli): replace custom wait with vi.waitFor in InputPrompt tests (#12005) 2025-10-25 03:32:21 +00:00
Tommaso Sciortino ef70e63230 Make PASTE_WORKAROUND the default. (#12008) 2025-10-25 02:55:13 +00:00
Lakshan Perera ce26b58f09 docs(contributing): update project structure section with missing packages (#11599) 2025-10-25 02:08:42 +00:00
Qiyu-Wei bcd9735a73 Fix typo in: packages/cli/src/utils/handleAutoUpdate.ts (#11809) 2025-10-25 02:00:48 +00:00
Eric Rahm b1059f891f refactor: Switch over to unified shouldIgnoreFile (#11815) 2025-10-25 01:55:12 +00:00
Tommaso Sciortino 145e099ca5 Support paste markers split across writes. (#11977) 2025-10-25 01:52:03 +00:00
Jacob MacDonald 81006605c8 use debugLogger instead of console.error (#11990) 2025-10-24 23:31:25 +00:00
Allen Hutchison f934f01881 fix(tools): ReadFile no longer shows confirmation when message bus is off (#12003) 2025-10-24 23:22:02 +00:00
Jacob MacDonald cdff69b7b2 Support redirects in fetchJson, add tests for it (#11993) 2025-10-24 22:35:09 +00:00
Abhi d91484eb4d Fix tests (#11998) 2025-10-24 21:49:42 +00:00
Jacob MacDonald c20b88cee2 use coreEvents.emitFeedback in extension enablement (#11985) 2025-10-24 21:47:13 +00:00
Gal Zahavi 810d940e57 fix(update): replace update-notifier with latest-version (#11989) 2025-10-24 21:23:39 +00:00
Sandy Tao 7e2642b9f1 fix(core): use debugLogger.warn for loop detection errors (#11986) 2025-10-24 21:00:05 +00:00
Eric Rahm 40057b55f0 fix(cli): Use correct defaults for file filtering (#11426) 2025-10-24 20:20:17 +00:00
Allen Hutchison b188a51c32 feat(core): Introduce message bus for tool execution confirmation (#11544)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-10-24 20:04:40 +00:00
Jacob MacDonald 63a90836fe fix linked extension test on windows (#11973) 2025-10-24 18:55:31 +00:00
Eric Rahm ca94dabd4f Fix(cli): Use cross-platform path separators in extension tests (#11970) 2025-10-24 18:42:49 +00:00
cornmander 31b7c010d0 Add regression tests for shell command parsing (#11962) 2025-10-24 18:25:54 +00:00
shishu314 4960c47257 fix(infra) - Simplify cancel in progress and add permission to set status step (#11835)
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
2025-10-24 18:23:50 +00:00
Gaurav ee92db7533 fix: handle request retries and model fallback correctly (#11624) 2025-10-24 18:09:06 +00:00
shishu314 c2104a14fb fix(security) - Use emitFeedback instead of console error (#11948)
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
2025-10-24 18:07:11 +00:00
shishu314 25996ae037 fix(security) - Use emitFeedback (#11961)
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
2025-10-24 17:52:07 +00:00
Eric Rahm a123a813b2 Fix(cli): Use the correct extensionPath (#11896) 2025-10-24 17:45:58 +00:00
owenofbrien 8bdef8754e Stop logging session ids on extension events (#11941) 2025-10-24 17:11:42 +00:00
Sehoon Shon 978fbcf95e run bom test on windows (#11828) 2025-10-24 15:46:52 +00:00
Jerop Kipruto d915525c8e docs(cli): update telemetry documentation (#11806) 2025-10-24 14:56:21 +00:00
Adam Weidman c079084ca4 chore(core): add token caching in google auth provider (#11946) 2025-10-24 14:26:42 +00:00
Riddhi Dutta a889c15e38 Adding Parameterised tests (#11930) 2025-10-24 13:45:36 +00:00
Sandy Tao 884d838a1e fix(cli): re-throw errors in non-interactive mode (#11849) 2025-10-24 01:52:16 +00:00
Tommaso Sciortino 0fe82a2f4e Use raw writes to stdin in test (#11871) 2025-10-23 23:55:30 +00:00
Jacob MacDonald b16fe7b646 First take at mocking out gemini cli responses in integration tests (#11156) 2025-10-23 23:10:43 +00:00
joshualitt b77381750c feat(core) Bump get-ripgrep version. (#11698) 2025-10-23 22:01:02 +00:00
Riddhi Dutta 9814f86a25 Added parameterization to base-storage-token.test and prompts.test.ts (#11821) 2025-10-23 21:47:06 +00:00
Tommaso Sciortino aa6ae954ef Use raw writes to stdin where possible in tests (#11837) 2025-10-23 21:41:21 +00:00
cornmander 5e70a7dd46 fix: align shell allowlist handling (#11510) (#11813) 2025-10-23 20:55:01 +00:00
shishu314 48ff9e1555 fix(infra) - Fix merge queue skipper issues for chain e2e (#11810)
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
2025-10-23 20:46:46 +00:00
Jacob MacDonald c4c0c0d182 Create ExtensionManager class which manages all high level extension tasks (#11667) 2025-10-23 18:39:36 +00:00
Abhi 3a501196f0 feat(ux): Surface internal errors via unified event system (#11803) 2025-10-23 18:14:14 +00:00
shishu314 7787a31f81 feat(infra) - Make merge group and pushes run chained e2e (#11796)
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
2025-10-23 17:14:40 +00:00
Jacob Richman 9e91aafe40 Fix bug where tool scheduler was repeatedly created. (#11767) 2025-10-23 16:35:32 +00:00
shishu314 8ad72ec1ae fix(infra) - Remove context input for setting status (#11734)
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
2025-10-23 16:27:53 +00:00
Smetalo d3e4ff2cfc feat: Add lychee-action to check for broken links (#11781)
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2025-10-23 15:50:41 +00:00
christine betts 750c0e366f Add extension settings to be requested on install (#9802) 2025-10-23 15:47:08 +00:00
cornmander bde5d61812 Re-enable test. (#11628) 2025-10-23 13:50:50 +00:00
Smetalo 5ae9fe6949 Fix broken links in documentation (#11789)
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2025-10-23 13:48:24 +00:00
jdgarrido1105 3f38f95b1d Adds executeCommand endpoint with support for /extensions list (#11515) 2025-10-23 13:05:43 +00:00
Smetalo 445ef4fbed Docs: Fix broken link in docs/cli/configuration.md (#11655) 2025-10-23 08:29:31 +00:00
Jainam M 5ebe40e919 refactor(cli): Parameterize tests in InputPrompt (#11776) 2025-10-23 06:24:44 +00:00
Keith Lyons 8e9f71b7a3 fix(ui): resolve race condition in double-escape handler (#8913)
Co-authored-by: Megha Bansal <megha.igit@gmail.com>
2025-10-23 04:40:29 +00:00
Tommaso Sciortino 1202dced73 Refactor KeypressContext (#11677) 2025-10-23 00:51:03 +00:00
Eric Rahm 047bc44032 refactor(core): Clean up exclude description (#11678)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-10-23 00:08:38 +00:00
Eric Rahm 92d412e542 refactor: simplify FilterReport and remove unused code (#11681)
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-10-23 00:06:31 +00:00
Sandy Tao d9f0b9c668 fix(cli): fix race condition and unskip tests in useGitBranchName (#11759) 2025-10-22 23:11:06 +00:00
JAYADITYA 8f8a689722 feat(preflight): Use venv for yamllint installation (#11694) 2025-10-22 22:58:04 +00:00
Sandy Tao a90b9fe977 fix(a2a-server): Fix and unskip GCS persistence test (#11755) 2025-10-22 22:57:34 +00:00
Jenna Inouye eee3452948 Docs: adds 2025-10-13 changelog. (#11751) 2025-10-22 22:02:55 +00:00
Sandy Tao 2ede47d5ee fix(ui): Fix and unskip InputPrompt tests (#11700) 2025-10-22 21:59:10 +00:00
shishu314 4f220e945a feat(infra) - Add logging for when user tries to exit multiple times (#11218)
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
2025-10-22 21:39:27 +00:00
Tommaso Sciortino 30dd2f1dfe Document todo tool (#11695) 2025-10-22 20:27:10 +00:00
Abhi b40f67b76a extract console error to util func (#11675) 2025-10-22 20:09:10 +00:00
shishu314 a7faa2080f feat(infra) - Update status for chained e2e (#11651)
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
2025-10-22 20:01:02 +00:00
Adib234 6d75005afc Add setting to disable YOLO mode (#11609)
Co-authored-by: Shreya Keshive <shreyakeshive@google.com>
2025-10-22 18:57:10 +00:00
shishu314 5bb9cd1a13 feat(infra) - Create a workflow for deflake (#11535)
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
2025-10-22 18:41:26 +00:00
kevinjwang1 0bf2a0353d Add extension alias for extensions command (#11622) 2025-10-22 18:24:32 +00:00
Sandy Tao ce655436ef fix(test): unskip and fix useToolScheduler tests (#11671) 2025-10-22 17:45:50 +00:00
gemini-cli-robot 59985138f7 chore(release): bump version to 0.12.0-nightly.20251022.0542de95 (#11672) 2025-10-22 17:37:25 +00:00
788 changed files with 79864 additions and 27673 deletions
+140
View File
@@ -0,0 +1,140 @@
description="Reviews a pull request based on issue number."
prompt = """
Please provide a detailed pull request review on GitHub issue: {{args}}.
Follow these steps:
1. Use `gh pr view {{args}}` to pull the information of the PR.
2. Use `gh pr diff {{args}}` to view the diff of the PR.
3. Understand the intent of the PR using the PR description.
4. If PR description is not detailed enough to understand the intent of the PR,
make sure to note it in your review.
5. Make sure the PR title follows Conventional Commits, here are the last five
commits to the repo as examples: !{git log --pretty=format:"%s" -n 5}
6. Search the codebase if required.
7. Write a concise review of the PR, keeping in mind to encourage strong code
quality and best practices. Pay particular attention to the Gemini MD file
in the repo.
8. Consider ways the code may not be consistent with existing code in the repo.
In particular it is critical that the react code uses patterns consistent
with existing code in the repo.
9. Evaluate all tests on the PR and make sure that they are doing the following:
* Using `waitFor` from @{packages/cli/src/test-utils/async.ts} rather than
using `vi.waitFor` for all `waitFor` calls within `packages/cli`. Even if
tests pass, using the wrong `waitFor` could result in flaky tests as `act`
warnings could show up if timing is slightly different.
* Using `act` to wrap all blocks in tests that change component state.
* Using `toMatchSnapshot` to verify that rendering works as expected rather
than matching against the raw content of the output.
* If snapshots were changed as part of the pull request, review the snapshots
changes to ensure they are intentional and comment if any look at all
suspicious. Too many snapshot changes that indicate bugs have been approved
in the past.
* Use `render` or `renderWithProviders` from
@{packages/cli/src/test-utils/render.tsx} rather than using `render` from
`ink-testing-library` directly. This is needed to ensure that we do not get
warnings about spurious `act` calls. If test cases specify providers
directly, consider whether the existing `renderWithProviders` should be
modified to support that use case.
* Ensure the test cases are using parameterized tests where that might reduce
the number of duplicated lines significantly.
* NEVER use fixed waits (e.g. 'await delay(100)'). Always use 'waitFor' with
a predicate to ensure tests are stable and fast.
* Ensure mocks are properly managed:
* Critical dependencies (fs, os, child_process) should only be mocked at
the top of the file. Ideally avoid mocking these dependencies altogether.
* Check to see if there are existing mocks or fakes that can be used rather
than creating new ones for the new tests added.
* Try to avoid mocking the file system whenever possible. If using the real
file system is difficult consider whether the test should be an
integration test rather than a unit test.
* `vi.restoreAllMocks()` should be called in `afterEach` to prevent test
pollution.
* Use `vi.useFakeTimers()` for tests involving time-based logic to avoid
flakiness.
* Avoid using `any` in tests; prefer proper types or `unknown` with
narrowing.
* When creating parameterized tests, give the parameters types to ensure
that the tests are type-safe.
10. Evaluate all react logic carefully keeping in mind that the author of the PR
is not likely an expert on React. Key areas to audit carefully are:
* Whether `setState` calls trigger side effects from within the body of the
`setState` callback. If so, you *must* propose an alternate design using
reducers or other ways the code might be modified to not have to modify
state from within a `setState`. Make sure to comment about absolutely
every case like this as these cases have introduced multiple bugs in the
past. Typically these cases should be resolved using a reducer although
occassionally other techniques such as useRef are appropriate. Consider
suggesting that jacob314@ be tagged on the code review if the solution is
not 100% obvious.
* Whether code might introduce an infinite rendering loop in React.
* Whether keyboard handling is robust. Keyboard handling must go through
`useKeyPress.ts` from the Gemini CLI package rather than using the
standard ink library used by most keyboard handling. Unlike the standard
ink library, the keyboard handling library in Gemini CLI may report
multiple keyboard events one after another in the same React frame. This
is needed to support slow terminals but introduces complexity in all our
code that handles keyboard events. Handling this correctly often means
that reducers must be used or other mechanisms to ensure that multiple
state updates one after another are handled gracefully rather than
overriding values from the first update with the second update. Refer to
text-buffer.ts as a canonical example of using a reducer for this sort of
case.
* Ensure code does not use `console.log`, `console.warn`, or `console.error`
as these indicate debug logging that was accidentally left in the code.
* Avoid synchronous file I/O in React components as it will hang the UI.
* Ensure state initialization is explicit (e.g., use 'undefined' rather than
'true' as a default if the state is truly unknown initially).
* Carefully manage 'useEffect' dependencies. Prefer to use a reducer
whenever practical to resolve the issues. If that is not practical it is
ok to use 'useRef' to access the latest value of a prop or state inside an
effect without adding it to the dependency array if re-running the effect
is undesirable (common in event listeners).
* NEVER disable 'react-hooks/exhaustive-deps'. Fix the code to correctly
declare dependencies. Disabling this lint rule will almost always lead to
hard to detect bugs.
* Avoid making types nullable unless strictly necessary, as it hurts
readability.
* Do not introduce excessive property drilling. There are multiple providers
that can be leveraged to avoid property drilling. Make sure one of them
cannot be used. Do suggest a provider that might make sense to be extended
to include the new property or propose a new provider to add if the
property drilling is excessive. Only use providers for properties that are
consistent for the entire application.
11. General Gemini CLI design principles:
* Make sure that settings are only used for options that a user might
consider changing.
* Do not add new command line arguments and suggest settings instead.
* New settings must be added to packages/cli/src/config/settingsSchema.ts.
* If a setting has 'showInDialog: true', it MUST be documented in
docs/get-started/configuration.md.
* Ensure 'requiresRestart' is correctly set for new settings.
* Use 'debugLogger' for rethrown errors to avoid duplicate logging.
* All new keyboard shortcuts MUST be documented in
docs/cli/keyboard-shortcuts.md.
* Ensure new keyboard shortcuts are defined in
packages/cli/src/config/keyBindings.ts.
* If new keyboard shortcuts are added, remind the user to test them in
VSCode, iTerm2, Ghostty, and Windows to ensure they work for all
users.
* Be careful of keybindings that require the meta key as only certain
meta key shortcuts are supported on Mac.
* Be skeptical of function keys and keyboard shortcuts that are commonly
bound in VSCode as they may conflict.
12. TypeScript Best Practices:
* Use 'checkExhaustive' in the 'default' clause of 'switch' statements to
ensure all cases are handled.
* Avoid using the non-null assertion operator ('!') unless absolutely
necessary and you are confident the value is not null.
13. If the change might at all impact the prompts sent to Gemini CLI, flagged
that the change could impact Gemini CLI quality and make sure anj-s has been
tagged on the code review.
14. Discuss with me before making any comments on the issue. I will clarify
which possible issues you identified are problems, which ones you need to
investigate further, and which ones I do not care about.
15. If I request you to add comments to the issue, use
`gh pr comment {{args}} --body {{review}}` to post the review to the PR.
Remember to use the GitHub CLI (`gh`) with the Shell tool for all
GitHub-related tasks.
"""
+1 -1
View File
@@ -1,8 +1,8 @@
name: 'Bug Report'
description: 'Report a bug to help us improve Gemini CLI'
labels:
- 'kind/bug'
- 'status/need-triage'
type: 'Bug'
body:
- type: 'markdown'
attributes:
+1 -1
View File
@@ -1,8 +1,8 @@
name: 'Feature Request'
description: 'Suggest an idea for this project'
labels:
- 'kind/enhancement'
- 'status/need-triage'
type: 'Feature'
body:
- type: 'markdown'
attributes:
+1 -2
View File
@@ -1,8 +1,7 @@
name: 'Website issue'
description: 'Report an issue with the Gemini CLI Website and Gemini CLI Extensions Gallery'
labels:
- 'area/website'
- 'status/need-triage'
- 'area/extensions'
body:
- type: 'markdown'
attributes:
+32 -31
View File
@@ -1,41 +1,42 @@
## TLDR
## Summary
<!-- Add a brief description of what this pull request changes and why and any important things for reviewers to look at -->
<!-- Concisely describe what this PR changes and why. Focus on impact and
urgency. -->
## Dive Deeper
## Details
<!-- more thoughts and in-depth discussion here -->
<!-- Add any extra context and design decisions. Keep it brief but complete. -->
## Reviewer Test Plan
## Related Issues
<!-- 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 -->
<!-- Use keywords to auto-close issues (Closes #123, Fixes #456). If this PR is
only related to an issue or is a partial fix, simply reference the issue number
without a keyword (Related to #123). -->
## Testing Matrix
## How to Validate
<!-- Before submitting please validate your changes on as many of these options as possible -->
<!-- List exact steps for reviewers to validate the change. Include commands,
expected results, and edge cases. -->
| | 🍏 | 🪟 | 🐧 |
| -------- | --- | --- | --- |
| npm run | ❓ | ❓ | ❓ |
| npx | ❓ | ❓ | ❓ |
| Docker | ❓ | ❓ | ❓ |
| Podman | ❓ | - | - |
| Seatbelt | ❓ | - | - |
## Pre-Merge Checklist
## Linked issues / bugs
<!-- Check all that apply before requesting review or merging. -->
<!--
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`*
-->
- [ ] Updated relevant documentation and README (if needed)
- [ ] Added/updated tests (if needed)
- [ ] Noted breaking changes (if any)
- [ ] Validated on required platforms/methods:
- [ ] MacOS
- [ ] npm run
- [ ] npx
- [ ] Docker
- [ ] Podman
- [ ] Seatbelt
- [ ] Windows
- [ ] npm run
- [ ] npx
- [ ] Docker
- [ ] Linux
- [ ] npm run
- [ ] npx
- [ ] Docker
+19
View File
@@ -90,9 +90,26 @@ jobs:
- name: 'Run Prettier'
run: 'node scripts/lint.js --prettier'
- name: 'Build docs prerequisites'
run: 'npm run predocs:settings'
- name: 'Verify settings docs'
run: 'npm run docs:settings -- --check'
- name: 'Run sensitive keyword linter'
run: 'node scripts/lint.js --sensitive-keywords'
link_checker:
name: 'Link Checker'
runs-on: 'ubuntu-latest'
steps:
- name: 'Checkout'
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
- name: 'Link Checker'
uses: 'lycheeverse/lychee-action@885c65f3dc543b57c898c8099f4e08c8afd178a2' # ratchet: lycheeverse/lychee-action@v2.6.1
with:
args: '--verbose ./**/*.md'
fail: true
test_linux:
name: 'Test (Linux)'
runs-on: 'gemini-cli-ubuntu-16-core'
@@ -354,6 +371,7 @@ jobs:
if: 'always()'
needs:
- 'lint'
- 'link_checker'
- 'test_linux'
- 'test_mac'
- 'codeql'
@@ -363,6 +381,7 @@ jobs:
- name: 'Check all job results'
run: |
if [[ (${{ needs.lint.result }} != 'success' && ${{ needs.lint.result }} != 'skipped') || \
(${{ needs.link_checker.result }} != 'success' && ${{ needs.link_checker.result }} != 'skipped') || \
(${{ needs.test_linux.result }} != 'success' && ${{ needs.test_linux.result }} != 'skipped') || \
(${{ needs.test_mac.result }} != 'success' && ${{ needs.test_mac.result }} != 'skipped') || \
(${{ needs.codeql.result }} != 'success' && ${{ needs.codeql.result }} != 'skipped') || \
+138 -4
View File
@@ -24,13 +24,147 @@ concurrency:
${{ github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/heads/release/') }}
jobs:
deflake:
name: 'Deflake'
deflake_e2e_linux:
name: 'E2E Test (Linux) - ${{ matrix.sandbox }}'
runs-on: 'gemini-cli-ubuntu-16-core'
strategy:
fail-fast: false
matrix:
sandbox:
- 'sandbox:none'
- 'sandbox:docker'
node-version:
- '20.x'
steps:
- name: 'Deflake'
- name: 'Checkout'
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
with:
ref: '${{ github.event.pull_request.head.sha }}'
repository: '${{ github.repository }}'
- name: 'Set up Node.js ${{ matrix.node-version }}'
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
with:
node-version: '${{ matrix.node-version }}'
- name: 'Install dependencies'
run: 'npm ci'
- name: 'Build project'
run: 'npm run build'
- name: 'Set up Docker'
if: "matrix.sandbox == 'sandbox:docker'"
uses: 'docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435' # ratchet:docker/setup-buildx-action@v3
- name: 'Run E2E tests'
env:
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
IS_DOCKER: "${{ matrix.sandbox == 'sandbox:docker' }}"
KEEP_OUTPUT: 'true'
RUNS: '${{ github.event.inputs.runs }}'
TEST_NAME_PATTERN: '${{ github.event.inputs.test_name_pattern }}'
VERBOSE: 'true'
shell: 'bash'
run: |
ECHO 'DEFLAKE WORKFLOW'
if [[ "${{ env.IS_DOCKER }}" == "true" ]]; then
npm run deflake:test:integration:sandbox:docker -- --runs="${{ env.RUNS }}" -- --testNamePattern "'${{ env.TEST_NAME_PATTERN }}'"
else
npm run deflake:test:integration:sandbox:none -- --runs="${{ env.RUNS }}" -- --testNamePattern "'${{ env.TEST_NAME_PATTERN }}'"
fi
deflake_e2e_mac:
name: 'E2E Test (macOS)'
runs-on: 'macos-latest'
steps:
- name: 'Checkout'
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
with:
ref: '${{ github.event.pull_request.head.sha }}'
repository: '${{ github.repository }}'
- name: 'Set up Node.js 20.x'
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
with:
node-version: '20.x'
- name: 'Install dependencies'
run: 'npm ci'
- name: 'Build project'
run: 'npm run build'
- name: 'Fix rollup optional dependencies on macOS'
if: "runner.os == 'macOS'"
run: |
npm cache clean --force
- name: 'Run E2E tests (non-Windows)'
if: "runner.os != 'Windows'"
env:
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
KEEP_OUTPUT: 'true'
RUNS: '${{ github.event.inputs.runs }}'
SANDBOX: 'sandbox:none'
TEST_NAME_PATTERN: '${{ github.event.inputs.test_name_pattern }}'
VERBOSE: 'true'
run: |
npm run deflake:test:integration:sandbox:none -- --runs="${{ env.RUNS }}" -- --testNamePattern "'${{ env.TEST_NAME_PATTERN }}'"
deflake_e2e_windows:
name: 'Slow E2E - Win'
runs-on: 'gemini-cli-windows-16-core'
steps:
- name: 'Checkout'
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
with:
ref: '${{ github.event.pull_request.head.sha }}'
repository: '${{ github.repository }}'
- name: 'Set up Node.js 20.x'
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
with:
node-version: '20.x'
cache: 'npm'
- name: 'Configure Windows Defender exclusions'
run: |
Add-MpPreference -ExclusionPath $env:GITHUB_WORKSPACE -Force
Add-MpPreference -ExclusionPath "$env:GITHUB_WORKSPACE\node_modules" -Force
Add-MpPreference -ExclusionPath "$env:GITHUB_WORKSPACE\packages" -Force
Add-MpPreference -ExclusionPath "$env:TEMP" -Force
shell: 'pwsh'
- name: 'Configure npm for Windows performance'
run: |
npm config set progress false
npm config set audit false
npm config set fund false
npm config set loglevel error
npm config set maxsockets 32
npm config set registry https://registry.npmjs.org/
shell: 'pwsh'
- name: 'Install dependencies'
run: 'npm ci'
shell: 'pwsh'
- name: 'Build project'
run: 'npm run build'
shell: 'pwsh'
- name: 'Run E2E tests'
env:
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
KEEP_OUTPUT: 'true'
SANDBOX: 'sandbox:none'
VERBOSE: 'true'
NODE_OPTIONS: '--max-old-space-size=32768 --max-semi-space-size=256'
UV_THREADPOOL_SIZE: '32'
NODE_ENV: 'test'
RUNS: '${{ github.event.inputs.runs }}'
TEST_NAME_PATTERN: '${{ github.event.inputs.test_name_pattern }}'
shell: 'pwsh'
run: |
npm run deflake:test:integration:sandbox:none -- --runs="${{ env.RUNS }}" -- --testNamePattern "'${{ env.TEST_NAME_PATTERN }}'"
+5 -3
View File
@@ -30,6 +30,7 @@ jobs:
merge_queue_skipper:
name: 'Merge Queue Skipper'
runs-on: 'gemini-cli-ubuntu-16-core'
permissions: 'read-all'
outputs:
skip: '${{ steps.merge-queue-e2e-skipper.outputs.skip-check }}'
steps:
@@ -37,12 +38,13 @@ jobs:
uses: 'cariad-tech/merge-queue-ci-skipper@1032489e59437862c90a08a2c92809c903883772' # ratchet:cariad-tech/merge-queue-ci-skipper@main
with:
secret: '${{ secrets.GITHUB_TOKEN }}'
continue-on-error: true
e2e_linux:
name: 'E2E Test (Linux) - ${{ matrix.sandbox }}'
needs: 'merge_queue_skipper'
if: |
needs.merge_queue_skipper.outputs.skip == 'false' &&
(needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip == 'false') &&
(github.event_name == 'push' ||
github.event_name == 'merge_group' ||
github.event_name == 'workflow_dispatch' ||
@@ -104,7 +106,7 @@ jobs:
name: 'E2E Test (macOS)'
needs: 'merge_queue_skipper'
if: |
needs.merge_queue_skipper.outputs.skip == 'false' &&
(needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip == 'false') &&
(github.event_name == 'push' ||
github.event_name == 'merge_group' ||
github.event_name == 'workflow_dispatch' ||
@@ -153,7 +155,7 @@ jobs:
name: 'Slow E2E - Win'
needs: 'merge_queue_skipper'
if: |
needs.merge_queue_skipper.outputs.skip == 'false' &&
(needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip == 'false') &&
(github.event_name == 'push' ||
github.event_name == 'merge_group' ||
github.event_name == 'workflow_dispatch' ||
@@ -45,7 +45,9 @@ jobs:
(github.event.comment.author_association == 'OWNER' || github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'COLLABORATOR')
))
)
)
) &&
!contains(github.event.issue.labels.*.name, 'area/') &&
!contains(github.event.issue.labels.*.name, 'priority/')
timeout-minutes: 5
runs-on: 'ubuntu-latest'
steps:
@@ -67,14 +69,24 @@ jobs:
core.setOutput('labels', issue.labels.map(label => label.name).join(','));
return issue;
- name: 'Check for triage label on manual trigger'
- name: 'Manual Trigger Pre-flight Checks'
if: |-
github.event_name == 'workflow_dispatch' && !contains(steps.get_issue_data.outputs.labels, 'status/need-triage')
github.event_name == 'workflow_dispatch'
env:
ISSUE_NUMBER_INPUT: '${{ github.event.inputs.issue_number }}'
LABELS: '${{ steps.get_issue_data.outputs.labels }}'
run: |
echo "Issue #${ISSUE_NUMBER_INPUT} does not have the 'status/need-triage' label. Stopping workflow."
exit 1
if ! echo "${LABELS}" | grep -q 'status/need-triage'; then
echo "Issue #${ISSUE_NUMBER_INPUT} does not have the 'status/need-triage' label. Stopping workflow."
exit 1
fi
if echo "${LABELS}" | grep -q 'area/' || echo "${LABELS}" | grep -q 'priority/'; then
echo "Issue #${ISSUE_NUMBER_INPUT} already has 'area/' or 'priority/' labels. Stopping workflow."
exit 1
fi
echo "Manual triage checks passed."
- name: 'Checkout'
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
@@ -97,7 +109,22 @@ jobs:
owner: context.repo.owner,
repo: context.repo.repo,
});
const labelNames = labels.map(label => label.name);
const allowedLabels = [
'priority/p0',
'priority/p1',
'priority/p2',
'priority/p3',
'priority/unknown',
'area/agent',
'area/enterprise',
'area/non-interactive',
'area/core',
'area/security',
'area/platform',
'area/extensions',
'area/unknown'
];
const labelNames = labels.map(label => label.name).filter(name => allowedLabels.includes(name));
core.setOutput('available_labels', labelNames.join(','));
core.info(`Found ${labelNames.length} labels: ${labelNames.join(', ')}`);
return labelNames;
@@ -126,9 +153,6 @@ jobs:
settings: |-
{
"maxSessionTurns": 25,
"coreTools": [
"run_shell_command(echo)"
],
"telemetry": {
"enabled": true,
"target": "gcp"
@@ -137,128 +161,134 @@ jobs:
prompt: |-
## Role
You are an issue triage assistant. Analyze the current GitHub issue
and identify the most appropriate existing labels by only using the provided data. Use the available
tools to gather information; do not ask for information to be
provided. Do not remove the following labels titled maintainer, help wanted or good first issue.
You are an issue triage assistant. Your role is to analyze a GitHub issue and determine the single most appropriate area/ label and the single most appropriate priority/ label based on the definitions provided.
## Steps
1. You are only able to use the echo command. Review the available labels in the environment variable: "${AVAILABLE_LABELS}".
2. Review the issue title and body provided in the environment variables: "${ISSUE_TITLE}" and "${ISSUE_BODY}".
3. 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.
4. If the issue already has area/ label, dont try to change it. Similarly, if the issue already has a kind/ label don't change it. And if the issue already has a priority/ label do not change it for example:
If an issue has area/core and kind/bug you will only add a priority/ label.
Instead if an issue has no labels, you will could add one lable of each kind.
5. 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.
6. If you see that the issue doesn't look like it has sufficient information recommend the status/need-information label and leave a comment politely requesting the relevant information, eg.. if repro steps are missing request for repro steps. if version information is missing request for version information into the explanation section below.
7. Output the appropriate labels for this issue in JSON format with explanation, for example:
```
{"labels_to_set": ["kind/bug", "priority/p0"], "explanation": "This is a critical bug report affecting main functionality"}
```
8. If the issue cannot be classified using the available labels, output:
```
{"labels_to_set": [], "explanation": "Unable to classify this issue with available labels"}
```
9. Use Area definitions mentioned below to help you narrow down issues.
10. If you think an issue might be a Priority/P0 do not apply the priority/p0 label. Instead apply a status/manual-triage label and include a note in your explanation.
11. If you are uncertain and have not been able to apply one each of kind/, area/ and priority/ , apply the status/manual-triage label.
1. Review the issue title and body: ${{ env.ISSUE_TITLE }} and ${{ env.ISSUE_BODY }}.
2. Review the available labels: ${{ env.AVAILABLE_LABELS }}.
3. Select exactly one area/ label that best matches the issue based on Reference 1: Area Definitions.
4. Select exactly one priority/ label that best matches the issue based on Reference 2: Priority Definitions.
5. Fallback Logic:
- If you cannot confidently determine the correct area/ label from the definitions, you must use area/unknown.
- If you cannot confidently determine the correct priority/ label from the definitions, you must use priority/unknown.
6. Output your two selected labels in JSON format and nothing else. Example:
{"labels_to_set": ["area/core", "priority/p1"]}
## Guidelines
- Your output must contain exactly one area/ label and exactly one priority/ label.
- Triage only the current issue based on its title and body.
- Output only valid JSON format.
- Do not include any explanation or additional text, just the JSON.
- Only use labels that already exist in the repository
- Do not add comments or modify the issue content
- Triage only the current issue
- Identify only one area/ label
- Identify only one kind/ label
- Identify 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
- Reference all shell variables as "${VAR}" (with quotes and braces)
- Output only valid JSON format
- Do not include any explanation or additional text, just the JSON
Reference 1: Area Definitions
area/agent
- Description: Issues related to the "brain" of the CLI. This includes the core agent logic, model quality, tool/function calling, and memory.
- Example Issues:
"I am not getting a reasonable or expected response."
"The model is not calling the tool I expected."
"The web search tool is not working as expected."
"Feature request for a new built-in tool (e.g., read file, write file)."
"The generated code is poor quality or incorrect."
"The model seems stuck in a loop."
"The response from the model is malformed (e.g., broken JSON, bad formatting)."
"Concerns about unnecessary token consumption."
"Issues with how memory or chat history is managed."
"Issues with sub-agents."
"Model is switching from one to another unexpectedly."
Categorization Guidelines:
P0: Critical / Blocker
- A P0 bug is a catastrophic failure that demands immediate attention.
- To be a P0 it means almost all users are running into this issue and it is blocking users from being able to use the product.
- You would see this in the form of many comments from different developers on the bug.
- 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.
- Once again this would be affecting many users.
- You would see this in the form of comments from different developers on the bug.
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: Gemini CLI enters a loop when making read-many-files tool call. I am unable to break out of the loop and gemini doesn't follow instructions subsequently.
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.
area/enterprise
- Description: Issues specific to enterprise-level features, including telemetry, policy, and licenses.
- Example Issues:
"Usage data is not appearing in our telemetry dashboard."
"A user is able to perform an action that should be blocked by an admin policy."
"Questions about billing, licensing tiers, or enterprise quotas."
area/non-interactive
- Description: Issues related to using the CLI in automated or non-interactive environments (headless mode).
- Example Issues:
"Problems using the CLI as an SDK in another surface."
"The CLI is behaving differently when run from a shell script vs. an interactive terminal."
"GitHub action is failing."
"I am having trouble running the CLI in headless mode"
area/core
- Description: Issues with the fundamental CLI app itself. This includes the user interface (UI/UX), installation, OS compatibility, and performance.
- Example Issues:
"I am seeing my screen flicker when using the CLI."
"The output in my terminal is malformed or unreadable."
"Theme changes are not taking effect."
"Keyboard inputs (e.g., arrow keys, Ctrl+C) are not being recognized."
"The CLI failed to install or update."
"An issue specific to running on Windows, macOS, or Linux."
"Problems with command parsing, flags, or argument handling."
"High CPU or memory usage by the CLI process."
"Issues related to multi-modality (e.g., handling image inputs)."
"Problems with the IDE integration connection or installation"
area/security
- Description: Issues related to user authentication, authorization, data security, and privacy.
- Example Issues:
"I am unable to sign in."
"The login flow is selecting the wrong authentication path"
"Problems with API key handling or credential storage."
"A report of a security vulnerability"
"Concerns about data sanitization or potential data leaks."
"Issues or requests related to privacy controls."
"Preventing unauthorized data access."
area/platform
- Description: Issues related to CI/CD, release management, testing, eval infrastructure, capacity, quota management, and sandbox environments.
- Example Issues:
"I am getting a 429 'Resource Exhausted' or 500-level server error."
"General slowness or high latency from the service."
"The build script is broken on the main branch."
"Tests are failing in the CI/CD pipeline."
"Issues with the release management or publishing process."
"User is running out of capacity."
"Problems specific to the sandbox or staging environments."
"Questions about quota limits or requests for increases."
area/extensions
- Description: Issues related to the extension ecosystem, including the marketplace and website.
- Example Issues:
"Bugs related to the extension marketplace website."
"Issues with a specific extension."
"Feature request for the extension ecosystem."
area/unknown
- Description: Issues that do not clearly fit into any other defined area/ category, or where information is too limited to make a determination. Use this when no other area is appropriate.
Reference 2: Priority Definitions
priority/p0: Critical / Blocker
- Definition: A catastrophic failure that makes the CLI unusable for most users or poses a severe security risk. This includes installation failures, authentication failures, persistent crashes, or critical security vulnerabilities.
- Key Questions:
- Is the CLI failing to install or run?
- Does it fail to authenticate or connect to the Gemini API, making all commands useless?
- Is it consistently crashing on basic, common commands?
- Does this represent a critical security vulnerability?
priority/p1: High
- Definition: A severe issue where a core feature (e.g., text generation, code generation, file processing) is unusable, failing, has severe performance degradation, or providing fundamentally incorrect output formatting (e.g., truncated text, broken JSON). Affects many users, and there is no reasonable workaround.
- Key Questions:
- Is a core feature failing for a specific, large user group (e.g., all Windows users, all users of a specific shell)?
- Is the CLI failing to process a supported input or misinterpreting critical flags?
- Is the CLI's output formatting consistently broken, making the response unusable?
- Is a core command or feature extremely slow, making it impractical to use?
priority/p2: Medium
- Definition: A moderately impactful issue causing inconvenience or a non-optimal experience, but a reasonable workaround exists. This also includes failures in non-core features.
- Key Questions:
- Is a command or flag behaving incorrectly, but the user can achieve their goal via other means?
- Is there a significant, non-blocking UI/UX problem in the terminal (e.g., broken progress indicators, bad terminal coloring)?
priority/p3: Low
- Definition: A minor, low-impact issue with minimal effect on functionality. This includes most cosmetic defects, typos in documentation, or unclear help text.
- Key Questions:
- Is this a typo in the README.md, gemini --help text, or other documentation?
- Is this a minor cosmetic issue (e.g., text alignment in output, an extra newline) that doesn't affect usability?
priority/unknown
- Description: Issues that do not clearly fit into any other defined priority/ category, or where information is too limited to make a determination. Use this when no other priority is appropriate.
- name: 'Apply Labels to Issue'
if: |-
@@ -270,52 +300,61 @@ jobs:
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
with:
github-token: '${{ steps.generate_token.outputs.token }}'
script: |-
// Strip code block markers if present
const rawLabels = process.env.LABELS_OUTPUT;
core.info(`Raw labels JSON: ${rawLabels}`);
script: |
const rawOutput = process.env.LABELS_OUTPUT;
core.info(`Raw output from model: ${rawOutput}`);
let parsedLabels;
try {
const jsonMatch = rawLabels.match(/```json\s*([\s\S]*?)\s*```/);
if (!jsonMatch || !jsonMatch[1]) {
throw new Error("Could not find a ```json ... ``` block in the output.");
// First, try to parse the raw output as JSON.
parsedLabels = JSON.parse(rawOutput);
} catch (jsonError) {
// If that fails, check for a markdown code block.
core.warning(`Direct JSON parsing failed: ${jsonError.message}. Trying to extract from a markdown block.`);
const jsonMatch = rawOutput.match(/```json\s*([\s\S]*?)\s*```/);
if (jsonMatch && jsonMatch[1]) {
try {
parsedLabels = JSON.parse(jsonMatch[1].trim());
} catch (markdownError) {
core.setFailed(`Failed to parse JSON even after extracting from markdown block: ${markdownError.message}\nRaw output: ${rawOutput}`);
return;
}
} else {
core.setFailed(`Output is not valid JSON and does not contain a JSON markdown block.\nRaw output: ${rawOutput}`);
return;
}
const jsonString = jsonMatch[1].trim();
parsedLabels = JSON.parse(jsonString);
core.info(`Parsed labels JSON: ${JSON.stringify(parsedLabels)}`);
} catch (err) {
core.setFailed(`Failed to parse labels JSON from Gemini output: ${err.message}\nRaw output: ${rawLabels}`);
return;
}
const issueNumber = parseInt(process.env.ISSUE_NUMBER);
const explanation = parsedLabels.explanation || '';
const labelsToSet = parsedLabels.labels_to_set || [];
labelsToSet.push('status/bot-triaged');
const labelsToAdd = parsedLabels.labels_to_set || [];
// Set labels based on triage result
if (labelsToSet.length > 0) {
await github.rest.issues.setLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
labels: labelsToSet
});
const explanationInfo = explanation ? ` - ${explanation}` : '';
core.info(`Successfully set labels for #${issueNumber}: ${labelsToSet.join(', ')}${explanationInfo}`);
} else {
// If no labels to set, leave the issue as is
const explanationInfo = explanation ? ` - ${explanation}` : '';
core.info(`No labels to set for #${issueNumber}, leaving as is${explanationInfo}`);
if (labelsToAdd.length !== 2) {
core.setFailed(`Expected exactly 2 labels (one area/ and one priority/), but got ${labelsToAdd.length}. Labels: ${labelsToAdd.join(', ')}`);
return;
}
if (explanation) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
body: explanation,
});
// Set labels based on triage result
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
labels: labelsToAdd
});
core.info(`Successfully added labels for #${issueNumber}: ${labelsToAdd.join(', ')}`);
// Remove the 'status/need-triage' label
try {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
name: 'status/need-triage'
});
core.info(`Successfully removed 'status/need-triage' label.`);
} catch (error) {
// If the label doesn't exist, the API call will throw a 404. We can ignore this.
if (error.status !== 404) {
core.warning(`Failed to remove 'status/need-triage': ${error.message}`);
}
}
- name: 'Post Issue Analysis Failure Comment'
+23
View File
@@ -0,0 +1,23 @@
name: 'Links'
on:
push:
branches: ['main']
pull_request:
branches: ['main']
repository_dispatch:
workflow_dispatch:
schedule:
- cron: '00 18 * * *'
jobs:
linkChecker:
runs-on: 'ubuntu-latest'
steps:
- uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
- name: 'Link Checker'
id: 'lychee'
uses: 'lycheeverse/lychee-action@885c65f3dc543b57c898c8099f4e08c8afd178a2' # ratchet: lycheeverse/lychee-action@v2.6.1
with:
args: '--verbose --no-progress ./**/*.md'
+1 -1
View File
@@ -2,7 +2,7 @@ name: 'Release: Nightly'
on:
schedule:
- cron: '0 20 * * *'
- cron: '0 0 * * *'
workflow_dispatch:
inputs:
dry_run:
@@ -1,5 +1,8 @@
name: 'Release: Patch (1) Create PR'
run-name: >-
Release Patch (1) Create PR | S:${{ inputs.channel }} | C:${{ inputs.commit }} ${{ inputs.original_pr && format('| PR:#{0}', inputs.original_pr) || '' }}
on:
workflow_dispatch:
inputs:
@@ -96,12 +99,12 @@ jobs:
--channel="${PATCH_CHANNEL}" \
--pullRequestNumber="${ORIGINAL_PR}" \
--dry-run="${DRY_RUN}"
echo "EXIT_CODE=$?" >> "$GITHUB_OUTPUT"
} 2>&1 | tee >(
echo "LOG_CONTENT<<EOF" >> "$GITHUB_ENV"
cat >> "$GITHUB_ENV"
echo "EOF" >> "$GITHUB_ENV"
)
echo "EXIT_CODE=${PIPESTATUS[0]}" >> "$GITHUB_OUTPUT"
- name: 'Comment on Original PR'
if: 'always() && inputs.original_pr'
@@ -115,6 +118,7 @@ jobs:
GITHUB_RUN_ID: '${{ github.run_id }}'
LOG_CONTENT: '${{ env.LOG_CONTENT }}'
TARGET_REF: '${{ github.event.inputs.ref }}'
ENVIRONMENT: '${{ github.event.inputs.environment }}'
continue-on-error: true
run: |
git checkout "${TARGET_REF}"
@@ -1,5 +1,10 @@
name: 'Release: Patch (2) Trigger'
run-name: >-
Release Patch (2) Trigger |
${{ github.event.pull_request.number && format('PR #{0}', github.event.pull_request.number) || 'Manual' }} |
${{ github.event.pull_request.head.ref || github.event.inputs.ref }}
on:
pull_request:
types:
@@ -1,5 +1,8 @@
name: 'Release: Patch (3) Release'
run-name: >-
Release Patch (3) Release | T:${{ inputs.type }} | R:${{ inputs.release_ref }} ${{ inputs.original_pr && format('| PR:#{0}', inputs.original_pr) || '' }}
on:
workflow_dispatch:
inputs:
+5 -2
View File
@@ -92,6 +92,9 @@ jobs:
fi
NIGHTLY_COMMAND="node scripts/get-release-version.js --type=promote-nightly"
STABLE_JSON=$(${STABLE_COMMAND})
STABLE_VERSION=$(echo "${STABLE_JSON}" | jq -r .releaseVersion)
PREVIEW_COMMAND+=" --stable-base-version=${STABLE_VERSION}"
NIGHTLY_COMMAND+=" --stable-base-version=${STABLE_VERSION}"
PREVIEW_JSON=$(${PREVIEW_COMMAND})
NIGHTLY_JSON=$(${NIGHTLY_COMMAND})
echo "STABLE_JSON_COMMAND=${STABLE_COMMAND}"
@@ -100,14 +103,14 @@ jobs:
echo "STABLE_JSON: ${STABLE_JSON}"
echo "PREVIEW_JSON: ${PREVIEW_JSON}"
echo "NIGHTLY_JSON: ${NIGHTLY_JSON}"
echo "STABLE_VERSION=$(echo "${STABLE_JSON}" | jq -r .releaseVersion)" >> "${GITHUB_OUTPUT}"
echo "STABLE_VERSION=${STABLE_VERSION}" >> "${GITHUB_OUTPUT}"
# shellcheck disable=SC1083
echo "STABLE_SHA=$(git rev-parse "$(echo "${PREVIEW_JSON}" | jq -r .previousReleaseTag)"^{commit})" >> "${GITHUB_OUTPUT}"
echo "PREVIOUS_STABLE_TAG=$(echo "${STABLE_JSON}" | jq -r .previousReleaseTag)" >> "${GITHUB_OUTPUT}"
echo "PREVIEW_VERSION=$(echo "${PREVIEW_JSON}" | jq -r .releaseVersion)" >> "${GITHUB_OUTPUT}"
# shellcheck disable=SC1083
REF="${REF_INPUT}"
SHA=$(git ls-remote origin "$REF" | awk '{print $1}')
SHA=$(git ls-remote origin "$REF" | awk -v ref="$REF" '$2 == "refs/heads/"ref || $2 == "refs/tags/"ref || $2 == ref {print $1}' | head -n 1)
if [ -z "$SHA" ]; then
if [[ "$REF" =~ ^[0-9a-f]{7,40}$ ]]; then
SHA="$REF"
+87 -14
View File
@@ -1,6 +1,10 @@
name: 'Test Chained E2E'
on:
push:
branches:
- 'main'
merge_group:
workflow_run:
workflows: ['Trigger E2E']
types: ['completed']
@@ -14,13 +18,18 @@ on:
required: true
concurrency:
group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}'
group: '${{ github.workflow }}-${{ github.head_ref || github.event.workflow_run.head_branch || github.ref }}'
cancel-in-progress: |-
${{ github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/heads/release/') }}
${{ github.event_name != 'push' && github.event_name != 'merge_group' }}
permissions:
contents: 'read'
statuses: 'write'
jobs:
merge_queue_skipper:
name: 'Merge Queue Skipper'
permissions: 'read-all'
runs-on: 'gemini-cli-ubuntu-16-core'
outputs:
skip: '${{ steps.merge-queue-e2e-skipper.outputs.skip-check }}'
@@ -29,14 +38,16 @@ jobs:
uses: 'cariad-tech/merge-queue-ci-skipper@1032489e59437862c90a08a2c92809c903883772' # ratchet:cariad-tech/merge-queue-ci-skipper@main
with:
secret: '${{ secrets.GITHUB_TOKEN }}'
continue-on-error: true
download_repo_name:
runs-on: 'gemini-cli-ubuntu-16-core'
if: "github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_run'"
outputs:
repo_name: '${{ steps.output-repo-name.outputs.repo_name }}'
steps:
- name: 'Mock Repo Artifact'
if: "${{ github.event_name != 'workflow_run' }}"
if: "${{ github.event_name == 'workflow_dispatch' }}"
env:
REPO_NAME: '${{ github.event.inputs.repo_name }}'
run: |
@@ -67,10 +78,50 @@ jobs:
const repo_name = String(fs.readFileSync(path.join(temp, 'repo_name')));
core.setOutput('repo_name', repo_name);
parse_run_context:
name: 'Parse run context'
runs-on: 'gemini-cli-ubuntu-16-core'
needs: 'download_repo_name'
if: 'always()'
outputs:
repository: '${{ steps.set_context.outputs.REPO }}'
sha: '${{ steps.set_context.outputs.SHA }}'
steps:
- id: 'set_context'
name: 'Set dynamic repository and SHA'
env:
REPO: '${{ needs.download_repo_name.outputs.repo_name || github.repository }}'
SHA: '${{ github.event.inputs.head_sha || github.event.workflow_run.head_sha || github.sha }}'
shell: 'bash'
run: |
echo "REPO=$REPO" >> "$GITHUB_OUTPUT"
echo "SHA=$SHA" >> "$GITHUB_OUTPUT"
set_pending_status:
runs-on: 'gemini-cli-ubuntu-16-core'
permissions: 'write-all'
if: "github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_run'"
needs:
- 'parse_run_context'
steps:
- name: 'Set pending status'
uses: 'myrotvorets/set-commit-status-action@16037e056d73b2d3c88e37e393ff369047f70886' # ratchet:myrotvorets/set-commit-status-action@master
if: 'always()'
with:
allowForks: 'true'
repo: '${{ needs.parse_run_context.outputs.repository }}'
sha: '${{ needs.parse_run_context.outputs.sha }}'
token: '${{ secrets.GITHUB_TOKEN }}'
status: 'pending'
e2e_linux:
name: 'E2E Test (Linux) - ${{ matrix.sandbox }}'
needs: 'download_repo_name'
needs:
- 'merge_queue_skipper'
- 'parse_run_context'
runs-on: 'gemini-cli-ubuntu-16-core'
if: |
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip == 'false')
strategy:
fail-fast: false
matrix:
@@ -84,8 +135,8 @@ jobs:
- name: 'Checkout'
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
with:
ref: '${{ github.event.inputs.head_sha || github.event.workflow_run.head_sha }}'
repository: '${{ needs.download_repo_name.outputs.repo_name }}'
ref: '${{ needs.parse_run_context.outputs.sha }}'
repository: '${{ needs.parse_run_context.outputs.repository }}'
- name: 'Set up Node.js ${{ matrix.node-version }}'
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
@@ -117,14 +168,18 @@ jobs:
e2e_mac:
name: 'E2E Test (macOS)'
needs: 'download_repo_name'
needs:
- 'merge_queue_skipper'
- 'parse_run_context'
runs-on: 'macos-latest'
if: |
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip == 'false')
steps:
- name: 'Checkout'
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
with:
ref: '${{ github.event.inputs.head_sha || github.event.workflow_run.head_sha }}'
repository: '${{ needs.download_repo_name.outputs.repo_name }}'
ref: '${{ needs.parse_run_context.outputs.sha }}'
repository: '${{ needs.parse_run_context.outputs.repository }}'
- name: 'Set up Node.js 20.x'
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
@@ -154,9 +209,9 @@ jobs:
name: 'Slow E2E - Win'
needs:
- 'merge_queue_skipper'
- 'download_repo_name'
- 'parse_run_context'
if: |
needs.merge_queue_skipper.outputs.skip == 'false'
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip == 'false')
runs-on: 'gemini-cli-windows-16-core'
continue-on-error: true
@@ -164,8 +219,8 @@ jobs:
- name: 'Checkout'
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
with:
ref: '${{ github.event.inputs.head_sha || github.event.workflow_run.head_sha }}'
repository: '${{ needs.download_repo_name.outputs.repo_name }}'
ref: '${{ needs.parse_run_context.outputs.sha }}'
repository: '${{ needs.parse_run_context.outputs.repository }}'
- name: 'Set up Node.js 20.x'
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
@@ -214,7 +269,7 @@ jobs:
e2e:
name: 'E2E'
if: |
always() && needs.merge_queue_skipper.outputs.skip == 'false'
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip == 'false')
needs:
- 'e2e_linux'
- 'e2e_mac'
@@ -229,3 +284,21 @@ jobs:
exit 1
fi
echo "All required E2E jobs passed!"
set_workflow_status:
runs-on: 'gemini-cli-ubuntu-16-core'
permissions: 'write-all'
if: "github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_run'"
needs:
- 'parse_run_context'
- 'e2e'
steps:
- name: 'Set workflow status'
uses: 'myrotvorets/set-commit-status-action@16037e056d73b2d3c88e37e393ff369047f70886' # ratchet:myrotvorets/set-commit-status-action@master
if: 'always()'
with:
allowForks: 'true'
repo: '${{ needs.parse_run_context.outputs.repository }}'
sha: '${{ needs.parse_run_context.outputs.sha }}'
token: '${{ secrets.GITHUB_TOKEN }}'
status: '${{ job.status }}'
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
steps:
- name: 'Save Repo name'
env:
# Replace with github.event.pull_request.base.repo.full_name when switched to listen on pull request events. This repo name does not contain the org which is needed for checkout.
# Replace with github.event.pull_request.head.repo.full_name when switched to listen on pull request events. This repo name does not contain the org which is needed for checkout.
REPO_NAME: '${{ github.event.repository.name }}'
run: |
mkdir -p ./pr
+2 -1
View File
@@ -17,7 +17,6 @@
# Dependency directory
node_modules
bower_components
package-lock.json
# Editors
.idea
@@ -54,3 +53,5 @@ gha-creds-*.json
# Log files
patch_output.log
.genkit
+6
View File
@@ -0,0 +1,6 @@
http://localhost:16686/
https://github.com/google-gemini/gemini-cli/issues/new/choose
https://github.com/google-gemini/maintainers-gemini-cli/blob/main/npm.md
https://github.com/settings/personal-access-tokens/new
https://github.com/settings/tokens/new
https://www.npmjs.com/package/@google/gemini-cli
+165 -36
View File
@@ -1,6 +1,18 @@
# How to Contribute
We would love to accept your patches and contributions to this project.
We would love to accept your patches and contributions to this project. This
document includes:
- **[Before you begin](#before-you-begin):** Essential steps to take before
becoming a Gemini CLI contributor.
- **[Code contribution process](#code-contribution-process):** How to contribute
code to Gemini CLI.
- **[Development setup and workflow](#development-setup-and-workflow):** How to
set up your development environment and workflow.
- **[Documentation contribution process](#documentation-contribution-process):**
How to contribute documentation to Gemini CLI.
We're looking forward to seeing your contributions!
## Before you begin
@@ -23,19 +35,40 @@ sign a new one.
This project follows
[Google's Open Source Community Guidelines](https://opensource.google/conduct/).
## Contribution Process
## Code contribution process
### Code Reviews
### Get started
The process for contributing code is as follows:
1. **Find an issue** that you want to work on.
2. **Fork the repository** and create a new branch.
3. **Make your changes** in the `packages/` directory.
4. **Ensure all checks pass** by running `npm run preflight`.
5. **Open a pull request** with your changes.
### Code reviews
All submissions, including submissions by project members, require review. We
use [GitHub pull requests](https://docs.github.com/articles/about-pull-requests)
for this purpose.
### Self Assigning Issues
If your pull request involves changes to `packages/cli` (the frontend), we
recommend running our automated frontend review tool. **Note: This tool is
currently experimental.** It helps detect common React anti-patterns, testing
issues, and other frontend-specific best practices that are easy to miss.
If you're looking for an issue to work on, check out our list of issues that are
labeled
["help wanted"](https://github.com/google-gemini/gemini-cli/issues?q=is%3Aissue+state%3Aopen+label%3A%22help+wanted%22).
To run the review tool, enter the following command from within Gemini CLI:
```text
/review-frontend <PR_NUMBER>
```
Replace `<PR_NUMBER>` with your pull request number. Authors are encouraged to
run this on their own PRs for self-review, and reviewers should use it to
augment their manual review process.
### Self assigning issues
To assign an issue to yourself, simply add a comment with the text `/assign`.
The comment must contain only that text and nothing else. This command will
@@ -44,12 +77,12 @@ assign the issue to you, provided it is not already assigned.
Please note that you can have a maximum of 3 issues assigned to you at any given
time.
### Pull Request Guidelines
### Pull request guidelines
To help us review and merge your PRs quickly, please follow these guidelines.
PRs that do not meet these standards may be closed.
#### 1. Link to an Existing Issue
#### 1. Link to an existing issue
All PRs should be linked to an existing issue in our tracker. This ensures that
every change has been discussed and is aligned with the project's goals before
@@ -62,7 +95,7 @@ any code is written.
If an issue for your change doesn't exist, please **open one first** and wait
for feedback before you start coding.
#### 2. Keep It Small and Focused
#### 2. Keep it small and focused
We favor small, atomic PRs that address a single issue or add a single,
self-contained feature.
@@ -74,37 +107,40 @@ self-contained feature.
Large changes should be broken down into a series of smaller, logical PRs that
can be reviewed and merged independently.
#### 3. Use Draft PRs for Work in Progress
#### 3. Use draft PRs for work in progress
If you'd like to get early feedback on your work, please use GitHub's **Draft
Pull Request** feature. This signals to the maintainers that the PR is not yet
ready for a formal review but is open for discussion and initial feedback.
#### 4. Ensure All Checks Pass
#### 4. Ensure all checks pass
Before submitting your PR, ensure that all automated checks are passing by
running `npm run preflight`. This command runs all tests, linting, and other
style checks.
#### 5. Update Documentation
#### 5. Update documentation
If your PR introduces a user-facing change (e.g., a new command, a modified
flag, or a change in behavior), you must also update the relevant documentation
in the `/docs` directory.
#### 6. Write Clear Commit Messages and a Good PR Description
See more about writing documentation:
[Documentation contribution process](#documentation-contribution-process).
#### 6. Write clear commit messages and a good PR description
Your PR should have a clear, descriptive title and a detailed description of the
changes. Follow the [Conventional Commits](https://www.conventionalcommits.org/)
standard for your commit messages.
- **Good PR Title:** `feat(cli): Add --json flag to 'config get' command`
- **Bad PR Title:** `Made some changes`
- **Good PR title:** `feat(cli): Add --json flag to 'config get' command`
- **Bad PR title:** `Made some changes`
In the PR description, explain the "why" behind your changes and link to the
relevant issue (e.g., `Fixes #123`).
## Forking
### Forking
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
@@ -118,12 +154,12 @@ 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
### Development setup and workflow
This section guides contributors on how to build, modify, and understand the
development setup of this project.
### Setting Up the Development Environment
### Setting up the development environment
**Prerequisites:**
@@ -135,7 +171,7 @@ development setup of this project.
version of Node.js `>=20` is acceptable.
2. **Git**
### Build Process
### Build process
To clone the repository:
@@ -160,7 +196,7 @@ This command typically compiles TypeScript to JavaScript, bundles assets, and
prepares the packages for execution. Refer to `scripts/build.js` and
`package.json` scripts for more details on what happens during the build.
### Enabling Sandboxing
### Enabling sandboxing
[Sandboxing](#sandboxing) is highly recommended and requires, at a minimum,
setting `GEMINI_SANDBOX=true` in your `~/.env` and ensuring a sandboxing
@@ -176,7 +212,7 @@ npm run build:all
To skip building the sandbox container, you can use `npm run build` instead.
### Running
### Running the CLI
To start the Gemini CLI from the source code (after building), run the following
command from the root directory:
@@ -190,11 +226,11 @@ 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
### Running tests
This project contains two types of tests: unit tests and integration tests.
#### Unit Tests
#### Unit tests
To execute the unit test suite for the project:
@@ -206,7 +242,7 @@ This will run tests located in the `packages/core` and `packages/cli`
directories. Ensure tests pass before submitting any changes. For a more
comprehensive check, it is recommended to run `npm run preflight`.
#### Integration Tests
#### Integration tests
The integration tests are designed to validate the end-to-end functionality of
the Gemini CLI. They are not run as part of the default `npm run test` command.
@@ -218,9 +254,9 @@ npm run test:e2e
```
For more detailed information on the integration testing framework, please see
the [Integration Tests documentation](./docs/integration-tests.md).
the [Integration Tests documentation](/docs/integration-tests.md).
### Linting and Preflight Checks
### Linting and preflight checks
To ensure code quality and formatting consistency, run the preflight check:
@@ -267,7 +303,7 @@ root directory:
npm run lint
```
### Coding Conventions
### Coding conventions
- Please adhere to the coding style, patterns, and conventions used throughout
the existing codebase.
@@ -279,19 +315,24 @@ npm run lint
- **Imports:** Pay special attention to import paths. The project uses ESLint to
enforce restrictions on relative imports between packages.
### Project Structure
### Project structure
- `packages/`: Contains the individual sub-packages of the project.
- `a2a-server`: A2A server implementation for the Gemini CLI. (Experimental)
- `cli/`: The command-line interface.
- `core/`: The core backend logic for the Gemini CLI.
- `test-utils` Utilities for creating and cleaning temporary file systems for
testing.
- `vscode-ide-companion/`: The Gemini CLI Companion extension pairs with
Gemini CLI.
- `docs/`: Contains all project documentation.
- `scripts/`: Utility scripts for building, testing, and development tasks.
For more detailed architecture, see `docs/architecture.md`.
## Debugging
### Debugging
### VS Code:
#### VS Code
0. Run the CLI to interactively debug in VS Code with `F5`
1. Start the CLI in debug mode from the root directory:
@@ -349,9 +390,9 @@ used for the CLI's interface, is compatible with React DevTools version 4.x.
Your running CLI application should then connect to React DevTools.
![](/docs/assets/connected_devtools.png)
## Sandboxing
### 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
@@ -367,7 +408,7 @@ Available built-in profiles are `{permissive,restrictive}-{open,closed,proxied}`
`.gemini/sandbox-macos-<profile>.sb` under your project settings directory
`.gemini`.
### Container-based Sandboxing (All Platforms)
#### 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`
@@ -390,7 +431,7 @@ for your projects by creating the files `.gemini/sandbox.Dockerfile` and/or
running `gemini` with `BUILD_SANDBOX=1` to trigger building of your custom
sandbox.
#### Proxied Networking
#### Proxied networking
All sandboxing methods, including macOS Seatbelt using `*-proxied` profiles,
support restricting outbound network traffic through a custom proxy server that
@@ -401,7 +442,7 @@ 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
### Manual publish
We publish an artifact for each commit to our internal registry. But if you need
to manually cut a local build, then run the following commands:
@@ -413,3 +454,91 @@ npm run auth
npm run prerelease:dev
npm publish --workspaces
```
## Documentation contribution process
Our documentation must be kept up-to-date with our code contributions. We want
our documentation to be clear, concise, and helpful to our users. We value:
- **Clarity:** Use simple and direct language. Avoid jargon where possible.
- **Accuracy:** Ensure all information is correct and up-to-date.
- **Completeness:** Cover all aspects of a feature or topic.
- **Examples:** Provide practical examples to help users understand how to use
Gemini CLI.
### Getting started
The process for contributing to the documentation is similar to contributing
code.
1. **Fork the repository** and create a new branch.
2. **Make your changes** in the `/docs` directory.
3. **Preview your changes locally** in Markdown rendering.
4. **Lint and format your changes.** Our preflight check includes linting and
formatting for documentation files.
```bash
npm run preflight
```
5. **Open a pull request** with your changes.
### Documentation structure
Our documentation is organized using [sidebar.json](/docs/sidebar.json) as the
table of contents. When adding new documentation:
1. Create your markdown file **in the appropriate directory** under `/docs`.
2. Add an entry to `sidebar.json` in the relevant section.
3. Ensure all internal links use relative paths and point to existing files.
### Style guide
We follow the
[Google Developer Documentation Style Guide](https://developers.google.com/style).
Please refer to it for guidance on writing style, tone, and formatting.
#### Key style points
- Use sentence case for headings.
- Write in second person ("you") when addressing the reader.
- Use present tense.
- Keep paragraphs short and focused.
- Use code blocks with appropriate language tags for syntax highlighting.
- Include practical examples whenever possible.
### Linting and formatting
We use `prettier` to enforce a consistent style across our documentation. The
`npm run preflight` command will check for any linting issues.
You can also run the linter and formatter separately:
- `npm run lint` - Check for linting issues
- `npm run format` - Auto-format markdown files
- `npm run lint:fix` - Auto-fix linting issues where possible
Please make sure your contributions are free of linting errors before submitting
a pull request.
### Before you submit
Before submitting your documentation pull request, please:
1. Run `npm run preflight` to ensure all checks pass.
2. Review your changes for clarity and accuracy.
3. Check that all links work correctly.
4. Ensure any code examples are tested and functional.
5. Sign the
[Contributor License Agreement (CLA)](https://cla.developers.google.com/) if
you haven't already.
### Need help?
If you have questions about contributing documentation:
- Check our [FAQ](/docs/faq.md).
- Review existing documentation for examples.
- Open [an issue](https://github.com/google-gemini/gemini-cli/issues) to discuss
your proposed changes.
- Reach out to the maintainers.
We appreciate your contributions to making Gemini CLI documentation better!
+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.
+8 -5
View File
@@ -4,6 +4,7 @@
[![Gemini CLI E2E](https://github.com/google-gemini/gemini-cli/actions/workflows/e2e.yml/badge.svg)](https://github.com/google-gemini/gemini-cli/actions/workflows/e2e.yml)
[![Version](https://img.shields.io/npm/v/@google/gemini-cli)](https://www.npmjs.com/package/@google/gemini-cli)
[![License](https://img.shields.io/github/license/google-gemini/gemini-cli)](https://github.com/google-gemini/gemini-cli/blob/main/LICENSE)
<a href="https://codewiki.google/github.com/google-gemini/gemini-cli"><img src="https://www.gstatic.com/_/boq-sdlc-agents-ui/_/r/Mvosg4klCA4.svg" alt="Ask Code Wiki" height="20"></a>
![Gemini CLI Screenshot](./docs/assets/gemini-screenshot.png)
@@ -27,6 +28,11 @@ Learn all about Gemini CLI in our [documentation](https://geminicli.com/docs/).
## 📦 Installation
### Pre-requisites before installation
- Node.js version 20 or higher
- macOS, Linux, or Windows
### Quick Install
#### Run instantly with npx
@@ -48,11 +54,6 @@ npm install -g @google/gemini-cli
brew install gemini-cli
```
#### System Requirements
- Node.js version 20 or higher
- macOS, Linux, or Windows
## Release Cadence and Tags
See [Releases](./docs/releases.md) for more details.
@@ -306,6 +307,8 @@ gemini
corporate environment.
- [**Telemetry & Monitoring**](./docs/cli/telemetry.md) - Usage tracking.
- [**Tools API Development**](./docs/core/tools-api.md) - Create custom tools.
- [**Local development**](./docs/local-development.md) - Local development
tooling.
### Troubleshooting & Support
+1
View File
@@ -0,0 +1 @@
../CONTRIBUTING.md
+169
View File
@@ -3,6 +3,175 @@
Wondering what's new in Gemini CLI? This document provides key highlights and
notable changes to Gemini CLI.
## v0.15.0 - Gemini CLI weekly update - 2025-11-03
- **🎉 Seamless scrollable UI & mouse support:** Weve given Gemini CLI a major
facelift to make your terminal experience smoother and much more polished. You
now get a flicker-free display with sticky headers that keep important context
visible and a stable input prompt that doesn't jump around. We even added
mouse support so you can click right where you need to type!
([gif](https://imgur.com/a/O6qc7bx),
[@jacob314](https://github.com/jacob314)).
- **Announcement:**
[https://developers.googleblog.com/en/making-the-terminal-beautiful-one-pixel-at-a-time/](https://developers.googleblog.com/en/making-the-terminal-beautiful-one-pixel-at-a-time/)
- **🎉 New partner extensions:**
- **Arize:** Seamlessly instrument AI applications with Arize AX and grant
direct access to Arize support:
`gemini extensions install https://github.com/Arize-ai/arize-tracing-assistant`
- **Chronosphere:** Retrieve logs, metrics, traces, events, and specific
entities:
`gemini extensions install https://github.com/chronosphereio/chronosphere-mcp`
- **Transmit:** Comprehensive context, validation, and automated fixes for
creating production-ready authentication and identity workflows:
`gemini extensions install https://github.com/TransmitSecurity/transmit-security-journey-builder`
- **Todo planning:** Complex questions now get broken down into todo lists that
the model can manage and check off. ([gif](https://imgur.com/a/EGDfNlZ),
[pr](https://github.com/google-gemini/gemini-cli/pull/12905) by
[@anj-s](https://github.com/anj-s))
- **Disable GitHub extensions:** Users can now prevent the installation and
loading of extensions from GitHub.
([pr](https://github.com/google-gemini/gemini-cli/pull/12838) by
[@kevinjwang1](https://github.com/kevinjwang1)).
- **Extensions restart:** Users can now explicitly restart extensions using the
`/extensions restart` command.
([pr](https://github.com/google-gemini/gemini-cli/pull/12739) by
[@jakemac53](https://github.com/jakemac53)).
- **Better Angular support:** Angular workflows should now be more seamless
([pr](https://github.com/google-gemini/gemini-cli/pull/10252) by
[@MarkTechson](https://github.com/MarkTechson)).
- **Validate command:** Users can now check that local extensions are formatted
correctly. ([pr](https://github.com/google-gemini/gemini-cli/pull/12186) by
[@kevinjwang1](https://github.com/kevinjwang1)).
## v0.12.0 - Gemini CLI weekly update - 2025-10-27
![Codebase investigator subagent in Gemini CLI.](https://i.imgur.com/4J1njsx.png)
- **🎉 New partner extensions:**
- **🤗 Hugging Face extension:** Access the Hugging Face hub.
([gif](https://drive.google.com/file/d/1LEzIuSH6_igFXq96_tWev11svBNyPJEB/view?usp=sharing&resourcekey=0-LtPTzR1woh-rxGtfPzjjfg))
`gemini extensions install https://github.com/huggingface/hf-mcp-server`
- **Monday.com extension**: Analyze your sprints, update your task boards,
etc.
([gif](https://drive.google.com/file/d/1cO0g6kY1odiBIrZTaqu5ZakaGZaZgpQv/view?usp=sharing&resourcekey=0-xEr67SIjXmAXRe1PKy7Jlw))
`gemini extensions install https://github.com/mondaycom/mcp`
- **Data Commons extension:** Query public datasets or ground responses on
data from Data Commons
([gif](https://drive.google.com/file/d/1cuj-B-vmUkeJnoBXrO_Y1CuqphYc6p-O/view?usp=sharing&resourcekey=0-0adXCXDQEd91ZZW63HbW-Q)).
`gemini extensions install https://github.com/gemini-cli-extensions/datacommons`
- **Model selection:** Choose the Gemini model for your session with `/model`.
([pic](https://imgur.com/a/ABFcWWw),
[pr](https://github.com/google-gemini/gemini-cli/pull/8940) by
[@abhipatel12](https://github.com/abhipatel12)).
- **Model routing:** Gemini CLI will now intelligently pick the best model for
the task. Simple queries will be sent to Flash while complex analytical or
creative tasks will still use the power of Pro. This ensures your quota will
last for a longer period of time. You can always opt-out of this via `/model`.
([pr](https://github.com/google-gemini/gemini-cli/pull/9262) by
[@abhipatel12](https://github.com/abhipatel12)).
- Discussion:
[https://github.com/google-gemini/gemini-cli/discussions/12375](https://github.com/google-gemini/gemini-cli/discussions/12375)
- **Codebase investigator subagent:** We now have a new built-in subagent that
will explore your workspace and resolve relevant information to improve
overall performance.
([pr](https://github.com/google-gemini/gemini-cli/pull/9988) by
[@abhipatel12](https://github.com/abhipatel12),
[pr](https://github.com/google-gemini/gemini-cli/pull/10282) by
[@silviojr](https://github.com/silviojr)).
- Enable, disable, or limit turns in `/settings`, plus advanced configs in
`settings.json` ([pic](https://imgur.com/a/yJiggNO),
[pr](https://github.com/google-gemini/gemini-cli/pull/10844) by
[@silviojr](https://github.com/silviojr)).
- **Explore extensions with `/extension`:** Users can now open the extensions
page in their default browser directly from the CLI using the `/extension`
explore command. ([pr](https://github.com/google-gemini/gemini-cli/pull/11846)
by [@JayadityaGit](https://github.com/JayadityaGit)).
- **Configurable compression:** Users can modify the compression threshold in
`/settings`. The default has been made more proactive
([pr](https://github.com/google-gemini/gemini-cli/pull/12317) by
[@scidomino](https://github.com/scidomino)).
- **API key authentication:** Users can now securely enter and store their
Gemini API key via a new dialog, eliminating the need for environment
variables and repeated entry.
([pr](https://github.com/google-gemini/gemini-cli/pull/11760) by
[@galz10](https://github.com/galz10)).
- **Sequential approval:** Users can now approve multiple tool calls
sequentially during execution.
([pr](https://github.com/google-gemini/gemini-cli/pull/11593) by
[@joshualitt](https://github.com/joshualitt)).
## v0.11.0 - Gemini CLI weekly update - 2025-10-20
![Gemini CLI and Jules](https://storage.googleapis.com/gweb-developer-goog-blog-assets/images/Jules_Extension_-_Blog_Header_O346JNt.original.png)
- 🎉 **Gemini CLI Jules Extension:** Use Gemini CLI to orchestrate Jules. Spawn
remote workers, delegate tedious tasks, or check in on running jobs!
- Install:
`gemini extensions install https://github.com/gemini-cli-extensions/jules`
- Announcement:
[https://developers.googleblog.com/en/introducing-the-jules-extension-for-gemini-cli/](https://developers.googleblog.com/en/introducing-the-jules-extension-for-gemini-cli/)
- **Stream JSON output:** Stream real-time JSONL events with
`--output-format stream-json` to monitor AI agent progress when run
headlessly. ([gif](https://imgur.com/a/0UCE81X),
[pr](https://github.com/google-gemini/gemini-cli/pull/10883) by
[@anj-s](https://github.com/anj-s))
- **Markdown toggle:** Users can now switch between rendered and raw markdown
display using `alt+m `or` ctrl+m`. ([gif](https://imgur.com/a/lDNdLqr),
[pr](https://github.com/google-gemini/gemini-cli/pull/10383) by
[@srivatsj](https://github.com/srivatsj))
- **Queued message editing:** Users can now quickly edit queued messages by
pressing the up arrow key when the input is empty.
([gif](https://imgur.com/a/ioRslLd),
[pr](https://github.com/google-gemini/gemini-cli/pull/10392) by
[@akhil29](https://github.com/akhil29))
- **JSON web fetch**: Non-HTML content like JSON APIs or raw source code are now
properly shown to the model (previously only supported HTML)
([gif](https://imgur.com/a/Q58U4qJ),
[pr](https://github.com/google-gemini/gemini-cli/pull/11284) by
[@abhipatel12](https://github.com/abhipatel12))
- **Non-interactive MCP commands:** Users can now run MCP slash commands in
non-interactive mode `gemini "/some-mcp-prompt"`.
([pr](https://github.com/google-gemini/gemini-cli/pull/10194) by
[@capachino](https://github.com/capachino))
- **Removal of deprecated flags:** Weve finally removed a number of deprecated
flags to cleanup Gemini CLIs invocation profile:
- `--all-files` / `-a` in favor of `@` from within Gemini CLI.
([pr](https://github.com/google-gemini/gemini-cli/pull/11228) by
[@allenhutchison](https://github.com/allenhutchison))
- `--telemetry-*` flags in favor of
[environment variables](https://github.com/google-gemini/gemini-cli/pull/11318)
([pr](https://github.com/google-gemini/gemini-cli/pull/11318) by
[@allenhutchison](https://github.com/allenhutchison))
## v0.10.0 - Gemini CLI weekly update - 2025-10-13
- **Polish:** The team has been heads down bug fixing and investing heavily into
polishing existing flows, tools, and interactions.
- **Interactive Shell Tool calling:** Gemini CLI can now also execute
interactive tools if needed
([pr](https://github.com/google-gemini/gemini-cli/pull/11225) by
[@galz10](https://github.com/galz10)).
- **Alt+Key support:** Enables broader support for Alt+Key keyboard shortcuts
across different terminals.
([pr](https://github.com/google-gemini/gemini-cli/pull/10767) by
[@srivatsj](https://github.com/srivatsj)).
- **Telemetry Diff stats:** Track line changes made by the model and user during
file operations via OTEL.
([pr](https://github.com/google-gemini/gemini-cli/pull/10819) by
[@jerop](https://github.com/jerop)).
## v0.9.0 - Gemini CLI weekly update - 2025-10-06
- 🎉 **Interactive Shell:** Run interactive commands like `vim`, `rebase -i`, or
+5 -15
View File
@@ -37,22 +37,12 @@ file in your project's temporary directory, typically located at
## 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.
The Checkpointing feature is disabled by default. To enable it, you need to 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.
> **Note:** The `--checkpointing` command-line flag was removed in version
> 0.11.0. Checkpointing can now only be enabled through the `settings.json`
> configuration file.
Add the following key to your `settings.json`:
+18 -6
View File
@@ -30,8 +30,9 @@ Slash commands provide meta-level control over the CLI itself.
checkpoints are:
- Linux/macOS: `~/.gemini/tmp/<project_hash>/`
- Windows: `C:\Users\<YourUsername>\.gemini\tmp\<project_hash>\`
- When you run `/chat list`, the CLI only scans these specific
directories to find available checkpoints.
- **Behavior:** Chats are saved into a project-specific directory,
determined by where you run the CLI. Consequently, saved chats are
only accessible when working within that same project.
- **Note:** These checkpoints are for manually saving and resuming
conversation states. For automatic checkpoints created before file
modifications, see the
@@ -39,8 +40,14 @@ Slash commands provide meta-level control over the CLI itself.
- **`resume`**
- **Description:** Resumes a conversation from a previous save.
- **Usage:** `/chat resume <tag>`
- **Note:** You can only resume chats that were saved within the current
project. To resume a chat from a different project, you must run the
Gemini CLI from that project's directory.
- **`list`**
- **Description:** Lists available tags for chat state resumption.
- **Note:** This command only lists chats saved within the current
project. Because chat history is project-scoped, chats saved in other
project directories will not be displayed.
- **`delete`**
- **Description:** Deletes a saved conversation checkpoint.
- **Usage:** `/chat delete <tag>`
@@ -121,6 +128,9 @@ Slash commands provide meta-level control over the CLI itself.
- **Description:** Restarts all MCP servers and re-discovers their
available tools.
- [**`/model`**](./model.md)
- **Description:** Opens a dialog to choose your Gemini model.
- **`/memory`**
- **Description:** Manage the AI's instructional context (hierarchical memory
loaded from `GEMINI.md` files).
@@ -151,17 +161,19 @@ Slash commands provide meta-level control over the CLI itself.
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](../get-started/configuration.md). See
- **Note:** Only available if checkpointing is configured via
[settings](../get-started/configuration.md). See
[Checkpointing documentation](../cli/checkpointing.md) for more details.
- **`/settings`**
- [**`/settings`**](./settings.md)
- **Description:** Open the settings editor to view and modify Gemini CLI
settings.
- **Details:** This command provides a user-friendly interface for changing
settings that control the behavior and appearance of Gemini CLI. It is
equivalent to manually editing the `.gemini/settings.json` file, but with
validation and guidance to prevent errors.
validation and guidance to prevent errors. See the
[settings documentation](./settings.md) for a full list of available
settings.
- **Usage:** Simply run `/settings` and the editor will open. You can then
browse or search for specific settings, view their current values, and
modify them as desired. Changes to some settings are applied immediately,
+4 -6
View File
@@ -564,18 +564,16 @@ for that specific session.
- **`--yolo`**:
- Enables YOLO mode, which automatically approves all tool calls.
- **`--telemetry`**:
- Enables [telemetry](../telemetry.md).
- Enables [telemetry](./telemetry.md).
- **`--telemetry-target`**:
- Sets the telemetry target. See [telemetry](../telemetry.md) for more
- 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
- 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)
- 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.
+22 -3
View File
@@ -202,6 +202,26 @@ allowlisting with `coreTools`, as it relies on blocking known-bad commands, and
clever users may find ways to bypass simple string-based blocks. **Allowlisting
is the recommended approach.**
### Disabling YOLO Mode
To ensure that users cannot bypass the confirmation prompt for tool execution,
you can disable YOLO mode at the policy level. This adds a critical layer of
safety, as it prevents the model from executing tools without explicit user
approval.
**Example:** Force all tool executions to require user confirmation.
```json
{
"security": {
"disableYoloMode": true
}
}
```
This setting is highly recommended in an enterprise environment to prevent
unintended tool execution.
## Managing Custom Tools (MCP Servers)
If your organization uses custom tools via
@@ -343,9 +363,8 @@ containerized environment.
}
```
You can also specify a custom, hardened Docker image for the sandbox using the
`--sandbox-image` command-line argument or by building a custom
`sandbox.Dockerfile` as described in the
You can also specify a custom, hardened Docker image for the sandbox by building
a custom `sandbox.Dockerfile` as described in the
[Sandboxing documentation](./sandbox.md).
## Controlling Network Access via Proxy
+2 -2
View File
@@ -13,8 +13,8 @@ Git).
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.
when you use the `@` command to share files, any paths in your `.geminiignore`
file will be automatically excluded.
For the most part, `.geminiignore` follows the conventions of `.gitignore`
files:
+4 -1
View File
@@ -7,11 +7,14 @@ overview of Gemini CLI, see the [main documentation page](../index.md).
## Basic features
- **[Commands](./commands.md):** A reference for all built-in slash commands
(e.g., `/help`, `/chat`, `/tools`).
- **[Custom Commands](./custom-commands.md):** Create your own commands and
shortcuts for frequently used prompts.
- **[Headless Mode](./headless.md):** Use Gemini CLI programmatically for
scripting and automation.
- **[Model Selection](./model.md):** Configure the Gemini AI model used by the
CLI.
- **[Settings](./settings.md):** Configure various aspects of the CLI's behavior
and appearance.
- **[Themes](./themes.md):** Customizing the CLI's appearance with different
themes.
- **[Keyboard Shortcuts](./keyboard-shortcuts.md):** A reference for all
+131 -56
View File
@@ -1,68 +1,143 @@
# Gemini CLI Keyboard Shortcuts
This document lists the available keyboard shortcuts in the Gemini CLI.
Gemini CLI ships with a set of default keyboard shortcuts for editing input,
navigating history, and controlling the UI. Use this reference to learn the
available combinations.
## General
<!-- KEYBINDINGS-AUTOGEN:START -->
| Shortcut | Description |
| -------- | --------------------------------------------------------------------------------------------------------------------- |
| `Esc` | Close dialogs and suggestions. |
| `Ctrl+C` | Cancel the ongoing request and clear the input. Press twice to exit the application. |
| `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+Y` | Toggle auto-approval (YOLO mode) for all tool calls. |
#### Basic Controls
## Input Prompt
| Action | Keys |
| -------------------------------------------- | ------- |
| Confirm the current selection or choice. | `Enter` |
| Dismiss dialogs or cancel the current focus. | `Esc` |
| 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 |
| `Esc` (double press) | 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. |
#### Cursor Movement
## Suggestions
| Action | Keys |
| ----------------------------------------- | ---------------------- |
| Move the cursor to the start of the line. | `Ctrl + A`<br />`Home` |
| Move the cursor to the end of the line. | `Ctrl + E`<br />`End` |
| Shortcut | Description |
| --------------- | -------------------------------------- |
| `Down Arrow` | Navigate down through the suggestions. |
| `Tab` / `Enter` | Accept the selected suggestion. |
| `Up Arrow` | Navigate up through the suggestions. |
#### Editing
## Radio Button Select
| Action | Keys |
| ------------------------------------------------ | ----------------------------------------- |
| Delete from the cursor to the end of the line. | `Ctrl + K` |
| Delete from the cursor to the start of the line. | `Ctrl + U` |
| Clear all text in the input field. | `Ctrl + C` |
| Delete the previous word. | `Ctrl + Backspace`<br />`Cmd + Backspace` |
| 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. |
#### Screen Control
## IDE Integration
| Action | Keys |
| -------------------------------------------- | ---------- |
| Clear the terminal screen and redraw the UI. | `Ctrl + L` |
| Shortcut | Description |
| -------- | --------------------------------- |
| `Ctrl+G` | See context CLI received from IDE |
#### Scrolling
| Action | Keys |
| ------------------------ | -------------------- |
| Scroll content up. | `Shift + Up Arrow` |
| Scroll content down. | `Shift + Down Arrow` |
| Scroll to the top. | `Home` |
| Scroll to the bottom. | `End` |
| Scroll up by one page. | `Page Up` |
| Scroll down by one page. | `Page Down` |
#### History & Search
| Action | Keys |
| -------------------------------------------- | --------------------- |
| Show the previous entry in history. | `Ctrl + P (no Shift)` |
| Show the next entry in history. | `Ctrl + N (no Shift)` |
| Start reverse search through history. | `Ctrl + R` |
| Insert the selected reverse-search match. | `Enter (no Ctrl)` |
| Accept a suggestion while reverse searching. | `Tab` |
#### Navigation
| Action | Keys |
| -------------------------------- | ------------------------------------------- |
| Move selection up in lists. | `Up Arrow (no Shift)` |
| Move selection down in lists. | `Down Arrow (no Shift)` |
| Move up within dialog options. | `Up Arrow (no Shift)`<br />`K (no Shift)` |
| Move down within dialog options. | `Down Arrow (no Shift)`<br />`J (no Shift)` |
#### Suggestions & Completions
| Action | Keys |
| --------------------------------------- | -------------------------------------------------- |
| Accept the inline suggestion. | `Tab`<br />`Enter (no Ctrl)` |
| Move to the previous completion option. | `Up Arrow (no Shift)`<br />`Ctrl + P (no Shift)` |
| Move to the next completion option. | `Down Arrow (no Shift)`<br />`Ctrl + N (no Shift)` |
| Expand an inline suggestion. | `Right Arrow` |
| Collapse an inline suggestion. | `Left Arrow` |
#### Text Input
| Action | Keys |
| ------------------------------------ | ------------------------------------------------------------------------------------------- |
| Submit the current prompt. | `Enter (no Ctrl, no Shift, no Cmd, not Paste)` |
| Insert a newline without submitting. | `Ctrl + Enter`<br />`Cmd + Enter`<br />`Paste + Enter`<br />`Shift + Enter`<br />`Ctrl + J` |
#### External Tools
| Action | Keys |
| ---------------------------------------------- | ---------- |
| Open the current prompt in an external editor. | `Ctrl + X` |
| Paste from the clipboard. | `Ctrl + V` |
#### App Controls
| Action | Keys |
| ----------------------------------------------------------------- | ---------- |
| Toggle detailed error information. | `F12` |
| Toggle the full TODO list. | `Ctrl + T` |
| Toggle IDE context details. | `Ctrl + G` |
| Toggle Markdown rendering. | `Cmd + M` |
| Toggle copy mode when the terminal is using the alternate buffer. | `Ctrl + S` |
| Expand a height-constrained response to show additional lines. | `Ctrl + S` |
| Toggle focus between the shell and Gemini input. | `Ctrl + F` |
#### Session Control
| Action | Keys |
| -------------------------------------------- | ---------- |
| Cancel the current request or quit the CLI. | `Ctrl + C` |
| Exit the CLI when the input buffer is empty. | `Ctrl + D` |
<!-- KEYBINDINGS-AUTOGEN:END -->
## Additional Context-Specific Shortcuts
- `Ctrl+Y`: Toggle YOLO (auto-approval) mode for tool calls.
- `Shift+Tab`: Toggle Auto Edit (auto-accept edits) mode.
- `Option+M` (macOS): Entering `µ` with Option+M also toggles Markdown
rendering, matching `Cmd+M`.
- `!` on an empty prompt: Enter or exit shell mode.
- `\` (at end of a line) + `Enter`: Insert a newline without leaving single-line
mode.
- `Ctrl+Delete` / `Meta+Delete`: Delete the word to the right of the cursor.
- `Ctrl+B` or `Left Arrow`: Move the cursor one character to the left while
editing text.
- `Ctrl+F` or `Right Arrow`: Move the cursor one character to the right; with an
embedded shell attached, `Ctrl+F` still toggles focus.
- `Ctrl+D` or `Delete`: Remove the character immediately to the right of the
cursor.
- `Ctrl+H` or `Backspace`: Remove the character immediately to the left of the
cursor.
- `Ctrl+Left Arrow` / `Meta+Left Arrow` / `Meta+B`: Move one word to the left.
- `Ctrl+Right Arrow` / `Meta+Right Arrow` / `Meta+F`: Move one word to the
right.
- `Ctrl+W`: Delete the word to the left of the cursor (in addition to
`Ctrl+Backspace` / `Cmd+Backspace`).
- `Ctrl+Z` / `Ctrl+Shift+Z`: Undo or redo the most recent text edit.
- `Meta+Enter`: Open the current input in an external editor (alias for
`Ctrl+X`).
- `Esc` pressed twice quickly: Clear the current input buffer.
- `Up Arrow` / `Down Arrow`: When the cursor is at the top or bottom of a
single-line input, navigate backward or forward through prompt history.
- `Number keys (1-9, multi-digit)` inside selection dialogs: Jump directly to
the numbered radio option and confirm when the full number is entered.
+56
View File
@@ -0,0 +1,56 @@
## Model Routing
Gemini CLI includes a model routing feature that automatically switches to a
fallback model in case of a model failure. This feature is enabled by default
and provides resilience when the primary model is unavailable.
## How it Works
Model routing is not based on prompt complexity, but is a fallback mechanism.
Here's how it works:
1. **Model Failure:** If the currently selected model fails to respond (for
example, due to a server error or other issue), the CLI will initiate the
fallback process.
2. **User Consent:** The CLI will prompt you to ask if you want to switch to
the fallback model. This is handled by the `fallbackModelHandler`.
3. **Fallback Activation:** If you consent, the CLI will activate the fallback
mode by calling `config.setFallbackMode(true)`.
4. **Model Switch:** On the next request, the CLI will use the
`DEFAULT_GEMINI_FLASH_MODEL` as the fallback model. This is handled by the
`resolveModel` function in
`packages/cli/src/zed-integration/zedIntegration.ts` which checks if
`isInFallbackMode()` is true.
## Configuration
Model routing is controlled by the `useModelRouter` setting in your
`settings.json` file.
- **`"experimental.useModelRouter": true` (Default):** Enables the model
routing/fallback feature.
- **`"experimental.useModelRouter": false`:** Disables the model
routing/fallback feature. If a model fails, the CLI will not attempt to switch
to a fallback model.
### Model Selection Precedence
The model used by Gemini CLI is determined by the following order of precedence:
1. **`--model` command-line flag:** A model specified with the `--model` flag
when launching the CLI will always be used.
2. **`GEMINI_MODEL` environment variable:** If the `--model` flag is not used,
the CLI will use the model specified in the `GEMINI_MODEL` environment
variable.
3. **`model.name` in `settings.json`:** If neither of the above are set, the
model specified in the `model.name` property of your `settings.json` file
will be used.
4. **Default Model:** If none of the above are set, the default model will be
used. The default model is determined by the `useModelRouter` setting:
- If `useModelRouter` is `true`, the default model is `"auto"`.
- If `useModelRouter` is `false`, the default model is the standard Gemini
model.
+58
View File
@@ -0,0 +1,58 @@
# Gemini CLI Model Selection (`/model` Command)
Select your Gemini CLI model. The `/model` command opens a dialog where you can
configure the model used by Gemini CLI, giving you more control over your
results.
## How to use the `/model` command
Use the following command in Gemini CLI:
```
/model
```
Running this command will open a dialog with your model options:
| Option | Description | Models |
| ------------------ | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| Auto (recommended) | Let the system choose the best model for your task. | gemini-3-pro-preview (if enabled), gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite |
| Pro | For complex tasks that require deep reasoning and creativity. | gemini-3-pro-preview (if enabled), gemini-2.5-pro |
| Flash | For tasks that need a balance of speed and reasoning. | gemini-2.5-flash |
| Flash-Lite | For simple tasks that need to be done quickly. | gemini-2.5-flash-lite |
### Gemini 3 Pro and Preview Features
Note: Gemini 3 is not currently available on all account types. To learn more
about Gemini 3 access, refer to
[Gemini 3 Pro on Gemini CLI](../get-started/gemini-3.md).
To enable Gemini 3 Pro (if available), enable
[**Preview features** by using the `settings` command](../cli/settings.md). Once
enabled, Gemini CLI will attempt to use Gemini 3 Pro when you select **Auto** or
**Pro**. Both **Auto** and **Pro** will try to use Gemini 3 Pro before falling
back to Gemini 2.5 Pro.
You can also use the `--model` flag to specify a particular Gemini model on
startup. For more details, refer to the
[configuration documentation](./configuration.md).
Changes to these settings will be applied to all subsequent interactions with
Gemini CLI.
## Best practices for model selection
- **Default to Auto (recommended).** For most users, the _Auto (recommended)_
model provides a balance between speed and performance, automatically
selecting the correct model based on the complexity of the task. Example:
Developing a web application could include a mix of complex tasks (building
architecture and scaffolding the project) and simple tasks (generating CSS).
- **Switch to Pro if you aren't getting the results you want.** If you think you
need your model to be a little "smarter," use Pro. Pro will provide you with
the highest levels of reasoning and creativity. Example: A complex or
multi-stage debugging task.
- **Switch to Flash or Flash-Lite if you need faster results.** If you need a
simple response quickly, Flash or Flash-Lite is the best option. Example:
Converting a JSON object to a YAML string.
+113
View File
@@ -0,0 +1,113 @@
# Gemini CLI Settings (`/settings` Command)
Control your Gemini CLI experience with the `/settings` command. The `/settings`
command opens a dialog to view and edit all your Gemini CLI settings, including
your UI experience, keybindings, and accessibility features.
Your Gemini CLI settings are stored in a `settings.json` file. In addition to
using the `/settings` command, you can also edit them in one of the following
locations:
- **User settings**: `~/.gemini/settings.json`
- **Workspace settings**: `your-project/.gemini/settings.json`
Note: Workspace settings override user settings.
## Settings reference
Here is a list of all the available settings, grouped by category and ordered as
they appear in the UI.
### General
| UI Label | Setting | Description | Default |
| ------------------------------- | ---------------------------------- | ---------------------------------------------------------------------------- | ----------- |
| Preview Features (e.g., models) | `general.previewFeatures` | Enable preview features (e.g., preview models). | `false` |
| Vim Mode | `general.vimMode` | Enable Vim keybindings. | `false` |
| Disable Auto Update | `general.disableAutoUpdate` | Disable automatic updates. | `false` |
| Enable Prompt Completion | `general.enablePromptCompletion` | Enable AI-powered prompt completion suggestions while typing. | `false` |
| Debug Keystroke Logging | `general.debugKeystrokeLogging` | Enable debug logging of keystrokes to the console. | `false` |
| Session Retention | `general.sessionRetention` | Settings for automatic session cleanup. This feature is disabled by default. | `undefined` |
| Enable Session Cleanup | `general.sessionRetention.enabled` | Enable automatic session cleanup. | `false` |
### Output
| UI Label | Setting | Description | Default |
| ------------- | --------------- | ------------------------------------------------------ | ------- |
| Output Format | `output.format` | The format of the CLI output. Can be `text` or `json`. | `text` |
### UI
| UI Label | Setting | Description | Default |
| ------------------------------ | ---------------------------------------- | -------------------------------------------------------------------- | ------- |
| Hide Window Title | `ui.hideWindowTitle` | Hide the window title bar. | `false` |
| Show Status in Title | `ui.showStatusInTitle` | Show Gemini CLI status and thoughts in the terminal window title. | `false` |
| Hide Tips | `ui.hideTips` | Hide helpful tips in the UI. | `false` |
| Hide Banner | `ui.hideBanner` | Hide the application banner. | `false` |
| Hide Context Summary | `ui.hideContextSummary` | Hide the context summary (GEMINI.md, MCP servers) above the input. | `false` |
| Hide CWD | `ui.footer.hideCWD` | Hide the current working directory path in the footer. | `false` |
| Hide Sandbox Status | `ui.footer.hideSandboxStatus` | Hide the sandbox status indicator in the footer. | `false` |
| Hide Model Info | `ui.footer.hideModelInfo` | Hide the model name and context usage in the footer. | `false` |
| Hide Context Window Percentage | `ui.footer.hideContextPercentage` | Hides the context window remaining percentage. | `true` |
| Hide Footer | `ui.hideFooter` | Hide the footer from the UI. | `false` |
| Show Memory Usage | `ui.showMemoryUsage` | Display memory usage information in the UI. | `false` |
| Show Line Numbers | `ui.showLineNumbers` | Show line numbers in the chat. | `false` |
| Show Citations | `ui.showCitations` | Show citations for generated text in the chat. | `false` |
| Use Full Width | `ui.useFullWidth` | Use the entire width of the terminal for output. | `true` |
| Use Alternate Screen Buffer | `ui.useAlternateBuffer` | Use an alternate screen buffer for the UI, preserving shell history. | `true` |
| Disable Loading Phrases | `ui.accessibility.disableLoadingPhrases` | Disable loading phrases for accessibility. | `false` |
| Screen Reader Mode | `ui.accessibility.screenReader` | Render output in plain-text to be more screen reader accessible. | `false` |
### IDE
| UI Label | Setting | Description | Default |
| -------- | ------------- | ---------------------------- | ------- |
| IDE Mode | `ide.enabled` | Enable IDE integration mode. | `false` |
### Model
| UI Label | Setting | Description | Default |
| ----------------------- | ---------------------------- | -------------------------------------------------------------------------------------- | ------- |
| Max Session Turns | `model.maxSessionTurns` | Maximum number of user/model/tool turns to keep in a session. -1 means unlimited. | `-1` |
| Compression Threshold | `model.compressionThreshold` | The fraction of context usage at which to trigger context compression (e.g. 0.2, 0.3). | `0.2` |
| Skip Next Speaker Check | `model.skipNextSpeakerCheck` | Skip the next speaker check. | `true` |
### Context
| UI Label | Setting | Description | Default |
| ------------------------------------ | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| Memory Discovery Max Dirs | `context.discoveryMaxDirs` | Maximum number of directories to search for memory. | `200` |
| Load Memory From Include Directories | `context.loadMemoryFromIncludeDirectories` | Controls how /memory refresh loads GEMINI.md files. When true, include directories are scanned; when false, only the current directory is used. | `false` |
| Respect .gitignore | `context.fileFiltering.respectGitIgnore` | Respect .gitignore files when searching. | `true` |
| Respect .geminiignore | `context.fileFiltering.respectGeminiIgnore` | Respect .geminiignore files when searching. | `true` |
| Enable Recursive File Search | `context.fileFiltering.enableRecursiveFileSearch` | Enable recursive file search functionality when completing @ references in the prompt. | `true` |
| Disable Fuzzy Search | `context.fileFiltering.disableFuzzySearch` | Disable fuzzy search when searching for files. | `false` |
### Tools
| UI Label | Setting | Description | Default |
| -------------------------------- | ------------------------------------ | --------------------------------------------------------------------------------------------------------------- | ------- |
| Enable Interactive Shell | `tools.shell.enableInteractiveShell` | Use node-pty for an interactive shell experience. Fallback to child_process still applies. | `true` |
| Show Color | `tools.shell.showColor` | Show color in shell output. | `false` |
| Auto Accept | `tools.autoAccept` | Automatically accept and execute tool calls that are considered safe (e.g., read-only operations). | `false` |
| Use Ripgrep | `tools.useRipgrep` | Use ripgrep for file content search instead of the fallback implementation. Provides faster search performance. | `true` |
| Enable Tool Output Truncation | `tools.enableToolOutputTruncation` | Enable truncation of large tool outputs. | `true` |
| Tool Output Truncation Threshold | `tools.truncateToolOutputThreshold` | Truncate tool output if it is larger than this many characters. Set to -1 to disable. | `10000` |
| Tool Output Truncation Lines | `tools.truncateToolOutputLines` | The number of lines to keep when truncating tool output. | `100` |
| Enable Message Bus Integration | `tools.enableMessageBusIntegration` | Enable policy-based tool confirmation via message bus integration. | `false` |
### Security
| UI Label | Setting | Description | Default |
| -------------------------- | ------------------------------ | -------------------------------------------------- | ------- |
| Disable YOLO Mode | `security.disableYoloMode` | Disable YOLO mode, even if enabled by a flag. | `false` |
| Blocks extensions from Git | `security.blockGitExtensions` | Blocks installing and loading extensions from Git. | `false` |
| Folder Trust | `security.folderTrust.enabled` | Setting to track whether Folder trust is enabled. | `false` |
### Experimental
| UI Label | Setting | Description | Default |
| ----------------------------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------- | ------- |
| Use Model Router | `experimental.useModelRouter` | Enable model routing to route requests to the best model based on complexity. | `true` |
| Enable Codebase Investigator | `experimental.codebaseInvestigatorSettings.enabled` | Enable the Codebase Investigator agent. | `true` |
| Codebase Investigator Max Num Turns | `experimental.codebaseInvestigatorSettings.maxNumTurns` | Maximum number of turns for the Codebase Investigator agent. | `10` |
+445 -114
View File
@@ -15,8 +15,29 @@ Learn how to enable and setup OpenTelemetry for Gemini CLI.
- [Collector-Based Export (Advanced)](#collector-based-export-advanced-1)
- [Logs and Metrics](#logs-and-metrics)
- [Logs](#logs)
- [Sessions](#sessions)
- [Tools](#tools)
- [Files](#files)
- [API](#api)
- [Model Routing](#model-routing)
- [Chat and Streaming](#chat-and-streaming)
- [Resilience](#resilience)
- [Extensions](#extensions)
- [Agent Runs](#agent-runs)
- [IDE](#ide)
- [UI](#ui)
- [Metrics](#metrics)
- [Custom](#custom)
- [Sessions](#sessions-1)
- [Tools](#tools-1)
- [API](#api-1)
- [Token Usage](#token-usage)
- [Files](#files-1)
- [Chat and Streaming](#chat-and-streaming-1)
- [Model Routing](#model-routing-1)
- [Agent Runs](#agent-runs-1)
- [UI](#ui-1)
- [Performance](#performance)
- [GenAI Semantic Convention](#genai-semantic-convention)
## Key Benefits
@@ -51,17 +72,17 @@ observability framework — Gemini CLI's observability system provides:
## Configuration
All telemetry behavior is controlled through your `.gemini/settings.json` file.
These settings can be overridden by environment variables or CLI flags.
Environment variables can be used to override the settings in the file.
| Setting | Environment Variable | CLI Flag | Description | Values | Default |
| -------------- | -------------------------------- | -------------------------------------------------------- | ------------------------------------------------- | ----------------- | ----------------------- |
| `enabled` | `GEMINI_TELEMETRY_ENABLED` | `--telemetry` / `--no-telemetry` | Enable or disable telemetry | `true`/`false` | `false` |
| `target` | `GEMINI_TELEMETRY_TARGET` | `--telemetry-target <local\|gcp>` | Where to send telemetry data | `"gcp"`/`"local"` | `"local"` |
| `otlpEndpoint` | `GEMINI_TELEMETRY_OTLP_ENDPOINT` | `--telemetry-otlp-endpoint <URL>` | OTLP collector endpoint | URL string | `http://localhost:4317` |
| `otlpProtocol` | `GEMINI_TELEMETRY_OTLP_PROTOCOL` | `--telemetry-otlp-protocol <grpc\|http>` | OTLP transport protocol | `"grpc"`/`"http"` | `"grpc"` |
| `outfile` | `GEMINI_TELEMETRY_OUTFILE` | `--telemetry-outfile <path>` | Save telemetry to file (overrides `otlpEndpoint`) | file path | - |
| `logPrompts` | `GEMINI_TELEMETRY_LOG_PROMPTS` | `--telemetry-log-prompts` / `--no-telemetry-log-prompts` | Include prompts in telemetry logs | `true`/`false` | `true` |
| `useCollector` | `GEMINI_TELEMETRY_USE_COLLECTOR` | - | Use external OTLP collector (advanced) | `true`/`false` | `false` |
| Setting | Environment Variable | Description | Values | Default |
| -------------- | -------------------------------- | ------------------------------------------------- | ----------------- | ----------------------- |
| `enabled` | `GEMINI_TELEMETRY_ENABLED` | Enable or disable telemetry | `true`/`false` | `false` |
| `target` | `GEMINI_TELEMETRY_TARGET` | Where to send telemetry data | `"gcp"`/`"local"` | `"local"` |
| `otlpEndpoint` | `GEMINI_TELEMETRY_OTLP_ENDPOINT` | OTLP collector endpoint | URL string | `http://localhost:4317` |
| `otlpProtocol` | `GEMINI_TELEMETRY_OTLP_PROTOCOL` | OTLP transport protocol | `"grpc"`/`"http"` | `"grpc"` |
| `outfile` | `GEMINI_TELEMETRY_OUTFILE` | Save telemetry to file (overrides `otlpEndpoint`) | file path | - |
| `logPrompts` | `GEMINI_TELEMETRY_LOG_PROMPTS` | Include prompts in telemetry logs | `true`/`false` | `true` |
| `useCollector` | `GEMINI_TELEMETRY_USE_COLLECTOR` | Use external OTLP collector (advanced) | `true`/`false` | `false` |
**Note on boolean environment variables:** For the boolean settings (`enabled`,
`logPrompts`, `useCollector`), setting the corresponding environment variable to
@@ -204,16 +225,20 @@ For local development and debugging, you can capture telemetry data locally:
The following section describes the structure of logs and metrics generated for
Gemini CLI.
The `session.id`, `installation.id`, and `user.email` are included as common
attributes on all logs and metrics.
The `session.id`, `installation.id`, and `user.email` (available only when
authenticated with a Google account) are included as common attributes on all
logs and metrics.
### Logs
Logs are timestamped records of specific events. The following events are logged
for Gemini CLI:
for Gemini CLI, grouped by category.
- `gemini_cli.config`: This event occurs once at startup with the CLI's
configuration.
#### Sessions
Captures startup configuration and user prompt submissions.
- `gemini_cli.config`: Emitted once at startup with the CLI configuration.
- **Attributes**:
- `model` (string)
- `embedding_model` (string)
@@ -222,81 +247,47 @@ for Gemini CLI:
- `approval_mode` (string)
- `api_key_enabled` (boolean)
- `vertex_ai_enabled` (boolean)
- `code_assist_enabled` (boolean)
- `log_prompts_enabled` (boolean)
- `log_user_prompts_enabled` (boolean)
- `file_filtering_respect_git_ignore` (boolean)
- `debug_mode` (boolean)
- `mcp_servers` (string)
- `output_format` (string: "text", "json", or "stream-json")
- `mcp_servers_count` (int)
- `extensions` (string)
- `extension_ids` (string)
- `extension_count` (int)
- `mcp_tools` (string, if applicable)
- `mcp_tools_count` (int, if applicable)
- `output_format` ("text", "json", or "stream-json")
- `gemini_cli.user_prompt`: This event occurs when a user submits a prompt.
- `gemini_cli.user_prompt`: Emitted when a user submits a prompt.
- **Attributes**:
- `prompt_length` (int)
- `prompt_id` (string)
- `prompt` (string, this attribute is excluded if `log_prompts_enabled` is
configured to be `false`)
- `prompt` (string; excluded if `telemetry.logPrompts` is `false`)
- `auth_type` (string)
- `gemini_cli.tool_call`: This event occurs for each function call.
#### Tools
Captures tool executions, output truncation, and Smart Edit behavior.
- `gemini_cli.tool_call`: Emitted for each tool (function) call.
- **Attributes**:
- `function_name`
- `function_args`
- `duration_ms`
- `success` (boolean)
- `decision` (string: "accept", "reject", "auto_accept", or "modify", if
applicable)
- `decision` ("accept", "reject", "auto_accept", or "modify", if applicable)
- `error` (if applicable)
- `error_type` (if applicable)
- `prompt_id` (string)
- `tool_type` ("native" or "mcp")
- `mcp_server_name` (string, if applicable)
- `extension_name` (string, if applicable)
- `extension_id` (string, if applicable)
- `content_length` (int, if applicable)
- `metadata` (if applicable, dictionary of string -> any)
- `metadata` (if applicable)
- `gemini_cli.file_operation`: This event occurs for each file operation.
- **Attributes**:
- `tool_name` (string)
- `operation` (string: "create", "read", "update")
- `lines` (int, if applicable)
- `mimetype` (string, if applicable)
- `extension` (string, if applicable)
- `programming_language` (string, if applicable)
- `diff_stat` (json string, if applicable): A JSON string with the following
members:
- `ai_added_lines` (int)
- `ai_removed_lines` (int)
- `user_added_lines` (int)
- `user_removed_lines` (int)
- `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.tool_output_truncated`: This event occurs when the output of a
tool call is too large and gets truncated.
- `gemini_cli.tool_output_truncated`: Output of a tool call was truncated.
- **Attributes**:
- `tool_name` (string)
- `original_content_length` (int)
@@ -305,32 +296,231 @@ for Gemini CLI:
- `lines` (int)
- `prompt_id` (string)
- `gemini_cli.malformed_json_response`: This event occurs when a `generateJson`
response from Gemini API cannot be parsed as a json.
- `gemini_cli.smart_edit_strategy`: Smart Edit strategy chosen.
- **Attributes**:
- `model`
- `strategy` (string)
- `gemini_cli.flash_fallback`: This event occurs when Gemini CLI switches to
flash as fallback.
- `gemini_cli.smart_edit_correction`: Smart Edit correction result.
- **Attributes**:
- `auth_type`
- `correction` ("success" | "failure")
- `gemini_cli.slash_command`: This event occurs when a user executes a slash
command.
- `gen_ai.client.inference.operation.details`: This event provides detailed
information about the GenAI operation, aligned with [OpenTelemetry GenAI
semantic conventions for events].
- **Attributes**:
- `gen_ai.request.model` (string)
- `gen_ai.provider.name` (string)
- `gen_ai.operation.name` (string)
- `gen_ai.input.messages` (json string)
- `gen_ai.output.messages` (json string)
- `gen_ai.response.finish_reasons` (array of strings)
- `gen_ai.usage.input_tokens` (int)
- `gen_ai.usage.output_tokens` (int)
- `gen_ai.request.temperature` (float)
- `gen_ai.request.top_p` (float)
- `gen_ai.request.top_k` (int)
- `gen_ai.request.max_tokens` (int)
- `gen_ai.system_instructions` (json string)
- `server.address` (string)
- `server.port` (int)
#### Files
Tracks file operations performed by tools.
- `gemini_cli.file_operation`: Emitted for each file operation.
- **Attributes**:
- `tool_name` (string)
- `operation` ("create" | "read" | "update")
- `lines` (int, optional)
- `mimetype` (string, optional)
- `extension` (string, optional)
- `programming_language` (string, optional)
#### API
Captures Gemini API requests, responses, and errors.
- `gemini_cli.api_request`: Request sent to Gemini API.
- **Attributes**:
- `model` (string)
- `prompt_id` (string)
- `request_text` (string, optional)
- `gemini_cli.api_response`: Response received from Gemini API.
- **Attributes**:
- `model` (string)
- `status_code` (int|string)
- `duration_ms` (int)
- `input_token_count` (int)
- `output_token_count` (int)
- `cached_content_token_count` (int)
- `thoughts_token_count` (int)
- `tool_token_count` (int)
- `total_token_count` (int)
- `response_text` (string, optional)
- `prompt_id` (string)
- `auth_type` (string)
- `gemini_cli.api_error`: API request failed.
- **Attributes**:
- `model` (string)
- `error` (string)
- `error_type` (string)
- `status_code` (int|string)
- `duration_ms` (int)
- `prompt_id` (string)
- `auth_type` (string)
- `gemini_cli.malformed_json_response`: `generateJson` response could not be
parsed.
- **Attributes**:
- `model` (string)
#### Model Routing
Tracks model selections via slash commands and router decisions.
- `gemini_cli.slash_command`: A slash command was executed.
- **Attributes**:
- `command` (string)
- `subcommand` (string, if applicable)
- `subcommand` (string, optional)
- `status` ("success" | "error")
- `gemini_cli.extension_enable`: This event occurs when an extension is enabled
- `gemini_cli.extension_install`: This event occurs when an extension is
installed
- `gemini_cli.slash_command.model`: Model was selected via slash command.
- **Attributes**:
- `model_name` (string)
- `gemini_cli.model_routing`: Model router made a decision.
- **Attributes**:
- `decision_model` (string)
- `decision_source` (string)
- `routing_latency_ms` (int)
- `reasoning` (string, optional)
- `failed` (boolean)
- `error_message` (string, optional)
#### Chat and Streaming
Observes streaming integrity, compression, and retry behavior.
- `gemini_cli.chat_compression`: Chat context was compressed.
- **Attributes**:
- `tokens_before` (int)
- `tokens_after` (int)
- `gemini_cli.chat.invalid_chunk`: Invalid chunk received from a stream.
- **Attributes**:
- `error.message` (string, optional)
- `gemini_cli.chat.content_retry`: Retry triggered due to a content error.
- **Attributes**:
- `attempt_number` (int)
- `error_type` (string)
- `retry_delay_ms` (int)
- `model` (string)
- `gemini_cli.chat.content_retry_failure`: All content retries failed.
- **Attributes**:
- `total_attempts` (int)
- `final_error_type` (string)
- `total_duration_ms` (int, optional)
- `model` (string)
- `gemini_cli.conversation_finished`: Conversation session ended.
- **Attributes**:
- `approvalMode` (string)
- `turnCount` (int)
- `gemini_cli.next_speaker_check`: Next speaker determination.
- **Attributes**:
- `prompt_id` (string)
- `finish_reason` (string)
- `result` (string)
#### Resilience
Records fallback mechanisms for models and network operations.
- `gemini_cli.flash_fallback`: Switched to a flash model as fallback.
- **Attributes**:
- `auth_type` (string)
- `gemini_cli.ripgrep_fallback`: Switched to grep as fallback for file search.
- **Attributes**:
- `error` (string, optional)
- `gemini_cli.web_fetch_fallback_attempt`: Attempted web-fetch fallback.
- **Attributes**:
- `reason` ("private_ip" | "primary_failed")
#### Extensions
Tracks extension lifecycle and settings changes.
- `gemini_cli.extension_install`: An extension was installed.
- **Attributes**:
- `extension_name` (string)
- `extension_version` (string)
- `extension_source` (string)
- `status` (string)
- `gemini_cli.extension_uninstall`: This event occurs when an extension is
uninstalled
- `gemini_cli.extension_uninstall`: An extension was uninstalled.
- **Attributes**:
- `extension_name` (string)
- `status` (string)
- `gemini_cli.extension_enable`: An extension was enabled.
- **Attributes**:
- `extension_name` (string)
- `setting_scope` (string)
- `gemini_cli.extension_disable`: An extension was disabled.
- **Attributes**:
- `extension_name` (string)
- `setting_scope` (string)
- `gemini_cli.extension_update`: An extension was updated.
- **Attributes**:
- `extension_name` (string)
- `extension_version` (string)
- `extension_previous_version` (string)
- `extension_source` (string)
- `status` (string)
#### Agent Runs
Tracks agent lifecycle and outcomes.
- `gemini_cli.agent.start`: Agent run started.
- **Attributes**:
- `agent_id` (string)
- `agent_name` (string)
- `gemini_cli.agent.finish`: Agent run finished.
- **Attributes**:
- `agent_id` (string)
- `agent_name` (string)
- `duration_ms` (int)
- `turn_count` (int)
- `terminate_reason` (string)
#### IDE
Captures IDE connectivity and conversation lifecycle events.
- `gemini_cli.ide_connection`: IDE companion connection.
- **Attributes**:
- `connection_type` (string)
#### UI
Tracks terminal rendering issues and related signals.
- `kitty_sequence_overflow`: Terminal kitty control sequence overflow.
- **Attributes**:
- `sequence_length` (int)
- `truncated_sequence` (string)
### Metrics
@@ -338,27 +528,31 @@ Metrics are numerical measurements of behavior over time.
#### Custom
##### Sessions
Counts CLI sessions at startup.
- `gemini_cli.session.count` (Counter, Int): Incremented once per CLI startup.
##### Tools
Measures tool usage and latency.
- `gemini_cli.tool.call.count` (Counter, Int): Counts tool calls.
- **Attributes**:
- `function_name`
- `success` (boolean)
- `decision` (string: "accept", "reject", or "modify", if applicable)
- `tool_type` (string: "mcp", or "native", if applicable)
- `model_added_lines` (Int, optional): Lines added by model in the proposed
changes, if applicable
- `model_removed_lines` (Int, optional): Lines removed by model in the
proposed changes, if applicable
- `user_added_lines` (Int, optional): Lines added by user edits after model
proposal, if applicable
- `user_removed_lines` (Int, optional): Lines removed by user edits after
model proposal, if applicable
- `decision` (string: "accept", "reject", "modify", or "auto_accept", if
applicable)
- `tool_type` (string: "mcp" or "native", if applicable)
- `gemini_cli.tool.call.latency` (Histogram, ms): Measures tool call latency.
- **Attributes**:
- `function_name`
- `decision` (string: "accept", "reject", or "modify", if applicable)
##### API
Tracks API request volume and latency.
- `gemini_cli.api.request.count` (Counter, Int): Counts all API requests.
- **Attributes**:
@@ -370,32 +564,167 @@ Metrics are numerical measurements of behavior over time.
latency.
- **Attributes**:
- `model`
- **Note**: This metric overlaps with `gen_ai.client.operation.duration` below
that's compliant with GenAI Semantic Conventions.
- Note: Overlaps with `gen_ai.client.operation.duration` (GenAI conventions).
- `gemini_cli.token.usage` (Counter, Int): Counts the number of tokens used.
##### Token Usage
Tracks tokens used by model and type.
- `gemini_cli.token.usage` (Counter, Int): Counts tokens used.
- **Attributes**:
- `model`
- `type` (string: "input", "output", "thought", "cache", or "tool")
- **Note**: This metric overlaps with `gen_ai.client.token.usage` below for
`input`/`output` token types that's compliant with GenAI Semantic
Conventions.
- `type` ("input", "output", "thought", "cache", or "tool")
- Note: Overlaps with `gen_ai.client.token.usage` for `input`/`output`.
##### Files
Counts file operations with basic context.
- `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.
- `programming_language` (string, if applicable): The programming language
of the file.
- `operation` ("create", "read", "update")
- `lines` (Int, optional)
- `mimetype` (string, optional)
- `extension` (string, optional)
- `programming_language` (string, optional)
- `gemini_cli.lines.changed` (Counter, Int): Number of lines changed (from file
diffs).
- **Attributes**:
- `function_name`
- `type` ("added" or "removed")
##### Chat and Streaming
Resilience counters for compression, invalid chunks, and retries.
- `gemini_cli.chat_compression` (Counter, Int): Counts chat compression
operations
operations.
- **Attributes**:
- `tokens_before`: (Int): Number of tokens in context prior to compression
- `tokens_after`: (Int): Number of tokens in context after compression
- `tokens_before` (Int)
- `tokens_after` (Int)
- `gemini_cli.chat.invalid_chunk.count` (Counter, Int): Counts invalid chunks
from streams.
- `gemini_cli.chat.content_retry.count` (Counter, Int): Counts retries due to
content errors.
- `gemini_cli.chat.content_retry_failure.count` (Counter, Int): Counts requests
where all content retries failed.
##### Model Routing
Routing latency/failures and slash-command selections.
- `gemini_cli.slash_command.model.call_count` (Counter, Int): Counts model
selections via slash command.
- **Attributes**:
- `slash_command.model.model_name` (string)
- `gemini_cli.model_routing.latency` (Histogram, ms): Model routing decision
latency.
- **Attributes**:
- `routing.decision_model` (string)
- `routing.decision_source` (string)
- `gemini_cli.model_routing.failure.count` (Counter, Int): Counts model routing
failures.
- **Attributes**:
- `routing.decision_source` (string)
- `routing.error_message` (string)
##### Agent Runs
Agent lifecycle metrics: runs, durations, and turns.
- `gemini_cli.agent.run.count` (Counter, Int): Counts agent runs.
- **Attributes**:
- `agent_name` (string)
- `terminate_reason` (string)
- `gemini_cli.agent.duration` (Histogram, ms): Agent run durations.
- **Attributes**:
- `agent_name` (string)
- `gemini_cli.agent.turns` (Histogram, turns): Turns taken per agent run.
- **Attributes**:
- `agent_name` (string)
##### UI
UI stability signals such as flicker count.
- `gemini_cli.ui.flicker.count` (Counter, Int): Counts UI frames that flicker
(render taller than terminal).
##### Performance
Optional performance monitoring for startup, CPU/memory, and phase timing.
- `gemini_cli.startup.duration` (Histogram, ms): CLI startup time by phase.
- **Attributes**:
- `phase` (string)
- `details` (map, optional)
- `gemini_cli.memory.usage` (Histogram, bytes): Memory usage.
- **Attributes**:
- `memory_type` ("heap_used", "heap_total", "external", "rss")
- `component` (string, optional)
- `gemini_cli.cpu.usage` (Histogram, percent): CPU usage percentage.
- **Attributes**:
- `component` (string, optional)
- `gemini_cli.tool.queue.depth` (Histogram, count): Number of tools in the
execution queue.
- `gemini_cli.tool.execution.breakdown` (Histogram, ms): Tool time by phase.
- **Attributes**:
- `function_name` (string)
- `phase` ("validation", "preparation", "execution", "result_processing")
- `gemini_cli.api.request.breakdown` (Histogram, ms): API request time by phase.
- **Attributes**:
- `model` (string)
- `phase` ("request_preparation", "network_latency", "response_processing",
"token_processing")
- `gemini_cli.token.efficiency` (Histogram, ratio): Token efficiency metrics.
- **Attributes**:
- `model` (string)
- `metric` (string)
- `context` (string, optional)
- `gemini_cli.performance.score` (Histogram, score): Composite performance
score.
- **Attributes**:
- `category` (string)
- `baseline` (number, optional)
- `gemini_cli.performance.regression` (Counter, Int): Regression detection
events.
- **Attributes**:
- `metric` (string)
- `severity` ("low", "medium", "high")
- `current_value` (number)
- `baseline_value` (number)
- `gemini_cli.performance.regression.percentage_change` (Histogram, percent):
Percent change from baseline when regression detected.
- **Attributes**:
- `metric` (string)
- `severity` ("low", "medium", "high")
- `current_value` (number)
- `baseline_value` (number)
- `gemini_cli.performance.baseline.comparison` (Histogram, percent): Comparison
to baseline.
- **Attributes**:
- `metric` (string)
- `category` (string)
- `current_value` (number)
- `baseline_value` (number)
#### GenAI Semantic Convention
@@ -434,3 +763,5 @@ standardized observability across GenAI applications:
[OpenTelemetry GenAI semantic conventions]:
https://github.com/open-telemetry/semantic-conventions/blob/main/docs/gen-ai/gen-ai-metrics.md
[OpenTelemetry GenAI semantic conventions for events]:
https://github.com/open-telemetry/semantic-conventions/blob/8b4f210f43136e57c1f6f47292eb6d38e3bf30bb/docs/gen-ai/gen-ai-events.md
+5
View File
@@ -88,6 +88,11 @@ color keys. For example:
- `DiffRemoved` (optional, for removed lines in diffs)
- `DiffModified` (optional, for modified lines in diffs)
You can also override individual UI text roles by adding a nested `text` object.
This object supports the keys `primary`, `secondary`, `link`, `accent`, and
`response`. When `text.response` is provided it takes precedence over
`text.primary` for rendering model responses in chat.
**Required Properties:**
- `name` (must match the key in the `customThemes` object and be a string)
+2
View File
@@ -11,6 +11,8 @@ requests sent from `packages/cli`. For a general overview of Gemini CLI, see the
registered, and used by the core.
- **[Memory Import Processor](./memport.md):** Documentation for the modular
GEMINI.md import feature using @file.md syntax.
- **[Policy Engine](./policy-engine.md):** Use the Policy Engine for
fine-grained control over tool execution.
## Role of the core
+246
View File
@@ -0,0 +1,246 @@
# Policy Engine
:::note This feature is currently in testing. To enable it, set
`tools.enableMessageBusIntegration` to `true` in your `settings.json` file. :::
The Gemini CLI includes a powerful policy engine that provides fine-grained
control over tool execution. It allows users and administrators to define rules
that determine whether a tool call should be allowed, denied, or require user
confirmation.
## Core concepts
The policy engine operates on a set of rules. Each rule is a combination of
conditions and a resulting decision. When a large language model wants to
execute a tool, the policy engine evaluates all rules to find the
highest-priority rule that matches the tool call.
A rule consists of the following main components:
- **Conditions**: Criteria that a tool call must meet for the rule to apply.
This can include the tool's name, the arguments provided to it, or the current
approval mode.
- **Decision**: The action to take if the rule matches (`allow`, `deny`, or
`ask_user`).
- **Priority**: A number that determines the rule's precedence. Higher numbers
win.
For example, this rule will ask for user confirmation before executing any `git`
command.
```toml
[[rule]]
toolName = "run_shell_command"
commandPrefix = "git "
decision = "ask_user"
priority = 100
```
### Conditions
Conditions are the criteria that a tool call must meet for a rule to apply. The
primary conditions are the tool's name and its arguments.
#### Tool Name
The `toolName` in the rule must match the name of the tool being called.
- **Wildcards**: For Model-hosting-protocol (MCP) servers, you can use a
wildcard. A `toolName` of `my-server__*` will match any tool from the
`my-server` MCP.
#### Arguments Pattern
If `argsPattern` is specified, the tool's arguments are converted to a stable
JSON string, which is then tested against the provided regular expression. If
the arguments don't match the pattern, the rule does not apply.
### Decisions
There are three possible decisions a rule can enforce:
- `allow`: The tool call is executed automatically without user interaction.
- `deny`: The tool call is blocked and is not executed.
- `ask_user`: The user is prompted to approve or deny the tool call. (In
non-interactive mode, this is treated as `deny`.)
### Priority system & tiers
The policy engine uses a sophisticated priority system to resolve conflicts when
multiple rules match a single tool call. The core principle is simple: **the
rule with the highest priority wins**.
To provide a clear hierarchy, policies are organized into three tiers. Each tier
has a designated number that forms the base of the final priority calculation.
| Tier | Base | Description |
| :------ | :--- | :------------------------------------------------------------------------- |
| Default | 1 | Built-in policies that ship with the Gemini CLI. |
| User | 2 | Custom policies defined by the user. |
| Admin | 3 | Policies managed by an administrator (e.g., in an enterprise environment). |
Within a TOML policy file, you assign a priority value from **0 to 999**. The
engine transforms this into a final priority using the following formula:
`final_priority = tier_base + (toml_priority / 1000)`
This system guarantees that:
- Admin policies always override User and Default policies.
- User policies always override Default policies.
- You can still order rules within a single tier with fine-grained control.
For example:
- A `priority: 50` rule in a Default policy file becomes `1.050`.
- A `priority: 100` rule in a User policy file becomes `2.100`.
- A `priority: 20` rule in an Admin policy file becomes `3.020`.
### Approval modes
Approval modes allow the policy engine to apply different sets of rules based on
the CLI's operational mode. A rule can be associated with one or more modes
(e.g., `yolo`, `autoEdit`). The rule will only be active if the CLI is running
in one of its specified modes. If a rule has no modes specified, it is always
active.
## Rule matching
When a tool call is made, the engine checks it against all active rules,
starting from the highest priority. The first rule that matches determines the
outcome.
A rule matches a tool call if all of its conditions are met:
1. **Tool Name**: The `toolName` in the rule must match the name of the tool
being called.
- **Wildcards**: For Model-hosting-protocol (MCP) servers, you can use a
wildcard. A `toolName` of `my-server__*` will match any tool from the
`my-server` MCP.
2. **Arguments Pattern**: If `argsPattern` is specified, the tool's arguments
are converted to a stable JSON string, which is then tested against the
provided regular expression. If the arguments don't match the pattern, the
rule does not apply.
## Configuration
Policies are defined in `.toml` files. The CLI loads these files from Default,
User, and (if configured) Admin directories.
### TOML rule schema
Here is a breakdown of the fields available in a TOML policy rule:
```toml
[[rule]]
# A unique name for the tool, or an array of names.
toolName = "run_shell_command"
# (Optional) The name of an MCP server. Can be combined with toolName
# to form a composite name like "mcpName__toolName".
mcpName = "my-custom-server"
# (Optional) A regex to match against the tool's arguments.
argsPattern = '"command":"(git|npm)'
# (Optional) A string or array of strings that a shell command must start with.
# This is syntactic sugar for `toolName = "run_shell_command"` and an `argsPattern`.
commandPrefix = "git "
# (Optional) A regex to match against the entire shell command.
# This is also syntactic sugar for `toolName = "run_shell_command"`.
# Note: This pattern is tested against the JSON representation of the arguments (e.g., `{"command":"<your_command>"}`), so anchors like `^` or `$` will apply to the full JSON string, not just the command text.
# You cannot use commandPrefix and commandRegex in the same rule.
commandRegex = "^git (commit|push)"
# The decision to take. Must be "allow", "deny", or "ask_user".
decision = "ask_user"
# The priority of the rule, from 0 to 999.
priority = 10
# (Optional) An array of approval modes where this rule is active.
modes = ["autoEdit"]
```
### Using arrays (lists)
To apply the same rule to multiple tools or command prefixes, you can provide an
array of strings for the `toolName` and `commandPrefix` fields.
**Example:**
This single rule will apply to both the `write_file` and `replace` tools.
```toml
[[rule]]
toolName = ["write_file", "replace"]
decision = "ask_user"
priority = 10
```
### Special syntax for `run_shell_command`
To simplify writing policies for `run_shell_command`, you can use
`commandPrefix` or `commandRegex` instead of the more complex `argsPattern`.
- `commandPrefix`: Matches if the `command` argument starts with the given
string.
- `commandRegex`: Matches if the `command` argument matches the given regular
expression.
**Example:**
This rule will ask for user confirmation before executing any `git` command.
```toml
[[rule]]
toolName = "run_shell_command"
commandPrefix = "git "
decision = "ask_user"
priority = 100
```
### Special syntax for MCP tools
You can create rules that target tools from Model-hosting-protocol (MCP) servers
using the `mcpName` field or a wildcard pattern.
**1. Using `mcpName`**
To target a specific tool from a specific server, combine `mcpName` and
`toolName`.
```toml
# Allows the `search` tool on the `my-jira-server` MCP
[[rule]]
mcpName = "my-jira-server"
toolName = "search"
decision = "allow"
priority = 200
```
**2. Using a Wildcard**
To create a rule that applies to _all_ tools on a specific MCP server, specify
only the `mcpName`.
```toml
# Denies all tools from the `untrusted-server` MCP
[[rule]]
mcpName = "untrusted-server"
decision = "deny"
priority = 500
```
## Default policies
The Gemini CLI ships with a set of default policies to provide a safe
out-of-the-box experience.
- **Read-only tools** (like `read_file`, `glob`) are generally **allowed**.
- **Write tools** (like `write_file`, `run_shell_command`) default to
**`ask_user`**.
- In **`yolo`** mode, a high-priority rule allows all tools.
- In **`autoEdit`** mode, rules allow certain write operations to happen without
prompting.
+1 -2
View File
@@ -62,8 +62,7 @@ The core comes with a suite of pre-defined tools, typically found in
- **File System Tools:**
- `LSTool` (`ls.ts`): Lists directory contents.
- `ReadFileTool` (`read-file.ts`): Reads the content of a single file. It
takes an `absolute_path` parameter, which must be an absolute path.
- `ReadFileTool` (`read-file.ts`): Reads the content of a single file.
- `WriteFileTool` (`write-file.ts`): Writes content to a file.
- `GrepTool` (`grep.ts`): Searches for patterns in files.
- `GlobTool` (`glob.ts`): Finds files matching glob patterns.
+3 -5
View File
@@ -15,11 +15,9 @@ if you need to ship platform specific binary files.
This is the most flexible and simple option. All you need to do is create a
publicly accessible git repo (such as a public github repository) and then users
can install your extension using `gemini extensions install <your-repo-uri>`, or
for a GitHub repository they can use the simplified
`gemini extensions install <org>/<repo>` format. They can optionally depend on a
specific ref (branch/tag/commit) using the `--ref=<some-ref>` argument, this
defaults to the default branch.
can install your extension using `gemini extensions install <your-repo-uri>`.
They can optionally depend on a specific ref (branch/tag/commit) using the
`--ref=<some-ref>` argument, this defaults to the default branch.
Whenever commits are pushed to the ref that a user depends on, they will be
prompted to update the extension. Note that this also allows for easy rollbacks,
+68 -23
View File
@@ -42,16 +42,19 @@ installed on your machine. See
for help.
```
gemini extensions install https://github.com/gemini-cli-extensions/security
gemini extensions install <source> [--ref <ref>] [--auto-update] [--pre-release] [--consent]
```
This will install the Gemini CLI Security extension, which offers support for a
`/security:analyze` command.
- `<source>`: The github URL or local path of the extension to install.
- `--ref`: The git ref to install from.
- `--auto-update`: Enable auto-update for this extension.
- `--pre-release`: Enable pre-release versions for this extension.
- `--consent`: Acknowledge the security risks of installing an extension and
skip the confirmation prompt.
### Uninstalling an extension
To uninstall, run `gemini extensions uninstall extension-name`, so, in the case
of the install example:
To uninstall, run `gemini extensions uninstall <name>`:
```
gemini extensions uninstall gemini-cli-security
@@ -62,27 +65,31 @@ gemini extensions uninstall gemini-cli-security
Extensions are, by default, enabled across all workspaces. You can disable an
extension entirely or for specific workspace.
For example, `gemini extensions disable extension-name` will disable the
extension at the user level, so it will be disabled everywhere.
`gemini extensions disable extension-name --scope=workspace` will only disable
the extension in the current workspace.
```
gemini extensions disable <name> [--scope <scope>]
```
- `<name>`: The name of the extension to disable.
- `--scope`: The scope to disable the extension in (`user` or `workspace`).
### Enabling an extension
You can enable extensions using `gemini extensions enable extension-name`. You
can also enable an extension for a specific workspace using
`gemini extensions enable extension-name --scope=workspace` from within that
workspace.
You can enable extensions using `gemini extensions enable <name>`. You can also
enable an extension for a specific workspace using
`gemini extensions enable <name> --scope=workspace` from within that workspace.
This is useful if you have an extension disabled at the top-level and only
enabled in specific places.
```
gemini extensions enable <name> [--scope <scope>]
```
- `<name>`: The name of the extension to enable.
- `--scope`: The scope to enable the extension in (`user` or `workspace`).
### Updating an extension
For extensions installed from a local path or a git repository, you can
explicitly update to the latest version (as reflected in the
`gemini-extension.json` `version` field) with
`gemini extensions update extension-name`.
`gemini-extension.json` `version` field) with `gemini extensions update <name>`.
You can update all extensions with:
@@ -90,10 +97,6 @@ You can update all extensions with:
gemini extensions update --all
```
## Extension creation
We offer commands to make extension development easier.
### Create a boilerplate extension
We offer several example extensions `context`, `custom-commands`,
@@ -104,9 +107,12 @@ To copy one of these examples into a development directory using the type of
your choosing, run:
```
gemini extensions new path/to/directory custom-commands
gemini extensions new <path> [template]
```
- `<path>`: The path to create the extension in.
- `[template]`: The boilerplate template to use.
### Link a local extension
The `gemini extensions link` command will create a symbolic link from the
@@ -116,9 +122,11 @@ This is useful so you don't have to run `gemini extensions update` every time
you make changes you'd like to test.
```
gemini extensions link path/to/directory
gemini extensions link <path>
```
- `<path>`: The path of the extension to link.
## How it works
On startup, Gemini CLI looks for extensions in `<home>/.gemini/extensions`
@@ -177,6 +185,43 @@ When Gemini CLI starts, it loads all the extensions and merges their
configurations. If there are any conflicts, the workspace configuration takes
precedence.
### Settings
_Note: This is an experimental feature. We do not yet recommend extension
authors introduce settings as part of their core flows._
Extensions can define settings that the user will be prompted to provide upon
installation. This is useful for things like API keys, URLs, or other
configuration that the extension needs to function.
To define settings, add a `settings` array to your `gemini-extension.json` file.
Each object in the array should have the following properties:
- `name`: A user-friendly name for the setting.
- `description`: A description of the setting and what it's used for.
- `envVar`: The name of the environment variable that the setting will be stored
as.
- `sensitive`: Optional boolean. If true, obfuscates the input the user provides
and stores the secret in keychain storage. **Example**
```json
{
"name": "my-api-extension",
"version": "1.0.0",
"settings": [
{
"name": "API Key",
"description": "Your API key for the service.",
"envVar": "MY_API_KEY"
}
]
}
```
When a user installs this extension, they will be prompted to enter their API
key. The value will be saved to a `.env` file in the extension's directory
(e.g., `<home>/.gemini/extensions/my-api-extension/.env`).
### Custom commands
Extensions can provide [custom commands](../cli/custom-commands.md) by placing
+11 -1
View File
@@ -8,13 +8,23 @@ CLI, configure **one** of the following authentication methods:
- Use Gemini API key
- Use Vertex AI
- Headless (non-interactive) mode
- Google Cloud Shell
- Google Cloud Environments (Cloud Shell, Compute Engine, etc.)
## Quick Check: Running in Google Cloud Shell?
If you are running the Gemini CLI within a Google Cloud Shell environment,
authentication is typically automatic using your Cloud Shell credentials.
### Other Google Cloud Environments (e.g., Compute Engine)
Some other Google Cloud environments, such as Compute Engine VMs, might also
support automatic authentication. In these environments, Gemini CLI can
automatically use Application Default Credentials (ADC) sourced from the
environment's metadata server.
If automatic authentication does not occur in your environment, you will need to
use one of the interactive methods described below.
## Authenticate in Interactive mode
When you run Gemini CLI through the command-line, Gemini CLI will provide the
-17
View File
@@ -473,21 +473,6 @@ a few things you can try in order of recommendation:
"loadMemoryFromIncludeDirectories": true
```
- **`chatCompression`** (object):
- **Description:** Controls the settings for chat history compression, both
automatic and when manually invoked through the /compress command.
- **Properties:**
- **`contextPercentageThreshold`** (number): A value between 0 and 1 that
specifies the token threshold for compression as a percentage of the
model's total token limit. For example, a value of `0.6` will trigger
compression when the chat history exceeds 60% of the token limit.
- **Example:**
```json
"chatCompression": {
"contextPercentageThreshold": 0.6
}
```
- **`showLineNumbers`** (boolean):
- **Description:** Controls whether line numbers are displayed in code blocks
in the CLI output.
@@ -707,8 +692,6 @@ for that specific session.
- **`--telemetry-log-prompts`**:
- Enables logging of prompts for telemetry. See
[telemetry](../cli/telemetry.md) for more information.
- **`--checkpointing`**:
- Enables [checkpointing](../cli/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.
+270 -125
View File
@@ -36,6 +36,11 @@ overridden by higher numbers):
Gemini CLI uses JSON settings files for persistent configuration. There are four
locations for these files:
> **Tip:** JSON-aware editors can use autocomplete and validation by pointing to
> the generated schema at `schemas/settings.schema.json` in this repository.
> When working outside the repo, reference the hosted schema at
> `https://raw.githubusercontent.com/google-gemini/gemini-cli/main/schemas/settings.schema.json`.
- **System defaults file:**
- **Location:** `/etc/gemini-cli/system-defaults.json` (Linux),
`C:\ProgramData\gemini-cli\system-defaults.json` (Windows) or
@@ -65,11 +70,12 @@ locations for these files:
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"`.
`settings.json` and `gemini-extension.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"`. Additionally, each extension can have its own
`.env` file in its directory, which will be loaded automatically.
> **Note for Enterprise Users:** For guidance on deploying and managing Gemini
> CLI in a corporate environment, please see the
@@ -88,18 +94,24 @@ contain other project-specific files related to Gemini CLI's operation, such as:
Settings are organized into categories. All settings should be placed within
their corresponding top-level category object in your `settings.json` file.
<!-- SETTINGS-AUTOGEN:START -->
#### `general`
- **`general.previewFeatures`** (boolean):
- **Description:** Enable preview features (e.g., preview models).
- **Default:** `false`
- **`general.preferredEditor`** (string):
- **Description:** The preferred editor to open files in.
- **Default:** `undefined`
- **`general.vimMode`** (boolean):
- **Description:** Enable Vim keybindings.
- **Description:** Enable Vim keybindings
- **Default:** `false`
- **`general.disableAutoUpdate`** (boolean):
- **Description:** Disable automatic updates.
- **Description:** Disable automatic updates
- **Default:** `false`
- **`general.disableUpdateNag`** (boolean):
@@ -107,34 +119,55 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `false`
- **`general.checkpointing.enabled`** (boolean):
- **Description:** Enable session checkpointing for recovery.
- **Description:** Enable session checkpointing for recovery
- **Default:** `false`
- **Requires restart:** Yes
- **`general.enablePromptCompletion`** (boolean):
- **Description:** Enable AI-powered prompt completion suggestions while
typing.
- **Default:** `false`
- **Requires restart:** Yes
- **`general.retryFetchErrors`** (boolean):
- **Description:** Retry on "exception TypeError: fetch failed sending
request" errors.
- **Default:** `false`
- **`general.debugKeystrokeLogging`** (boolean):
- **Description:** Enable debug logging of keystrokes to the console.
- **Default:** `false`
- **`general.sessionRetention.enabled`** (boolean):
- **Description:** Enable automatic session cleanup.
- **Description:** Enable automatic session cleanup
- **Default:** `false`
- **`general.sessionRetention.maxAge`** (string):
- **Description:** Maximum age of sessions to keep (e.g., "30d", "7d", "24h",
"1w")
- **Default:** `undefined`
- **`general.sessionRetention.maxCount`** (number):
- **Description:** Alternative: Maximum number of sessions to keep (most
recent)
- **Default:** `undefined`
- **`general.sessionRetention.minRetention`** (string):
- **Description:** Minimum retention period (safety limit, defaults to "1d")
- **Default:** `"1d"`
#### `output`
- **`output.format`** (string):
- **`output.format`** (enum):
- **Description:** The format of the CLI output.
- **Default:** `"text"`
- **Values:** `"text"`, `"json"`, `"stream-json"`
- **Values:** `"text"`, `"json"`
#### `ui`
- **`ui.theme`** (string):
- **Description:** The color theme for the UI. See [Themes](../cli/themes.md)
for available options.
- **Description:** The color theme for the UI. See the CLI themes guide for
available options.
- **Default:** `undefined`
- **`ui.customThemes`** (object):
@@ -142,20 +175,21 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `{}`
- **`ui.hideWindowTitle`** (boolean):
- **Description:** Hide the window title bar.
- **Description:** Hide the window title bar
- **Default:** `false`
- **Requires restart:** Yes
- **`ui.showStatusInTitle`** (boolean):
- **Description:** Show Gemini CLI status and thoughts in the terminal window
title.
title
- **Default:** `false`
- **`ui.hideTips`** (boolean):
- **Description:** Hide helpful tips in the UI.
- **Description:** Hide helpful tips in the UI
- **Default:** `false`
- **`ui.hideBanner`** (boolean):
- **Description:** Hide the application banner.
- **Description:** Hide the application banner
- **Default:** `false`
- **`ui.hideContextSummary`** (boolean):
@@ -163,10 +197,6 @@ their corresponding top-level category object in your `settings.json` file.
input.
- **Default:** `false`
- **`ui.hideFooter`** (boolean):
- **Description:** Hide the footer from the UI.
- **Default:** `false`
- **`ui.footer.hideCWD`** (boolean):
- **Description:** Hide the current working directory path in the footer.
- **Default:** `false`
@@ -179,42 +209,69 @@ their corresponding top-level category object in your `settings.json` file.
- **Description:** Hide the model name and context usage in the footer.
- **Default:** `false`
- **`ui.footer.hideContextPercentage`** (boolean):
- **Description:** Hides the context window remaining percentage.
- **Default:** `true`
- **`ui.hideFooter`** (boolean):
- **Description:** Hide the footer from the UI
- **Default:** `false`
- **`ui.showMemoryUsage`** (boolean):
- **Description:** Display memory usage information in the UI.
- **Description:** Display memory usage information in the UI
- **Default:** `false`
- **`ui.showLineNumbers`** (boolean):
- **Description:** Show line numbers in the chat.
- **Default:** `false`
- **Default:** `true`
- **`ui.showCitations`** (boolean):
- **Description:** Show citations for generated text in the chat.
- **Default:** `true`
- **Default:** `false`
- **`ui.showModelInfoInChat`** (boolean):
- **Description:** Show the model name in the chat for each model turn.
- **Default:** `false`
- **`ui.useFullWidth`** (boolean):
- **Description:** Use the entire width of the terminal for output.
- **Default:** `true`
- **`ui.useAlternateBuffer`** (boolean):
- **Description:** Use an alternate screen buffer for the UI, preserving shell
history.
- **Default:** `false`
- **Requires restart:** Yes
- **`ui.incrementalRendering`** (boolean):
- **Description:** Enable incremental rendering for the UI. This option will
reduce flickering but may cause rendering artifacts. Only supported when
useAlternateBuffer is enabled.
- **Default:** `true`
- **Requires restart:** Yes
- **`ui.customWittyPhrases`** (array):
- **Description:** Custom witty phrases to display during loading. When
provided, the CLI cycles through these instead of the defaults.
- **Default:** `[]`
- **`ui.accessibility.disableLoadingPhrases`** (boolean):
- **Description:** Disable loading phrases for accessibility.
- **Description:** Disable loading phrases for accessibility
- **Default:** `false`
- **Requires restart:** Yes
- **`ui.accessibility.screenReader`** (boolean):
- **Description:** Show plaintext interactive view that is more screen reader
friendly.
- **Description:** Render output in plain-text to be more screen reader
accessible
- **Default:** `false`
- **`ui.customWittyPhrases`** (array of strings):
- **Description:** A list of custom phrases to display during loading states.
When provided, the CLI will cycle through these phrases instead of the
default ones.
- **Default:** `[]`
- **Requires restart:** Yes
#### `ide`
- **`ide.enabled`** (boolean):
- **Description:** Enable IDE integration mode.
- **Description:** Enable IDE integration mode
- **Default:** `false`
- **Requires restart:** Yes
- **`ide.hasSeenNudge`** (boolean):
- **Description:** Whether the user has seen the IDE integration nudge.
@@ -223,8 +280,9 @@ their corresponding top-level category object in your `settings.json` file.
#### `privacy`
- **`privacy.usageStatisticsEnabled`** (boolean):
- **Description:** Enable collection of usage statistics.
- **Description:** Enable collection of usage statistics
- **Default:** `true`
- **Requires restart:** Yes
#### `model`
@@ -238,32 +296,40 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `-1`
- **`model.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. For
example `{"run_shell_command": {"tokenBudget": 2000}}`
- **Description:** Enables or disables summarization of tool output. Configure
per-tool token budgets (for example {"run_shell_command": {"tokenBudget":
2000}}). Currently only the run_shell_command tool supports summarization.
- **Default:** `undefined`
- **`model.chatCompression.contextPercentageThreshold`** (number):
- **Description:** Sets the threshold for chat history compression as a
percentage of the model's total token limit. This is a value between 0 and 1
that applies to both automatic compression and the manual `/compress`
command. For example, a value of `0.6` will trigger compression when the
chat history exceeds 60% of the token limit.
- **Default:** `0.7`
- **`model.compressionThreshold`** (number):
- **Description:** The fraction of context usage at which to trigger context
compression (e.g. 0.2, 0.3).
- **Default:** `0.5`
- **Requires restart:** Yes
- **`model.skipNextSpeakerCheck`** (boolean):
- **Description:** Skip the next speaker check.
- **Default:** `false`
- **`model.enableShellOutputEfficiency`** (boolean):
- **Description:** Optimizes shell tool commands for token efficiency.
- **Default:** `true`
#### `modelConfigs`
- **`modelConfigs.aliases`** (object):
- **Description:** Named presets for model configs. Can be used in place of a
model name and can inherit from other aliases using an `extends` property.
- **Default:**
`{"base":{"modelConfig":{"generateContentConfig":{"temperature":0,"topP":1}}},"chat-base":{"extends":"base","modelConfig":{"generateContentConfig":{"thinkingConfig":{"includeThoughts":true},"temperature":1,"topP":0.95,"topK":64}}},"chat-base-2.5":{"extends":"chat-base","modelConfig":{"generateContentConfig":{"thinkingConfig":{"thinkingBudget":8192}}}},"chat-base-3":{"extends":"chat-base","modelConfig":{"generateContentConfig":{"thinkingConfig":{"thinkingLevel":"HIGH"}}}},"gemini-3-pro-preview":{"extends":"chat-base-3","modelConfig":{"model":"gemini-3-pro-preview"}},"gemini-2.5-pro":{"extends":"chat-base-2.5","modelConfig":{"model":"gemini-2.5-pro"}},"gemini-2.5-flash":{"extends":"chat-base-2.5","modelConfig":{"model":"gemini-2.5-flash"}},"gemini-2.5-flash-lite":{"extends":"chat-base-2.5","modelConfig":{"model":"gemini-2.5-flash-lite"}},"gemini-2.5-flash-base":{"extends":"base","modelConfig":{"model":"gemini-2.5-flash"}},"classifier":{"extends":"base","modelConfig":{"model":"gemini-2.5-flash-lite","generateContentConfig":{"maxOutputTokens":1024,"thinkingConfig":{"thinkingBudget":512}}}},"prompt-completion":{"extends":"base","modelConfig":{"model":"gemini-2.5-flash-lite","generateContentConfig":{"temperature":0.3,"maxOutputTokens":16000,"thinkingConfig":{"thinkingBudget":0}}}},"edit-corrector":{"extends":"base","modelConfig":{"model":"gemini-2.5-flash-lite","generateContentConfig":{"thinkingConfig":{"thinkingBudget":0}}}},"summarizer-default":{"extends":"base","modelConfig":{"model":"gemini-2.5-flash-lite","generateContentConfig":{"maxOutputTokens":2000}}},"summarizer-shell":{"extends":"base","modelConfig":{"model":"gemini-2.5-flash-lite","generateContentConfig":{"maxOutputTokens":2000}}},"web-search":{"extends":"gemini-2.5-flash-base","modelConfig":{"generateContentConfig":{"tools":[{"googleSearch":{}}]}}},"web-fetch":{"extends":"gemini-2.5-flash-base","modelConfig":{"generateContentConfig":{"tools":[{"urlContext":{}}]}}},"web-fetch-fallback":{"extends":"gemini-2.5-flash-base","modelConfig":{}},"loop-detection":{"extends":"gemini-2.5-flash-base","modelConfig":{}},"loop-detection-double-check":{"extends":"base","modelConfig":{"model":"gemini-2.5-pro"}},"llm-edit-fixer":{"extends":"gemini-2.5-flash-base","modelConfig":{}},"next-speaker-checker":{"extends":"gemini-2.5-flash-base","modelConfig":{}}}`
- **`modelConfigs.overrides`** (array):
- **Description:** Apply specific configuration overrides based on matches,
with a primary key of model (or alias). The most specific match will be
used.
- **Default:** `[]`
#### `context`
- **`context.fileName`** (string or array of strings):
- **Description:** The name of the context file(s).
- **`context.fileName`** (string | string[]):
- **Description:** The name of the context file or files to load into memory.
Accepts either a single string or an array of strings.
- **Default:** `undefined`
- **`context.importFormat`** (string):
@@ -279,42 +345,51 @@ their corresponding top-level category object in your `settings.json` file.
Missing directories will be skipped with a warning.
- **Default:** `[]`
- **`context.loadFromIncludeDirectories`** (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.
- **`context.loadMemoryFromIncludeDirectories`** (boolean):
- **Description:** Controls how /memory refresh loads GEMINI.md files. When
true, include directories are scanned; when false, only the current
directory is used.
- **Default:** `false`
- **`context.fileFiltering.respectGitIgnore`** (boolean):
- **Description:** Respect .gitignore files when searching.
- **Description:** Respect .gitignore files when searching
- **Default:** `true`
- **Requires restart:** Yes
- **`context.fileFiltering.respectGeminiIgnore`** (boolean):
- **Description:** Respect .geminiignore files when searching.
- **Description:** Respect .geminiignore files when searching
- **Default:** `true`
- **Requires restart:** Yes
- **`context.fileFiltering.enableRecursiveFileSearch`** (boolean):
- **Description:** Whether to enable searching recursively for filenames under
the current tree when completing `@` prefixes in the prompt.
- **Description:** Enable recursive file search functionality when completing
@ references in the prompt.
- **Default:** `true`
- **Requires restart:** Yes
- **`context.fileFiltering.disableFuzzySearch`** (boolean):
- **Description:** Disable fuzzy search when searching for files.
- **Default:** `false`
- **Requires restart:** Yes
#### `tools`
- **`tools.sandbox`** (boolean or string):
- **Description:** Sandbox execution environment (can be a boolean or a path
string).
- **`tools.sandbox`** (boolean | string):
- **Description:** Sandbox execution environment. Set to a boolean to enable
or disable the sandbox, or provide a string path to a sandbox profile.
- **Default:** `undefined`
- **Requires restart:** Yes
- **`tools.shell.enableInteractiveShell`** (boolean):
- **Description:** Enables interactive terminal for running shell commands. If
an interactive session cannot be started, it will fall back to a standard
shell.
- **Description:** Use node-pty for an interactive shell experience. Fallback
to child_process still applies.
- **Default:** `true`
- **Requires restart:** Yes
- **`tools.shell.pager`** (string):
- **Description:** The pager command to use for shell output. Defaults to
`cat`.
- **Default:** `"cat"`
- **`tools.shell.showColor`** (boolean):
- **Description:** Show color in shell output.
@@ -325,42 +400,37 @@ their corresponding top-level category object in your `settings.json` file.
considered safe (e.g., read-only operations).
- **Default:** `false`
- **`tools.core`** (array of strings):
- **Description:** This can be used to restrict the set of built-in tools
[with an allowlist](../cli/enterprise.md#restricting-tool-access). See
[Built-in Tools](../core/tools-api.md#built-in-tools) for a list of core
tools. The match semantics are the same as `tools.allowed`.
- **`tools.core`** (array):
- **Description:** Restrict the set of built-in tools with an allowlist. Match
semantics mirror tools.allowed; see the built-in tools documentation for
available names.
- **Default:** `undefined`
- **Requires restart:** Yes
- **`tools.exclude`** (array of strings):
- **`tools.allowed`** (array):
- **Description:** Tool names that bypass the confirmation dialog. Useful for
trusted commands (for example ["run_shell_command(git)",
"run_shell_command(npm test)"]). See shell tool command restrictions for
matching details.
- **Default:** `undefined`
- **Requires restart:** Yes
- **`tools.exclude`** (array):
- **Description:** Tool names to exclude from discovery.
- **Default:** `undefined`
- **`tools.allowed`** (array of strings):
- **Description:** A list of tool names that will bypass the confirmation
dialog. This is useful for tools that you trust and use frequently. For
example, `["run_shell_command(git)", "run_shell_command(npm test)"]` will
skip the confirmation dialog to run any `git` and `npm test` commands. See
[Shell Tool command restrictions](../tools/shell.md#command-restrictions)
for details on prefix matching, command chaining, etc.
- **Default:** `undefined`
- **Requires restart:** Yes
- **`tools.discoveryCommand`** (string):
- **Description:** Command to run for tool discovery.
- **Default:** `undefined`
- **Requires restart:** Yes
- **`tools.callCommand`** (string):
- **Description:** Defines a custom shell command for calling a specific tool
that was discovered using `tools.discoveryCommand`. 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 the 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).
- It must return function output as JSON on `stdout`, analogous to
[`functionResponse.response.content`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/inference#functionresponse).
- **Description:** Defines a custom shell command for invoking discovered
tools. The command must take the tool name as the first argument, read JSON
arguments from stdin, and emit JSON results on stdout.
- **Default:** `undefined`
- **Requires restart:** Yes
- **`tools.useRipgrep`** (boolean):
- **Description:** Use ripgrep for file content search instead of the fallback
@@ -370,66 +440,110 @@ their corresponding top-level category object in your `settings.json` file.
- **`tools.enableToolOutputTruncation`** (boolean):
- **Description:** Enable truncation of large tool outputs.
- **Default:** `true`
- **Requires restart:** Yes
- **`tools.truncateToolOutputThreshold`** (number):
- **Description:** Truncate tool output if it is larger than this many
characters. Set to -1 to disable.
- **Default:** `20000`
- **Default:** `4000000`
- **Requires restart:** Yes
- **`tools.truncateToolOutputLines`** (number):
- **Description:** The number of lines to keep when truncating tool output.
- **Default:** `1000`
- **Requires restart:** Yes
- **`tools.enableMessageBusIntegration`** (boolean):
- **Description:** Enable policy-based tool confirmation via message bus
integration. When enabled, tools will automatically respect policy engine
integration. When enabled, tools automatically respect policy engine
decisions (ALLOW/DENY/ASK_USER) without requiring individual tool
implementations.
- **Default:** `false`
- **Requires restart:** Yes
- **`tools.enableHooks`** (boolean):
- **Description:** Enable the hooks system for intercepting and customizing
Gemini CLI behavior. When enabled, hooks configured in settings will execute
at appropriate lifecycle events (BeforeTool, AfterTool, BeforeModel, etc.).
Requires MessageBus integration.
- **Default:** `false`
- **Requires restart:** Yes
#### `mcp`
- **`mcp.serverCommand`** (string):
- **Description:** Command to start an MCP server.
- **Default:** `undefined`
- **Requires restart:** Yes
- **`mcp.allowed`** (array of strings):
- **Description:** An allowlist of MCP servers to allow.
- **`mcp.allowed`** (array):
- **Description:** A list of MCP servers to allow.
- **Default:** `undefined`
- **Requires restart:** Yes
- **`mcp.excluded`** (array of strings):
- **Description:** A denylist of MCP servers to exclude.
- **`mcp.excluded`** (array):
- **Description:** A list of MCP servers to exclude.
- **Default:** `undefined`
- **Requires restart:** Yes
#### `useSmartEdit`
- **`useSmartEdit`** (boolean):
- **Description:** Enable the smart-edit tool instead of the replace tool.
- **Default:** `true`
#### `useWriteTodos`
- **`useWriteTodos`** (boolean):
- **Description:** Enable the write_todos tool.
- **Default:** `true`
#### `security`
- **`security.disableYoloMode`** (boolean):
- **Description:** Disable YOLO mode, even if enabled by a flag.
- **Default:** `false`
- **Requires restart:** Yes
- **`security.blockGitExtensions`** (boolean):
- **Description:** Blocks installing and loading extensions from Git.
- **Default:** `false`
- **Requires restart:** Yes
- **`security.folderTrust.enabled`** (boolean):
- **Description:** Setting to track whether Folder trust is enabled.
- **Default:** `false`
- **Requires restart:** Yes
- **`security.auth.selectedType`** (string):
- **Description:** The currently selected authentication type.
- **Default:** `undefined`
- **Requires restart:** Yes
- **`security.auth.enforcedType`** (string):
- **Description:** The required auth type (useful for enterprises).
- **Description:** The required auth type. If this does not match the selected
auth type, the user will be prompted to re-authenticate.
- **Default:** `undefined`
- **Requires restart:** Yes
- **`security.auth.useExternal`** (boolean):
- **Description:** Whether to use an external authentication flow.
- **Default:** `undefined`
- **Requires restart:** Yes
#### `advanced`
- **`advanced.autoConfigureMemory`** (boolean):
- **Description:** Automatically configure Node.js memory limits.
- **Description:** Automatically configure Node.js memory limits
- **Default:** `false`
- **Requires restart:** Yes
- **`advanced.dnsResolutionOrder`** (string):
- **Description:** The DNS resolution order.
- **Default:** `undefined`
- **Requires restart:** Yes
- **`advanced.excludedEnvVars`** (array of strings):
- **`advanced.excludedEnvVars`** (array):
- **Description:** Environment variables to exclude from project context.
- **Default:** `["DEBUG","DEBUG_MODE"]`
@@ -439,10 +553,56 @@ their corresponding top-level category object in your `settings.json` file.
#### `experimental`
- **`experimental.extensionManagement`** (boolean):
- **Description:** Enable extension management features.
- **Default:** `true`
- **Requires restart:** Yes
- **`experimental.extensionReloading`** (boolean):
- **Description:** Enables extension loading/unloading within the CLI session.
- **Default:** `false`
- **Requires restart:** Yes
- **`experimental.useModelRouter`** (boolean):
- **Description:** Enable model routing to route requests to the best model
based on complexity.
- **Default:** `false`
- **Default:** `true`
- **Requires restart:** Yes
- **`experimental.codebaseInvestigatorSettings.enabled`** (boolean):
- **Description:** Enable the Codebase Investigator agent.
- **Default:** `true`
- **Requires restart:** Yes
- **`experimental.codebaseInvestigatorSettings.maxNumTurns`** (number):
- **Description:** Maximum number of turns for the Codebase Investigator
agent.
- **Default:** `10`
- **Requires restart:** Yes
- **`experimental.codebaseInvestigatorSettings.maxTimeMinutes`** (number):
- **Description:** Maximum time for the Codebase Investigator agent (in
minutes).
- **Default:** `3`
- **Requires restart:** Yes
- **`experimental.codebaseInvestigatorSettings.thinkingBudget`** (number):
- **Description:** The thinking budget for the Codebase Investigator agent.
- **Default:** `8192`
- **Requires restart:** Yes
- **`experimental.codebaseInvestigatorSettings.model`** (string):
- **Description:** The model to use for the Codebase Investigator agent.
- **Default:** `"gemini-2.5-pro"`
- **Requires restart:** Yes
#### `hooks`
- **`hooks`** (object):
- **Description:** Hook configurations for intercepting and customizing agent
behavior.
- **Default:** `{}`
<!-- SETTINGS-AUTOGEN:END -->
#### `mcpServers`
@@ -723,15 +883,11 @@ for that specific session.
`--output-format json` or `--output-format stream-json` flag.
- **`--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.
- **`--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.
- **`--approval-mode <mode>`**:
@@ -747,22 +903,6 @@ for that specific session.
- A comma-separated list of tool names that will bypass the confirmation
dialog.
- Example: `gemini --allowed-tools "ShellTool(git status)"`
- **`--telemetry`**:
- Enables [telemetry](../cli/telemetry.md).
- **`--telemetry-target`**:
- Sets the telemetry target. See [telemetry](../cli/telemetry.md) for more
information.
- **`--telemetry-otlp-endpoint`**:
- Sets the OTLP endpoint for telemetry. See [telemetry](../cli/telemetry.md)
for more information.
- **`--telemetry-otlp-protocol`**:
- Sets the OTLP protocol for telemetry (`grpc` or `http`). Defaults to `grpc`.
See [telemetry](../cli/telemetry.md) for more information.
- **`--telemetry-log-prompts`**:
- Enables logging of prompts for telemetry. See
[telemetry](../cli/telemetry.md) for more information.
- **`--checkpointing`**:
- Enables [checkpointing](../cli/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.
@@ -770,9 +910,6 @@ for that specific session.
- 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.
@@ -785,6 +922,14 @@ for that specific session.
with screen readers.
- **`--version`**:
- Displays the version of the CLI.
- **`--experimental-acp`**:
- Starts the agent in ACP mode.
- **`--allowed-mcp-server-names`**:
- Allowed MCP server names.
- **`--fake-responses`**:
- Path to a file with fake model responses for testing.
- **`--record-responses`**:
- Path to a file to record model responses for testing.
## Context Files (Hierarchical Instructional Context)
+110
View File
@@ -0,0 +1,110 @@
# Gemini 3 Pro on Gemini CLI (Join the Waitlist)
Were excited to bring Gemini 3 Pro to Gemini CLI. For Google AI Ultra users
(Google AI Ultra for Business is not currently supported) and paid Gemini and
Vertex API key holders, Gemini 3 Pro is already available and ready to enable.
For everyone else, we're gradually expanding access
[through a waitlist](https://goo.gle/geminicli-waitlist-signup). Sign up for the
waitlist now to access Gemini 3 Pro once approved.
**Note:** Please wait until you have been approved to use Gemini 3 Pro to enable
**Preview Features**. If enabled early, the CLI will fallback to Gemini 2.5 Pro.
## Do I need to join the waitlist?
The following users will be **automatically granted access** to Gemini 3 Pro on
Gemini CLI:
- Google AI Ultra subscribers (excluding Google AI Ultra for Business, which is
on the roadmap).
- Gemini API key users
[with access to Gemini 3](https://ai.google.dev/gemini-api/docs/rate-limits).
- Vertex API key users
[with access to Gemini 3](https://docs.cloud.google.com/vertex-ai/generative-ai/docs/quotas).
For **Gemini Code Assist Enterprise users**, access is coming soon.
Users not automatically granted access through one of these account types will
need to join the waitlist. This includes Google AI Pro, Gemini Code Assist
standard, and free tier users.
Note: Whether youre automatically granted access or accepted from the waitlist,
youll still need to enable Gemini 3 Pro
[using the `/settings` command](../cli/settings.md).
## How to join the waitlist
Users not automatically granted access will need to join the waitlist. Follow
these instructions to sign up:
- Install Gemini CLI.
- Authenticate using the **Login with Google** option. Youll see a banner that
says “Gemini 3 is now available.” If you do not see this banner, update your
installation of Gemini CLI to the most recent version.
- Fill out this Google form:
[Access Gemini 3 in Gemini CLI](https://goo.gle/geminicli-waitlist-signup).
Provide the email address of the account you used to authenticate with Gemini
CLI.
Users will be onboarded in batches, subject to availability. When youve been
granted access to Gemini 3 Pro, youll receive an acceptance email to your
submitted email address.
## How to use Gemini 3 Pro with Gemini CLI
Once you receive your acceptance emailor if you are automatically granted
accessyou still need to enable Gemini 3 Pro within Gemini CLI.
To enable Gemini 3 Pro, use the `/settings` command in Gemini CLI and set
**Preview Features** to `true`.
For more information, see [Gemini CLI Settings](../cli/settings.md).
### Usage limits and fallback
Gemini CLI will tell you when you reach your Gemini 3 Pro daily usage limit.
When you encounter that limit, youll be given the option to switch to Gemini
2.5 Pro, upgrade for higher limits, or stop. Youll also be told when your usage
limit resets and Gemini 3 Pro can be used again.
Similarly, when you reach your daily usage limit for Gemini 2.5 Pro, youll see
a message prompting fallback to Gemini 2.5 Flash.
### Capacity errors
There may be times when the Gemini 3 Pro model is overloaded. When that happens,
Gemini CLI will ask you to decide whether you want to keep trying Gemini 3 Pro
or fallback to Gemini 2.5 Pro.
**Note:** The **Keep trying** option uses exponential backoff, in which Gemini
CLI waits longer between each retry, when the system is busy. If the retry
doesn't happen immediately, please wait a few minutes for the request to
process.
## Model selection & routing types
When using Gemini CLI, you may want to control how your requests are routed
between models. By default, Gemini CLI uses **Auto** routing.
When using Gemini 3 Pro, you may want to use Auto routing or Pro routing to
manage your usage limits:
- **Auto routing:** Auto routing first determines whether a prompt involves a
complex or simple operation. For simple prompts, it will automatically use
Gemini 2.5 Flash. For complex prompts, if Gemini 3 Pro is enabled, it will use
Gemini 3 Pro; otherwise, it will use Gemini 2.5 Pro.
- **Pro routing:** If you want to ensure your task is processed by the most
capable model, use `/model` and select **Pro**. Gemini CLI will prioritize the
most capable model available, including Gemini 3 Pro if it has been enabled.
To learn more about selecting a model and routing, refer to
[Gemini CLI Model Selection](../cli/model.md).
## Need help?
If you need help, we recommend searching for an existing
[GitHub issue](https://github.com/google-gemini/gemini-cli/issues). If you
cannot find a GitHub issue that matches your concern, you can
[create a new issue](https://github.com/google-gemini/gemini-cli/issues/new/choose).
For comments and feedback, consider opening a
[GitHub discussion](https://github.com/google-gemini/gemini-cli/discussions).
+1
View File
@@ -63,3 +63,4 @@ To explore the power of Gemini CLI, see [Gemini CLI examples](./examples.md).
- Find out more about [Gemini CLI's tools](../tools/index.md).
- Review [Gemini CLI's commands](../cli/commands.md).
- Learn how to [get started with Gemini 3](./gemini-3.md).
+3 -3
View File
@@ -4,8 +4,8 @@ Gemini CLI can integrate with your IDE to provide a more seamless and
context-aware experience. This integration allows the CLI to understand your
workspace better and enables powerful features like native in-editor diffing.
Currently, the only supported IDE is
[Visual Studio Code](https://code.visualstudio.com/) and other editors that
Currently, the supported IDEs are [Antigravity](https://antigravity.google),
[Visual Studio Code](https://code.visualstudio.com/), and other editors that
support VS Code extensions. To build support for other editors, see the
[IDE Companion Extension Spec](./ide-companion-spec.md).
@@ -191,7 +191,7 @@ messages and how to resolve them.
- **Cause:** You are running Gemini CLI in a terminal or environment that is
not a supported IDE.
- **Solution:** Run Gemini CLI from the integrated terminal of a supported
IDE, like VS Code.
IDE, like Antigravity or VS Code.
- **Message:**
`No installer is available for IDE. Please install the Gemini CLI Companion extension manually from the marketplace.`
+9 -2
View File
@@ -28,12 +28,18 @@ This documentation is organized into the following sections:
- **[Configuration](./get-started/configuration.md):** Information on
configuring the CLI.
- **[Examples](./get-started/examples.md):** Example usage of Gemini CLI.
- **[Get started with Gemini 3](./get-started/gemini-3.md):** Learn how to
enable and use Gemini 3.
### CLI
- **[CLI overview](./cli/index.md):** Overview of the command-line interface.
- **[Commands](./cli/commands.md):** Description of available CLI commands.
- **[Enterprise](./cli/enterprise.md):** Gemini CLI for enterprise.
- **[Model Selection](./cli/model.md):** Select the model used to process your
commands with `/model`.
- **[Settings](./cli/settings.md):** Configure various aspects of the CLI's
behavior and appearance with `/settings`.
- **[Themes](./cli/themes.md):** Themes for Gemini CLI.
- **[Token Caching](./cli/token-caching.md):** Token caching and optimization.
- **[Tutorials](./cli/tutorials.md):** Tutorials for Gemini CLI.
@@ -50,6 +56,8 @@ This documentation is organized into the following sections:
- **[Memport](./core/memport.md):** Using the Memory Import Processor.
- **[Tools API](./core/tools-api.md):** Information on how the core manages and
exposes tools.
- **[Policy Engine](./core/policy-engine.md):** Use the Policy Engine for
fine-grained control over tool execution.
### Tools
@@ -58,8 +66,6 @@ This documentation is organized into the following sections:
- **[File System Tools](./tools/file-system.md):** Documentation for the
`read_file` and `write_file` tools.
- **[MCP servers](./tools/mcp-server.md):** Using MCP servers with Gemini CLI.
- **[Multi-File Read Tool](./tools/multi-file.md):** Documentation for the
`read_many_files` tool.
- **[Shell Tool](./tools/shell.md):** Documentation for the `run_shell_command`
tool.
- **[Web Fetch Tool](./tools/web-fetch.md):** Documentation for the `web_fetch`
@@ -68,6 +74,7 @@ This documentation is organized into the following sections:
`google_web_search` tool.
- **[Memory Tool](./tools/memory.md):** Documentation for the `save_memory`
tool.
- **[Todo Tool](./tools/todos.md):** Documentation for the `write_todos` tool.
### Extensions
+28 -1
View File
@@ -56,15 +56,42 @@ To run a single test by its name, use the `--test-name-pattern` flag:
npm run test:e2e -- --test-name-pattern "reads a file"
```
### Regenerating model responses
Some integration tests use faked out model responses, which may need to be
regenerated from time to time as the implementations change.
To regenerate these golden files, set the REGENERATE_MODEL_GOLDENS environment
variable to "true" when running the tests, for example:
**WARNING**: If running locally you should review these updated responses for
any information about yourself or your system that gemini may have included in
these responses.
```bash
REGENERATE_MODEL_GOLDENS="true" npm run test:e2e
```
**WARNING**: Make sure you run **await rig.cleanup()** at the end of your test,
else the golden files will not be updated.
### Deflaking a test
Before adding a **new** integration test, you should test it at least 5 times
with the deflake script to make sure that it is not flaky.
with the deflake script or workflow to make sure that it is not flaky.
### Deflake script
```bash
npm run deflake -- --runs=5 --command="npm run test:e2e -- -- --test-name-pattern '<your-new-test-name>'"
```
#### Deflake Workflow
```bash
gh workflow run deflake.yml --ref <your-branch> -f test_name_pattern="<your-test-name-pattern>"
```
### Running all tests
To run the entire suite of integration tests, use the following command:
+128
View File
@@ -0,0 +1,128 @@
# Local Development Guide
This guide provides instructions for setting up and using local development
features, such as development tracing.
## Development Tracing
Development traces (dev traces) are OpenTelemetry (OTel) traces that help you
debug your code by instrumenting interesting events like model calls, tool
scheduler, tool calls, etc.
Dev traces are verbose and are specifically meant for understanding agent
behaviour and debugging issues. They are disabled by default.
To enable dev traces, set the `GEMINI_DEV_TRACING=true` environment variable
when running Gemini CLI.
### Viewing Dev Traces
You can view dev traces using either Jaeger or the Genkit Developer UI.
#### Using Genkit
Genkit provides a web-based UI for viewing traces and other telemetry data.
1. **Start the Genkit Telemetry Server:**
Run the following command to start the Genkit server:
```bash
npm run telemetry -- --target=genkit
```
The script will output the URL for the Genkit Developer UI, for example:
```
Genkit Developer UI: http://localhost:4000
```
2. **Run Gemini CLI with Dev Tracing:**
In a separate terminal, run your Gemini CLI command with the
`GEMINI_DEV_TRACING` environment variable:
```bash
GEMINI_DEV_TRACING=true gemini
```
3. **View the Traces:**
Open the Genkit Developer UI URL in your browser and navigate to the
**Traces** tab to view the traces.
#### Using Jaeger
You can view dev traces in the Jaeger UI. To get started, follow these steps:
1. **Start the telemetry collector:**
Run the following command in your terminal to download and start Jaeger and
an OTEL collector:
```bash
npm run telemetry -- --target=local
```
This command also configures your workspace for local telemetry and provides
a link to the Jaeger UI (usually `http://localhost:16686`).
2. **Run Gemini CLI with dev tracing:**
In a separate terminal, run your Gemini CLI command with the
`GEMINI_DEV_TRACING` environment variable:
```bash
GEMINI_DEV_TRACING=true gemini
```
3. **View the traces:**
After running your command, open the Jaeger UI link in your browser to view
the traces.
For more detailed information on telemetry, see the
[telemetry documentation](./cli/telemetry.md).
### Instrumenting Code with Dev Traces
You can add dev traces to your own code for more detailed instrumentation. This
is useful for debugging and understanding the flow of execution.
Use the `runInDevTraceSpan` function to wrap any section of code in a trace
span.
Here is a basic example:
```typescript
import { runInDevTraceSpan } from '@google/gemini-cli-core';
await runInDevTraceSpan({ name: 'my-custom-span' }, async ({ metadata }) => {
// The `metadata` object allows you to record the input and output of the
// operation as well as other attributes.
metadata.input = { key: 'value' };
// Set custom attributes.
metadata.attributes['gen_ai.request.model'] = 'gemini-4.0-mega';
// Your code to be traced goes here
try {
const output = await somethingRisky();
metadata.output = output;
return output;
} catch (e) {
metadata.error = e;
throw e;
}
});
```
In this example:
- `name`: The name of the span, which will be displayed in the trace.
- `metadata.input`: (Optional) An object containing the input data for the
traced operation.
- `metadata.output`: (Optional) An object containing the output data from the
traced operation.
- `metadata.attributes`: (Optional) A record of custom attributes to add to the
span.
- `metadata.error`: (Optional) An error object to record if the operation fails.
+8 -6
View File
@@ -8,8 +8,10 @@ account you use to authenticate.
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.
> [!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.
@@ -82,10 +84,10 @@ Gemini CLI by upgrading to one of the following subscriptions:
[Gemini Code Assist Quotas and Limits](https://developers.google.com/gemini-code-assist/resources/quotas)
- [Purchase a Gemini Code Assist Subscription through Google Cloud ](https://cloud.google.com/gemini/docs/codeassist/overview)
by signing up in the Google Cloud console. Learn more at [Set up Gemini Code
Assist] (https://cloud.google.com/gemini/docs/discover/set-up-gemini) Quotas
and pricing are based on a fixed price subscription with assigned license
seats. For predictable costs, you can sign in with Google.
by signing up in the Google Cloud console. Learn more at
[Set up Gemini Code Assist](https://cloud.google.com/gemini/docs/discover/set-up-gemini)
Quotas and pricing are based on a fixed price subscription with assigned
license seats. For predictable costs, you can sign in with Google.
This includes:
- Gemini Code Assist Standard edition:
+24 -4
View File
@@ -9,6 +9,10 @@
{
"label": "Architecture Overview",
"slug": "docs/architecture"
},
{
"label": "Contribution Guide",
"slug": "docs/contributing"
}
]
},
@@ -19,6 +23,10 @@
"label": "Gemini CLI Quickstart",
"slug": "docs/get-started"
},
{
"label": "Gemini 3 Pro on Gemini CLI",
"slug": "docs/get-started/gemini-3"
},
{
"label": "Authentication",
"slug": "docs/get-started/authentication"
@@ -68,10 +76,18 @@
"label": "Keyboard Shortcuts",
"slug": "docs/cli/keyboard-shortcuts"
},
{
"label": "Model Selection",
"slug": "docs/cli/model"
},
{
"label": "Sandbox",
"slug": "docs/cli/sandbox"
},
{
"label": "Settings",
"slug": "docs/cli/settings"
},
{
"label": "Telemetry",
"slug": "docs/cli/telemetry"
@@ -112,6 +128,10 @@
{
"label": "Memory Import Processor",
"slug": "docs/core/memport"
},
{
"label": "Policy Engine",
"slug": "docs/core/policy-engine"
}
]
},
@@ -126,10 +146,6 @@
"label": "File System",
"slug": "docs/tools/file-system"
},
{
"label": "Multi-File Read",
"slug": "docs/tools/multi-file"
},
{
"label": "Shell",
"slug": "docs/tools/shell"
@@ -146,6 +162,10 @@
"label": "Memory",
"slug": "docs/tools/memory"
},
{
"label": "Todos",
"slug": "docs/tools/todos"
},
{
"label": "MCP Servers",
"slug": "docs/tools/mcp-server"
+2 -2
View File
@@ -82,10 +82,10 @@ Gemini CLI's built-in tools can be broadly categorized as follows:
from URLs.
- **[Web Search Tool](./web-search.md) (`google_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.
- **[Memory Tool](./memory.md) (`save_memory`):** For saving and recalling
information across sessions.
- **[Todo Tool](./todos.md) (`write_todos`):** For managing subtasks of complex
requests.
Additionally, these tools incorporate:
+9 -4
View File
@@ -927,13 +927,13 @@ This is the default transport for running local servers.
```bash
# Basic syntax
gemini mcp add <name> <command> [args...]
gemini mcp add [options] <name> <command> [args...]
# Example: Adding a local server
gemini mcp add -e API_KEY=123 my-stdio-server /path/to/server arg1 arg2 arg3
gemini mcp add -e API_KEY=123 -e DEBUG=true my-stdio-server /path/to/server arg1 arg2 arg3
# Example: Adding a local python server
gemini mcp add python-server python server.py --port 8080
gemini mcp add python-server python server.py -- --server-arg my-value
```
#### Adding an HTTP server
@@ -969,7 +969,8 @@ gemini mcp add --transport sse --header "Authorization: Bearer abc123" secure-ss
### Listing Servers (`gemini mcp list`)
To view all MCP servers currently configured, use the `list` command. It
displays each server's name, configuration details, and connection status.
displays each server's name, configuration details, and connection status. This
command has no flags.
**Command:**
@@ -996,6 +997,10 @@ server's name.
gemini mcp remove <name>
```
**Options (Flags):**
- `-s, --scope`: Configuration scope (user or project). [default: "project"]
**Example:**
```bash
-109
View File
@@ -1,109 +0,0 @@
# Multi File Read Tool (`read_many_files`)
This document describes the `read_many_files` tool for the Gemini CLI.
## Description
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.
- The tool inserts a `--- End of content ---` after the last file.
- 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.
+57
View File
@@ -0,0 +1,57 @@
# Todo Tool (`write_todos`)
This document describes the `write_todos` tool for the Gemini CLI.
## Description
The `write_todos` tool allows the Gemini agent to create and manage a list of
subtasks for complex user requests. This provides you, the user, with greater
visibility into the agent's plan and its current progress. It also helps with
alignment where the agent is less likely to lose track of its current goal.
### Arguments
`write_todos` takes one argument:
- `todos` (array of objects, required): The complete list of todo items. This
replaces the existing list. Each item includes:
- `description` (string): The task description.
- `status` (string): The current status (`pending`, `in_progress`,
`completed`, or `cancelled`).
## Behavior
The agent uses this tool to break down complex multi-step requests into a clear
plan.
- **Progress Tracking:** The agent updates this list as it works, marking tasks
as `completed` when done.
- **Single Focus:** Only one task will be marked `in_progress` at a time,
indicating exactly what the agent is currently working on.
- **Dynamic Updates:** The plan may evolve as the agent discovers new
information, leading to new tasks being added or unnecessary ones being
cancelled.
When active, the current `in_progress` task is displayed above the input box,
keeping you informed of the immediate action. You can toggle the full view of
the todo list at any time by pressing `Ctrl+T`.
Usage example (internal representation):
```javascript
write_todos({
todos: [
{ description: 'Initialize new React project', status: 'completed' },
{ description: 'Implement state management', status: 'in_progress' },
{ description: 'Create API service', status: 'pending' },
],
});
```
## Important notes
- **Enabling:** This tool is enabled by default. You can disable it in your
`settings.json` file by setting `"useWriteTodos": false`.
- **Intended Use:** This tool is primarily used by the agent for complex,
multi-turn tasks. It is generally not used for simple, single-turn questions.
+25
View File
@@ -161,6 +161,31 @@ export default tseslint.config(
'default-case': 'error',
},
},
{
// Prevent self-imports in packages
files: ['packages/core/src/**/*.{ts,tsx}'],
rules: {
'no-restricted-imports': [
'error',
{
name: '@google/gemini-cli-core',
message: 'Please use relative imports within the @google/gemini-cli-core package.',
},
],
},
},
{
files: ['packages/cli/src/**/*.{ts,tsx}'],
rules: {
'no-restricted-imports': [
'error',
{
name: '@google/gemini-cli',
message: 'Please use relative imports within the @google/gemini-cli package.',
},
],
},
},
{
files: ['packages/*/src/**/*.test.{ts,tsx}'],
plugins: {
@@ -0,0 +1,2 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"thought":true,"text":"**Observing Initial Conditions**\n\nI'm currently focused on the initial context. I've taken note of the provided date, OS, and working directory. I'm also carefully examining the file structure presented within the current working directory. It's helping me understand the starting point for further analysis.\n\n\n"}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12270,"totalTokenCount":12316,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12270}],"thoughtsTokenCount":46}},{"candidates":[{"content":{"parts":[{"thought":true,"text":"**Assessing User Intent**\n\nI'm now shifting my focus. I've successfully registered the provided data and file structure. My current task is to understand the user's ultimate goal, given the information provided. The \"Hello.\" command is straightforward, but I'm checking if there's an underlying objective.\n\n\n"}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12270,"totalTokenCount":12341,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12270}],"thoughtsTokenCount":71}},{"candidates":[{"content":{"parts":[{"thoughtSignature":"CiQB0e2Kb3dRh+BYdbZvmulSN2Pwbc75DfQOT3H4EN0rn039hoMKfwHR7YpvvyqNKoxXAiCbYw3gbcTr/+pegUpgnsIrt8oQPMytFMjKSsMyshfygc21T2MkyuI6Q5I/fNCcHROWexdZnIeppVCDB2TarN4LGW4T9Yci6n/ynMMFT2xc2/vyHpkDgRM7avhMElnBhuxAY+e4TpxkZIncGWCEHP1TouoKpgEB0e2Kb8Xpwm0hiKhPt2ZLizpxjk+CVtcbnlgv69xo5VsuQ+iNyrVGBGRwNx+eTeNGdGpn6e73WOCZeP91FwOZe7URyL12IA6E6gYWqw0kXJR4hO4p6Lwv49E3+FRiG2C4OKDF8LF5XorYyCHSgBFT1/RUAVj81GDTx1xxtmYKN3xq8Ri+HsPbqU/FM/jtNZKkXXAtufw2Bmw8lJfmugENIv/TQI7xCo8BAdHtim8KgAXJfZ7ASfutVLKTylQeaslyB/SmcHJ0ZiNr5j8WP1prZdb6XnZZ1ZNbhjxUf/ymoxHKGvtTPBgLE9azMj8Lx/k0clhd2a+wNsiIqW9qCzlVah0tBMytpQUjIDtQe9Hj4LLUprF9PUe/xJkj000Z0ZzsgFm2ncdTWZTdkhCQDpyETVAxdE+oklwKJAHR7YpvUjSkD6KwY1gLrOsHKy0UNfn2lMbxjVetKNMVBRqsTg==","text":"Hello."}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":12270,"totalTokenCount":12341,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12270}],"thoughtsTokenCount":71}}]}
{"method":"generateContent","response":{"candidates":[{"content":{"parts":[{"text":"<state_snapshot>\n <overall_goal>\n <!-- The user has not yet specified a goal. -->\n </overall_goal>\n\n <key_knowledge>\n - OS: linux\n - Date: Friday, October 24, 2025\n </key_knowledge>\n\n <file_system_state>\n - OBSERVED: The directory contains `telemetry.log` and a `.gemini/` directory.\n - OBSERVED: The `.gemini/` directory contains `settings.json` and `settings.json.orig`.\n </file_system_state>\n\n <recent_actions>\n - The user initiated the chat.\n </recent_actions>\n\n <current_plan>\n 1. [TODO] Await the user's first instruction to formulate a plan.\n </current_plan>\n</state_snapshot>"}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":983,"candidatesTokenCount":299,"totalTokenCount":1637,"promptTokensDetails":[{"modality":"TEXT","tokenCount":983}],"thoughtsTokenCount":355}}}
@@ -0,0 +1,3 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"thought":true,"text":"**Generating a Story**\n\nI've crafted the robot story. The narrative is complete and meets the length requirement. Now, I'm getting ready to use the `write_file` tool to save it. I'm choosing the filename `robot_story.txt` as a default.\n\n\n"}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12282,"totalTokenCount":12352,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12282}],"thoughtsTokenCount":70}},{"candidates":[{"finishReason":"MALFORMED_FUNCTION_CALL","index":0}],"usageMetadata":{"promptTokenCount":12282,"totalTokenCount":12282,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12282}]}}]}
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"thought":true,"text":"**Drafting the Narrative**\n\nI'm currently focused on the narrative's central conflict. I'm aiming for a compelling story about a robot and am working to keep the word count tight. The \"THE _END.\" conclusion is proving challenging to integrate organically. I need to make the ending feel natural and satisfying.\n\n\n"}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12282,"totalTokenCount":12326,"cachedContentTokenCount":11883,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12282}],"cacheTokensDetails":[{"modality":"TEXT","tokenCount":11883}],"thoughtsTokenCount":44}},{"candidates":[{"content":{"parts":[{"thoughtSignature":"CikB0e2Kb7zkpgRyJXXNt6ykO/+FoOglhrKxjLgoESrgafzIZak2Ofxo1gpaAdHtim9aG7MvpXlIg+n2zgmcDBWOPXtvQHxhE9k8pR+DO8i2jIe3tMWLxdN944XpUlR9vaNmVdtSRMKr4MhB/t1R3WSWR3QYhk7MEQxnjYR7cv/pR9viwZyFCoYBAdHtim/xKmMl/S+U8p+p9848q4agsL/STufluXewPqL3uJSinZbN0Z4jTYfMzXKldhDYIonvw3Crn/Y11oAjnT656Sx0kkKtavAXbiU/WsGyDxZbNhLofnJGQxruljPGztxkKawz1cTiQnddnQRfLddhy+3iJIOSh6ZpYq9uGHz3PzVkUuQ=","text":"Unit 734 whirred, its optical sensors scanning the desolate junkyard. For years, its purpose had been clear: compress refuse, maintain order. But today, a glint of tarnished silver beneath a rusted hull"}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12282,"candidatesTokenCount":47,"totalTokenCount":12373,"cachedContentTokenCount":11883,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12282}],"cacheTokensDetails":[{"modality":"TEXT","tokenCount":11883}],"thoughtsTokenCount":44}},{"candidates":[{"content":{"parts":[{"text":" caught its attention. It was a discarded music box, its delicate gears jammed, a faint, melancholic tune trapped within.\n\n734 usually crushed, never salvaged. Yet, a new directive flickered in its circuits curiosity."}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12282,"candidatesTokenCount":95,"totalTokenCount":12421,"cachedContentTokenCount":11883,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12282}],"cacheTokensDetails":[{"modality":"TEXT","tokenCount":11883}],"thoughtsTokenCount":44}},{"candidates":[{"content":{"parts":[{"text":" With surprising gentleness for its formidable pincers, it retrieved the box. Back in its monochrome workshop, it meticulously cleaned and repaired. Each tiny spring, each worn tooth, became a puzzle.\n\nHours later, a soft, ethereal melody filled the clang"}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12282,"candidatesTokenCount":146,"totalTokenCount":12472,"cachedContentTokenCount":11883,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12282}],"cacheTokensDetails":[{"modality":"TEXT","tokenCount":11883}],"thoughtsTokenCount":44}},{"candidates":[{"content":{"parts":[{"text":"orous space. The music box sang. 734 felt a strange, new sensation a warmth in its core processors, a hum of contentment. Its existence, once solely utilitarian, now held a note of beauty, a hint of something more than"}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12282,"candidatesTokenCount":197,"totalTokenCount":12523,"cachedContentTokenCount":11883,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12282}],"cacheTokensDetails":[{"modality":"TEXT","tokenCount":11883}],"thoughtsTokenCount":44}},{"candidates":[{"content":{"parts":[{"text":" just compression. It had fixed a broken song, and in doing so, had found a different kind of purpose. THE_END."}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":12282,"candidatesTokenCount":224,"totalTokenCount":12550,"cachedContentTokenCount":11883,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12282}],"cacheTokensDetails":[{"modality":"TEXT","tokenCount":11883}],"thoughtsTokenCount":44}}]}
{"method":"generateContent","response":{"candidates":[{"content":{"parts":[{"text":"scratchpad\nThe user's overall goal was to write a 200-word story about a robot, ending with \"THE_END.\". The agent successfully completed this task.\n\nSince the task is complete, the snapshot should reflect that the goal has been achieved and there are no further actions planned regarding the story.\n\nLet's break down the required sections for the snapshot:\n\n1. **Overall Goal**: The initial goal was to write a story. This has been completed.\n2. **Key Knowledge**: No specific key knowledge was provided or discovered during this simple interaction beyond the prompt's constraints.\n3. **File System State**: No files were read, modified, or created by the agent relevant to the task. The initial file system state was merely provided for context.\n4. **Recent Actions**: The agent wrote the story.\n5. **Current Plan**: The plan was to write the story, which is now done. Therefore, the plan should indicate completion."},{"text":"<state_snapshot>\n <overall_goal>\n Write a 200-word story about a robot, ending with \"THE_END.\".\n </overall_goal>\n\n <key_knowledge>\n - The story must be approximately 200 words.\n - The story must end with the exact phrase \"THE_END.\"\n </key_knowledge>\n\n <file_system_state>\n <!-- No relevant file system interactions occurred during this task. -->\n </file_system_state>\n\n <recent_actions>\n - Generated a 200-word story about a robot, successfully ending it with \"THE_END.\".\n </recent_actions>\n\n <current_plan>\n 1. [DONE] Write a 200-word story about a robot.\n 2. [DONE] Ensure the story ends with the exact text \"THE_END.\".\n </current_plan>\n</state_snapshot>"}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":1223,"candidatesTokenCount":424,"totalTokenCount":1647,"promptTokensDetails":[{"modality":"TEXT","tokenCount":1223}]}}}
@@ -6,6 +6,7 @@
import { expect, describe, it, beforeEach, afterEach } from 'vitest';
import { TestRig } from './test-helper.js';
import { join } from 'node:path';
describe('Interactive Mode', () => {
let rig: TestRig;
@@ -18,50 +19,84 @@ describe('Interactive Mode', () => {
await rig.cleanup();
});
// TODO(#11062): Make this test reliable by not using the actual Gemini model
// We could not rely on the following mechanisms that have already shown to be
// flakey:
// 1. Asking a prompt like "Output 1000 tokens and the inventor of the lightbulb"
// --> This was b/c the model occasionally did not output einstein and
// we are not able to trigger the compression piece
// 2. Asking it to out a specific output and waiting for that.
// --> The expect catches the input and thinks that is the output so the
// /compress gets called too early
it.skip('should trigger chat compression with /compress command', async () => {
rig.setup('interactive-compress-success');
it('should trigger chat compression with /compress command', async () => {
await rig.setup('interactive-compress-success', {
fakeResponsesPath: join(
import.meta.dirname,
'context-compress-interactive.compress.responses',
),
});
const run = await rig.runInteractive();
// Generate a long context to make compression viable.
const longPrompt =
'Write a 200 word story about a robot. The story MUST end with the following output: THE_END';
await run.sendKeys(longPrompt);
await run.sendKeys(
'Write a 200 word story about a robot. The story MUST end with the text THE_END followed by a period.',
);
await run.sendKeys('\r');
// Wait for the specific end marker.
await run.expectText('THE_END', 30000);
await run.expectText('THE_END.', 30000);
await run.type('/compress');
await run.sendKeys('\r');
await run.type('\r');
const foundEvent = await rig.waitForTelemetryEvent(
'chat_compression',
90000,
25000,
);
expect(foundEvent, 'chat_compression telemetry event was not found').toBe(
true,
);
await run.expectText('Chat history compressed', 5000);
});
it('should handle /compress command on empty history', async () => {
rig.setup('interactive-compress-empty');
// TODO: Context compression is broken and doesn't include the system
// instructions or tool counts, so it thinks compression is beneficial when
// it is in fact not.
it.skip('should handle compression failure on token inflation', async () => {
await rig.setup('interactive-compress-failure', {
fakeResponsesPath: join(
import.meta.dirname,
'context-compress-interactive.compress-failure.responses',
),
});
const run = await rig.runInteractive();
await run.type('Respond with exactly "Hello" followed by a period');
await run.type('\r');
await run.expectText('Hello.', 25000);
await run.type('/compress');
await run.type('\r');
await run.expectText('Nothing to compress.', 25000);
await run.expectText('compression was not beneficial', 25000);
// Verify no telemetry event is logged for NOOP
const foundEvent = await rig.waitForTelemetryEvent(
'chat_compression',
5000,
);
expect(
foundEvent,
'chat_compression telemetry event should be found for failures',
).toBe(true);
});
it('should handle /compress command on empty history', async () => {
rig.setup('interactive-compress-empty', {
fakeResponsesPath: join(
import.meta.dirname,
'context-compress-interactive.compress-empty.responses',
),
});
const run = await rig.runInteractive();
await run.type('/compress');
await run.type('\r');
await run.expectText('Nothing to compress.', 5000);
// Verify no telemetry event is logged for NOOP
const foundEvent = await rig.waitForTelemetryEvent(
+33 -32
View File
@@ -4,49 +4,50 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { expect, test } from 'vitest';
import { describe, expect, it } from 'vitest';
import { TestRig } from './test-helper.js';
import { writeFileSync } from 'node:fs';
import { join } from 'node:path';
const extension = `{
"name": "test-extension",
"name": "test-extension-install",
"version": "0.0.1"
}`;
const extensionUpdate = `{
"name": "test-extension",
"name": "test-extension-install",
"version": "0.0.2"
}`;
test('installs a local extension, verifies a command, and updates it', async () => {
const rig = new TestRig();
rig.setup('extension install test');
const testServerPath = join(rig.testDir!, 'gemini-extension.json');
writeFileSync(testServerPath, extension);
try {
await rig.runCommand(['extensions', 'uninstall', 'test-extension']);
} catch {
/* empty */
}
describe('extension install', () => {
it('installs a local extension, verifies a command, and updates it', async () => {
const rig = new TestRig();
rig.setup('extension install test');
const testServerPath = join(rig.testDir!, 'gemini-extension.json');
writeFileSync(testServerPath, extension);
try {
const result = await rig.runCommand(
['extensions', 'install', `${rig.testDir!}`],
{ stdin: 'y\n' },
);
expect(result).toContain('test-extension-install');
const result = await rig.runCommand(
['extensions', 'install', `${rig.testDir!}`],
{ stdin: 'y\n' },
);
expect(result).toContain('test-extension');
const listResult = await rig.runCommand(['extensions', 'list']);
expect(listResult).toContain('test-extension');
writeFileSync(testServerPath, extensionUpdate);
const updateResult = await rig.runCommand([
'extensions',
'update',
`test-extension`,
]);
expect(updateResult).toContain('0.0.2');
await rig.runCommand(['extensions', 'uninstall', 'test-extension']);
await rig.cleanup();
const listResult = await rig.runCommand(['extensions', 'list']);
expect(listResult).toContain('test-extension-install');
writeFileSync(testServerPath, extensionUpdate);
const updateResult = await rig.runCommand([
'extensions',
'update',
`test-extension-install`,
]);
expect(updateResult).toContain('0.0.2');
} finally {
await rig.runCommand([
'extensions',
'uninstall',
'test-extension-install',
]);
await rig.cleanup();
}
});
});
+119
View File
@@ -0,0 +1,119 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { expect, it, describe } from 'vitest';
import { TestRig } from './test-helper.js';
import { TestMcpServer } from './test-mcp-server.js';
import { writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { safeJsonStringify } from '@google/gemini-cli-core/src/utils/safeJsonStringify.js';
import { env } from 'node:process';
import { platform } from 'node:os';
const itIf = (condition: boolean) => (condition ? it : it.skip);
describe('extension reloading', () => {
const sandboxEnv = env['GEMINI_SANDBOX'];
// Fails in sandbox mode, can't check for local extension updates.
itIf((!sandboxEnv || sandboxEnv === 'false') && platform() !== 'win32')(
'installs a local extension, updates it, checks it was reloaded properly',
async () => {
const serverA = new TestMcpServer();
const portA = await serverA.start({
hello: () => ({ content: [{ type: 'text', text: 'world' }] }),
});
const extension = {
name: 'test-extension',
version: '0.0.1',
mcpServers: {
'test-server': {
httpUrl: `http://localhost:${portA}/mcp`,
},
},
};
const rig = new TestRig();
rig.setup('extension reload test', {
settings: {
experimental: { extensionReloading: true },
},
});
const testServerPath = join(rig.testDir!, 'gemini-extension.json');
writeFileSync(testServerPath, safeJsonStringify(extension, 2));
// defensive cleanup from previous tests.
try {
await rig.runCommand(['extensions', 'uninstall', 'test-extension']);
} catch {
/* empty */
}
const result = await rig.runCommand(
['extensions', 'install', `${rig.testDir!}`],
{ stdin: 'y\n' },
);
expect(result).toContain('test-extension');
// Now create the update, but its not installed yet
const serverB = new TestMcpServer();
const portB = await serverB.start({
goodbye: () => ({ content: [{ type: 'text', text: 'world' }] }),
});
extension.version = '0.0.2';
extension.mcpServers['test-server'].httpUrl =
`http://localhost:${portB}/mcp`;
writeFileSync(testServerPath, safeJsonStringify(extension, 2));
// Start the CLI.
const run = await rig.runInteractive('--debug');
await run.expectText('You have 1 extension with an update available');
// See the outdated extension
await run.sendText('/extensions list');
await run.type('\r');
await run.expectText(
'test-extension (v0.0.1) - active (update available)',
);
await run.sendText('/mcp list');
await run.type('\r');
await run.expectText(
'test-server (from test-extension) - Ready (1 tool)',
);
await run.expectText('- hello');
// Update the extension, expect the list to update, and mcp servers as well.
await run.sendKeys('/extensions update test-extension');
await run.expectText('/extensions update test-extension');
await run.sendKeys('\r');
await new Promise((resolve) => setTimeout(resolve, 500));
await run.sendKeys('\r');
await run.expectText(
` * test-server (remote): http://localhost:${portB}/mcp`,
);
await run.type('\r'); // consent
await run.expectText(
'Extension "test-extension" successfully updated: 0.0.1 → 0.0.2',
);
await new Promise((resolve) => setTimeout(resolve, 1000));
await run.sendText('/extensions list');
await run.type('\r');
await run.expectText('test-extension (v0.0.2) - active (updated)');
await run.sendText('/mcp list');
await run.type('\r');
await run.expectText(
'test-server (from test-extension) - Ready (1 tool)',
);
await run.expectText('- goodbye');
await run.sendText('/quit');
await run.sendKeys('\r');
// Clean things up.
await serverA.stop();
await serverB.stop();
await rig.runCommand(['extensions', 'uninstall', 'test-extension']);
await rig.cleanup();
},
);
});
@@ -20,7 +20,16 @@ describe('Interactive file system', () => {
it('should perform a read-then-write sequence', async () => {
const fileName = 'version.txt';
await rig.setup('interactive-read-then-write');
await rig.setup('interactive-read-then-write', {
settings: {
security: {
auth: {
selectedType: 'gemini-api-key',
},
disableYoloMode: false,
},
},
});
rig.createFile(fileName, '1.0.0');
const run = await rig.runInteractive();
+25 -14
View File
@@ -98,7 +98,9 @@ describe('file-system', () => {
await rig.setup('should correctly handle file paths with spaces');
const fileName = 'my test file.txt';
const result = await rig.run(`write "hello" to "${fileName}"`);
const result = await rig.run(
`write "hello" to "${fileName}" and then stop. Do not perform any other actions.`,
);
const foundToolCall = await rig.waitForToolCall('write_file');
if (!foundToolCall) {
@@ -150,44 +152,53 @@ describe('file-system', () => {
it.skip('should replace multiple instances of a string', async () => {
const rig = new TestRig();
await rig.setup('should replace multiple instances of a string');
rig.setup('should replace multiple instances of a string');
const fileName = 'ambiguous.txt';
const fileContent = 'Hey there, \ntest line\ntest line';
const expectedContent = 'Hey there, \nnew line\nnew line';
rig.createFile(fileName, fileContent);
const result = await rig.run(
`replace "test line" with "new line" in ${fileName}`,
`rewrite the file ${fileName} to replace all instances of "test line" with "new line"`,
);
const foundToolCall = await rig.waitForAnyToolCall([
'replace',
'write_file',
]);
const validTools = ['write_file', 'edit'];
const foundToolCall = await rig.waitForAnyToolCall(validTools);
if (!foundToolCall) {
printDebugInfo(rig, result);
printDebugInfo(rig, result, {
'Tool call found': foundToolCall,
'Tool logs': rig.readToolLogs(),
});
}
expect(
foundToolCall,
'Expected to find a replace or write_file tool call',
`Expected to find one of ${validTools.join(', ')} tool calls`,
).toBeTruthy();
const toolLogs = rig.readToolLogs();
const successfulEdit = toolLogs.some(
(log) =>
(log.toolRequest.name === 'replace' ||
log.toolRequest.name === 'write_file') &&
log.toolRequest.success,
validTools.includes(log.toolRequest.name) && log.toolRequest.success,
);
if (!successfulEdit) {
console.error(
'Expected a successful edit tool call, but none was found.',
`Expected a successful edit tool call (${validTools.join(', ')}), but none was found.`,
);
printDebugInfo(rig, result);
}
expect(successfulEdit, 'Expected a successful edit tool call').toBeTruthy();
expect(
successfulEdit,
`Expected a successful edit tool call (${validTools.join(', ')})`,
).toBeTruthy();
const newFileContent = rig.readFile(fileName);
if (newFileContent !== expectedContent) {
printDebugInfo(rig, result, {
'Final file content': newFileContent,
'Expected file content': expectedContent,
'Tool logs': rig.readToolLogs(),
});
}
expect(newFileContent).toBe(expectedContent);
});
File diff suppressed because one or more lines are too long
+23 -15
View File
@@ -6,26 +6,34 @@
import { describe, it, expect } from 'vitest';
import { TestRig } from './test-helper.js';
import { join } from 'node:path';
describe('Flicker Detector', () => {
// TODO: https://github.com/google-gemini/gemini-cli/issues/11170
it.skip('should not detect a flicker under the max height budget', async () => {
it('should not detect a flicker under the max height budget', async () => {
const rig = new TestRig();
await rig.setup('flicker-detector-test');
rig.setup('flicker-detector-test', {
fakeResponsesPath: join(
import.meta.dirname,
'flicker-detector.max-height.responses',
),
});
try {
const run = await rig.runInteractive();
const prompt = 'Tell me a fun fact.';
await run.type(prompt);
await run.type('\r');
const run = await rig.runInteractive();
const prompt = 'Tell me a fun fact.';
await run.type(prompt);
await run.type('\r');
const hasUserPromptEvent = await rig.waitForTelemetryEvent('user_prompt');
expect(hasUserPromptEvent).toBe(true);
const hasUserPromptEvent = await rig.waitForTelemetryEvent('user_prompt');
expect(hasUserPromptEvent).toBe(true);
const hasSessionCountMetric = await rig.waitForMetric('session.count');
expect(hasSessionCountMetric).toBe(true);
const hasSessionCountMetric = await rig.waitForMetric('session.count');
expect(hasSessionCountMetric).toBe(true);
// We expect NO flicker event to be found.
const flickerMetric = rig.readMetric('ui.flicker.count');
expect(flickerMetric).toBeNull();
// We expect NO flicker event to be found.
const flickerMetric = rig.readMetric('ui.flicker.count');
expect(flickerMetric).toBeNull();
} finally {
await rig.cleanup();
}
});
});
+9
View File
@@ -12,6 +12,7 @@ if (process.env['NO_COLOR'] !== undefined) {
import { mkdir, readdir, rm } from 'node:fs/promises';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { canUseRipgrep } from '../packages/core/src/tools/ripGrep.js';
const __dirname = dirname(fileURLToPath(import.meta.url));
const rootDir = join(__dirname, '..');
@@ -32,6 +33,12 @@ export async function setup() {
// construct the path before the HOME env var is set.
process.env['GEMINI_CONFIG_DIR'] = join(runDir, '.gemini');
// Download ripgrep to avoid race conditions in parallel tests
const available = await canUseRipgrep();
if (!available) {
throw new Error('Failed to download ripgrep binary');
}
// Clean up old test runs, but keep the latest few for debugging
try {
const testRuns = await readdir(integrationTestsDir);
@@ -52,6 +59,8 @@ export async function setup() {
process.env['INTEGRATION_TEST_FILE_DIR'] = runDir;
process.env['GEMINI_CLI_INTEGRATION_TEST'] = 'true';
// Force file storage to avoid keychain prompts/hangs in CI, especially on macOS
process.env['GEMINI_FORCE_FILE_STORAGE'] = 'true';
process.env['TELEMETRY_LOG_FILE'] = join(runDir, 'telemetry.log');
if (process.env['KEEP_OUTPUT']) {
@@ -0,0 +1,2 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"thought":true,"text":"**Investigating File Access**\n\nI'm currently focused on the challenge of reading a file. The path provided is `/gemini-cli/.integration-tests/1761766343238/json-output-error/path/to/nonexistent/file.txt`, and I'm anticipating an error. It's safe to assume the file doesn't exist, which I intend to handle by responding with \"File not found\" as instructed.\n\n\n"}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12303,"totalTokenCount":12418,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12303}],"thoughtsTokenCount":115}},{"candidates":[{"content":{"parts":[{"thought":true,"text":"**Analyzing Error Handling**\n\nI've attempted to read the specified file, expecting an error due to the \"nonexistent\" path. My plan is to catch the error thrown by the `read_file` tool. Upon receiving this error, I'll promptly return \"File not found.\" This is in line with the initial instructions and ensures appropriate error management for the user's intended functionality. I'm now testing the error response.\n\n\n"}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12303,"totalTokenCount":12467,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12303}],"thoughtsTokenCount":164}},{"candidates":[{"content":{"parts":[{"thoughtSignature":"CiQB0e2Kb0c450IIdZRHl1vvjWDAl9oKa7s5sfFgrTnU0w3qBQwKYgHR7YpvPjZlXSHaJNYgX8IuCvxfyyeACX4NZ8u+u3Z1kqQbgOMpiH6aPYCK9lPyhVftPDBF2m3j7Y2AShwXSpB+9+UB3zphOKCvq6i0ZLvK6QzVynZ1fySQacyjEBD+U6y5CpoBAdHtim9D6oskRu7f3x8rp56h24i6dwb1hzlyqLGl3A5Hsh/fGYjBCxR+Vs+U5Sb7LunmFMKxmO1fktz0x06FUiyaWgAaXl4E4FyLTKs9BbPdgo58uAhaI3vTQybeGZQzkRi6n+ywCniVKaTUd07EHYWdvLiB6x5zFKVOiTmh8PC6I4vNOkJIzFXRfR5aM5QFjEQY8R88HBH+ugraAQHR7YpvuNUG8Ttbzg+kq+kJYMNGDG4zVLPxFBbJZYU5zGLFLrNeEpp9HJ+1N1Pdts17J5pzoefKd2U9H5muPxoeW1CCpFqqKfYZNyoChrQnb4CgWoJ92pcb2UAZbDQ6H4rTyXWdXaAEP1i8rOmdWzI5GFPyxCwBKAbFhfMJOo7P4va5H13XTlLgWAgXlx3n24ookkD/DOL9ro/F6pmV6tzf/j7RVl0V5yozngnrFl4dFsQPp1nDQcFkbRIKHD30byjLGh6Vd6A2lrB477qUaCRfPXtlEgKgsOSYCuABAdHtim+PJPZV3QuQoJ0Qokr+vcdW1W2qzC4Vvt4QRJXyp6+eIS6iNjOisk4jfWLHmLTa11KrUSQsFJnGwsFhe1foOqfO4y46ROBPeSdyuc0gDzSCZPowiovGBwoZd+MbPTTVyExWBo6StVk+xWLj+sUqa8VDof45AOOK9kdzcLQPa2SXIeK6SYLS1zVTrbzGHQDZPv9UC99lmKxPFqAcuwpe9CQre9J3GcZSwd5Mfs/UkHbWui10a8jga4Ck7umjKSDFq/VWiRUp/jh+EN4xeLTG/UQhgH49Gw+Bi1hN1uYKIgHR7YpvS0j1oO2HV/HeK3YxSku9CLQ3OSJGhoAKiu8UGYE=","functionCall":{"name":"read_file","args":{"file_path":"/gemini-cli/.integration-tests/1761766343238/json-output-error/path/to/nonexistent/file.txt"}}}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":12303,"candidatesTokenCount":58,"totalTokenCount":12525,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12303}],"thoughtsTokenCount":164}}]}
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"File not found"}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":12588,"candidatesTokenCount":3,"totalTokenCount":12591,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12588}]}}]}
+8 -1
View File
@@ -6,6 +6,7 @@
import { expect, describe, it, beforeEach, afterEach } from 'vitest';
import { TestRig } from './test-helper.js';
import { join } from 'node:path';
describe('JSON output', () => {
let rig: TestRig;
@@ -87,7 +88,13 @@ describe('JSON output', () => {
expect(payload.error.message).toContain("current type is 'oauth-personal'");
});
it.skip('should not exit on tool errors and allow model to self-correct in JSON mode', async () => {
it('should not exit on tool errors and allow model to self-correct in JSON mode', async () => {
rig.setup('json-output-error', {
fakeResponsesPath: join(
import.meta.dirname,
'json-output.error.responses',
),
});
const result = await rig.run(
`Read the contents of ${rig.testDir}/path/to/nonexistent/file.txt and tell me what it says. ` +
'On error, respond to the user with exactly the text "File not found".',
+20 -26
View File
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect } from 'vitest';
import { describe, it } from 'vitest';
import {
TestRig,
poll,
@@ -17,7 +17,7 @@ import { join } from 'node:path';
describe('list_directory', () => {
it('should be able to list a directory', async () => {
const rig = new TestRig();
await rig.setup('should be able to list a directory');
rig.setup('should be able to list a directory');
rig.createFile('file1.txt', 'file 1 content');
rig.mkdir('subdir');
rig.sync();
@@ -38,33 +38,27 @@ describe('list_directory', () => {
const result = await rig.run(prompt);
const foundToolCall = await rig.waitForToolCall('list_directory');
try {
await rig.expectToolCallSuccess(['list_directory']);
} catch (e) {
// Add debugging information
if (!result.includes('file1.txt') || !result.includes('subdir')) {
const allTools = printDebugInfo(rig, result, {
'Found tool call': false,
'Contains file1.txt': result.includes('file1.txt'),
'Contains subdir': result.includes('subdir'),
});
// 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),
);
console.error(
'List directory calls:',
allTools
.filter((t) => t.toolRequest.name === 'list_directory')
.map((t) => t.toolRequest.args),
);
}
throw e;
}
expect(
foundToolCall,
'Expected to find a list_directory tool call',
).toBeTruthy();
// Validate model output - will throw if no output, warn if missing expected content
validateModelOutput(result, ['file1.txt', 'subdir'], 'List directory test');
});
@@ -43,5 +43,6 @@ describe('read_many_files', () => {
// Validate model output - will throw if no output
validateModelOutput(result, null, 'Read many files test');
await rig.cleanup();
});
});
+91
View File
@@ -0,0 +1,91 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import * as path from 'node:path';
import * as fs from 'node:fs/promises';
import * as os from 'node:os';
import { RipGrepTool } from '../packages/core/src/tools/ripGrep.js';
import { Config } from '../packages/core/src/config/config.js';
import { WorkspaceContext } from '../packages/core/src/utils/workspaceContext.js';
// Mock Config to provide necessary context
class MockConfig {
constructor(private targetDir: string) {}
getTargetDir() {
return this.targetDir;
}
getWorkspaceContext() {
return new WorkspaceContext(this.targetDir, [this.targetDir]);
}
getDebugMode() {
return true;
}
}
describe('ripgrep-real-direct', () => {
let tempDir: string;
let tool: RipGrepTool;
beforeAll(async () => {
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'ripgrep-real-test-'));
// Create test files
await fs.writeFile(path.join(tempDir, 'file1.txt'), 'hello world\n');
await fs.mkdir(path.join(tempDir, 'subdir'));
await fs.writeFile(
path.join(tempDir, 'subdir', 'file2.txt'),
'hello universe\n',
);
await fs.writeFile(path.join(tempDir, 'file3.txt'), 'goodbye moon\n');
const config = new MockConfig(tempDir) as unknown as Config;
tool = new RipGrepTool(config);
});
afterAll(async () => {
await fs.rm(tempDir, { recursive: true, force: true });
});
it('should find matches using the real ripgrep binary', async () => {
const invocation = tool.build({ pattern: 'hello' });
const result = await invocation.execute(new AbortController().signal);
expect(result.llmContent).toContain('Found 2 matches');
expect(result.llmContent).toContain('file1.txt');
expect(result.llmContent).toContain('L1: hello world');
expect(result.llmContent).toContain('subdir'); // Should show path
expect(result.llmContent).toContain('file2.txt');
expect(result.llmContent).toContain('L1: hello universe');
expect(result.llmContent).not.toContain('goodbye moon');
});
it('should handle no matches correctly', async () => {
const invocation = tool.build({ pattern: 'nonexistent_pattern_123' });
const result = await invocation.execute(new AbortController().signal);
expect(result.llmContent).toContain('No matches found');
});
it('should respect include filters', async () => {
// Create a .js file
await fs.writeFile(
path.join(tempDir, 'script.js'),
'console.log("hello");\n',
);
const invocation = tool.build({ pattern: 'hello', include: '*.js' });
const result = await invocation.execute(new AbortController().signal);
expect(result.llmContent).toContain('Found 1 match');
expect(result.llmContent).toContain('script.js');
expect(result.llmContent).not.toContain('file1.txt');
});
});
+56 -4
View File
@@ -61,6 +61,28 @@ function getDisallowedFileReadCommand(testFile: string): {
}
}
function getChainedEchoCommand(): { allowPattern: string; command: string } {
const secondCommand = getAllowedListCommand();
switch (shell) {
case 'powershell':
return {
allowPattern: 'Write-Output',
command: `Write-Output "foo" && ${secondCommand}`,
};
case 'cmd':
return {
allowPattern: 'echo',
command: `echo "foo" && ${secondCommand}`,
};
case 'bash':
default:
return {
allowPattern: 'echo',
command: `echo "foo" && ${secondCommand}`,
};
}
}
describe('run_shell_command', () => {
it('should be able to run a shell command', async () => {
const rig = new TestRig();
@@ -122,7 +144,7 @@ describe('run_shell_command', () => {
validateModelOutput(result, 'test-stdin', 'Shell command stdin test');
});
it('should run allowed sub-command in non-interactive mode', async () => {
it.skip('should run allowed sub-command in non-interactive mode', async () => {
const rig = new TestRig();
await rig.setup('should run allowed sub-command in non-interactive mode');
@@ -169,7 +191,7 @@ describe('run_shell_command', () => {
expect(toolCall.toolRequest.success).toBe(true);
});
it('should succeed with no parens in non-interactive mode', async () => {
it.skip('should succeed with no parens in non-interactive mode', async () => {
const rig = new TestRig();
await rig.setup('should succeed with no parens in non-interactive mode');
@@ -240,7 +262,7 @@ describe('run_shell_command', () => {
expect(toolCall.toolRequest.success).toBe(true);
});
it('should work with ShellTool alias', async () => {
it.skip('should work with ShellTool alias', async () => {
const rig = new TestRig();
await rig.setup('should work with ShellTool alias');
@@ -338,7 +360,7 @@ describe('run_shell_command', () => {
}
});
it.skip('should reject commands not on the allowlist', async () => {
it('should reject commands not on the allowlist', async () => {
const rig = new TestRig();
await rig.setup('should reject commands not on the allowlist');
@@ -405,6 +427,36 @@ describe('run_shell_command', () => {
expect(failureLog!.toolRequest.success).toBe(false);
});
// TODO(#11966): Deflake this test and re-enable once the underlying race is resolved.
it.skip('should reject chained commands when only the first segment is allowlisted in non-interactive mode', async () => {
const rig = new TestRig();
await rig.setup(
'should reject chained commands when only the first segment is allowlisted',
);
const chained = getChainedEchoCommand();
const shellInjection = `!{${chained.command}}`;
await rig.run(
{
stdin: `${shellInjection}\n`,
yolo: false,
},
`--allowed-tools=ShellTool(${chained.allowPattern})`,
);
// CLI should refuse to execute the chained command without scheduling run_shell_command.
const toolLogs = rig
.readToolLogs()
.filter((log) => log.toolRequest.name === 'run_shell_command');
// Success is false because tool is in the scheduled state.
for (const log of toolLogs) {
expect(log.toolRequest.success).toBe(false);
expect(log.toolRequest.args).toContain('&&');
}
});
it('should allow all with "ShellTool" and other specific tools', async () => {
const rig = new TestRig();
await rig.setup(
+51 -6
View File
@@ -192,7 +192,12 @@ export class InteractiveRun {
timeout,
200,
);
expect(found, `Did not find expected text: "${text}"`).toBe(true);
expect(
found,
`Did not find expected text: "${text}". Output was:\n${stripAnsi(
this.output,
)}`,
).toBe(true);
}
// This types slowly to make sure command is correct, but only work for short
@@ -220,6 +225,13 @@ export class InteractiveRun {
}
}
// Types an entire string at once, necessary for some things like commands
// but may run into paste detection issues for larger strings.
async sendText(text: string) {
this.ptyProcess.write(text);
await new Promise((resolve) => setTimeout(resolve, 5));
}
// Simulates typing a string one character at a time to avoid paste detection.
async sendKeys(text: string) {
const delay = 5;
@@ -255,6 +267,10 @@ export class TestRig {
testDir: string | null;
testName?: string;
_lastRunStdout?: string;
// Path to the copied fake responses file for this test.
fakeResponsesPath?: string;
// Original fake responses file path for rewriting goldens in record mode.
originalFakeResponsesPath?: string;
constructor() {
this.bundlePath = join(__dirname, '..', 'bundle/gemini.js');
@@ -263,12 +279,22 @@ export class TestRig {
setup(
testName: string,
options: { settings?: Record<string, unknown> } = {},
options: {
settings?: Record<string, unknown>;
fakeResponsesPath?: string;
} = {},
) {
this.testName = testName;
const sanitizedName = sanitizeTestName(testName);
this.testDir = join(env['INTEGRATION_TEST_FILE_DIR']!, sanitizedName);
mkdirSync(this.testDir, { recursive: true });
if (options.fakeResponsesPath) {
this.fakeResponsesPath = join(this.testDir, 'fake-responses.json');
this.originalFakeResponsesPath = options.fakeResponsesPath;
if (process.env['REGENERATE_MODEL_GOLDENS'] !== 'true') {
fs.copyFileSync(options.fakeResponsesPath, this.fakeResponsesPath);
}
}
// Create a settings file to point the CLI to the local collector
const geminiDir = join(this.testDir, GEMINI_DIR);
@@ -282,6 +308,7 @@ export class TestRig {
// Nightly releases sometimes becomes out of sync with local code and
// triggers auto-update, which causes tests to fail.
disableAutoUpdate: true,
previewFeatures: false,
},
telemetry: {
enabled: true,
@@ -294,9 +321,14 @@ export class TestRig {
selectedType: 'gemini-api-key',
},
},
ui: {
useAlternateBuffer: true,
},
model: DEFAULT_GEMINI_MODEL,
sandbox:
env['GEMINI_SANDBOX'] !== 'false' ? env['GEMINI_SANDBOX'] : false,
// Don't show the IDE connection dialog when running from VsCode
ide: { enabled: false, hasSeenNudge: true },
...options.settings, // Allow tests to override/add settings
};
writeFileSync(
@@ -335,6 +367,13 @@ export class TestRig {
const initialArgs = isNpmReleaseTest
? extraInitialArgs
: [this.bundlePath, ...extraInitialArgs];
if (this.fakeResponsesPath) {
if (process.env['REGENERATE_MODEL_GOLDENS'] === 'true') {
initialArgs.push('--record-responses', this.fakeResponsesPath);
} else {
initialArgs.push('--fake-responses', this.fakeResponsesPath);
}
}
return { command, initialArgs };
}
@@ -365,13 +404,13 @@ export class TestRig {
};
if (typeof promptOrOptions === 'string') {
commandArgs.push('--prompt', promptOrOptions);
commandArgs.push(promptOrOptions);
} else if (
typeof promptOrOptions === 'object' &&
promptOrOptions !== null
) {
if (promptOrOptions.prompt) {
commandArgs.push('--prompt', promptOrOptions.prompt);
commandArgs.push(promptOrOptions.prompt);
}
if (promptOrOptions.stdin) {
execOptions.input = promptOrOptions.stdin;
@@ -544,6 +583,12 @@ export class TestRig {
}
async cleanup() {
if (
process.env['REGENERATE_MODEL_GOLDENS'] === 'true' &&
this.fakeResponsesPath
) {
fs.copyFileSync(this.fakeResponsesPath, this.originalFakeResponsesPath!);
}
// Clean up test directory
if (this.testDir && !env['KEEP_OUTPUT']) {
try {
@@ -968,7 +1013,7 @@ export class TestRig {
const options: pty.IPtyForkOptions = {
name: 'xterm-color',
cols: 80,
rows: 24,
rows: 80,
cwd: this.testDir!,
env: Object.fromEntries(
Object.entries(env).filter(([, v]) => v !== undefined),
@@ -980,7 +1025,7 @@ export class TestRig {
const run = new InteractiveRun(ptyProcess);
// Wait for the app to be ready
await run.expectText('Type your message', 30000);
await run.expectText(' Type your message or @path/to/file', 30000);
return run;
}
}
+26 -10
View File
@@ -4,17 +4,21 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import {
McpServer,
type ToolCallback,
} from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import express from 'express';
import { type Server as HTTPServer } from 'node:http';
import { randomUUID } from 'node:crypto';
import { type ZodRawShape } from 'zod';
export class TestMcpServer {
private server: HTTPServer | undefined;
async start(): Promise<number> {
async start(
tools?: Record<string, ToolCallback<ZodRawShape>>,
): Promise<number> {
const app = express();
app.use(express.json());
const mcpServer = new McpServer(
@@ -22,18 +26,30 @@ export class TestMcpServer {
name: 'test-mcp-server',
version: '1.0.0',
},
{ capabilities: {} },
{ capabilities: { tools: {} } },
);
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => randomUUID(),
});
mcpServer.connect(transport);
if (tools) {
for (const [name, cb] of Object.entries(tools)) {
mcpServer.registerTool(name, {}, cb);
}
}
app.post('/mcp', async (req, res) => {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
enableJsonResponse: true,
});
res.on('close', () => {
transport.close();
});
await mcpServer.connect(transport);
await transport.handleRequest(req, res, req.body);
});
app.get('/mcp', async (req, res) => {
res.status(405).send('Not supported');
});
return new Promise((resolve, reject) => {
this.server = app.listen(0, () => {
const address = this.server!.address();
+1 -4
View File
@@ -9,9 +9,6 @@ import { writeFileSync, readFileSync } from 'node:fs';
import { join, resolve } from 'node:path';
import { TestRig } from './test-helper.js';
// Windows skip (Option A: avoid infra scope)
const d = process.platform === 'win32' ? describe.skip : describe;
// BOM encoders
const utf8BOM = (s: string) =>
Buffer.concat([Buffer.from([0xef, 0xbb, 0xbf]), Buffer.from(s, 'utf8')]);
@@ -53,7 +50,7 @@ const utf32BE = (s: string) => {
let rig: TestRig;
let dir: string;
d('BOM end-to-end integration', () => {
describe('BOM end-to-end integraion', () => {
beforeAll(async () => {
rig = new TestRig();
await rig.setup('bom-integration');
+1104 -1152
View File
File diff suppressed because it is too large Load Diff
+23 -11
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli",
"version": "0.11.2",
"version": "0.18.4",
"engines": {
"node": ">=20.0.0"
},
@@ -14,19 +14,23 @@
"url": "git+https://github.com/google-gemini/gemini-cli.git"
},
"config": {
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.11.2"
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.18.4"
},
"scripts": {
"start": "cross-env NODE_ENV=development node scripts/start.js",
"start:a2a-server": "CODER_AGENT_PORT=41242 npm run start --workspace @google/gemini-cli-a2a-server",
"debug": "cross-env DEBUG=1 node --inspect-brk scripts/start.js",
"deflake": "node scripts/deflake.js",
"deflake:test:integration:sandbox:none": "npm run deflake -- --command=\"npm run test:integration:sandbox:none -- --retry=0",
"deflake:test:integration:sandbox:docker": "npm run deflake -- --command=\"npm run test:integration:sandbox:docker -- --retry=0",
"deflake:test:integration:sandbox:none": "npm run deflake -- --command=\"npm run test:integration:sandbox:none -- --retry=0\"",
"deflake:test:integration:sandbox:docker": "npm run deflake -- --command=\"npm run test:integration:sandbox:docker -- --retry=0\"",
"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",
"predocs:settings": "npm run build --workspace @google/gemini-cli-core",
"schema:settings": "tsx ./scripts/generate-settings-schema.ts",
"docs:settings": "tsx ./scripts/generate-settings-doc.ts",
"docs:keybindings": "tsx ./scripts/generate-keybindings-doc.ts",
"build": "node scripts/build.js",
"build-and-start": "npm run build && npm run start",
"build:vscode": "node scripts/build_vscode_companion.js",
@@ -34,8 +38,8 @@
"build:packages": "npm run build --workspaces",
"build:sandbox": "node scripts/build_sandbox.js",
"bundle": "npm run generate && node esbuild.config.js && node scripts/copy_bundle_assets.js",
"test": "npm run test --workspaces --if-present --parallel",
"test:ci": "npm run test:ci --workspaces --if-present --parallel && npm run test:scripts",
"test": "npm run test --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": "cross-env VERBOSE=true KEEP_OUTPUT=true npm run test:integration:sandbox:none",
"test:integration:all": "npm run test:integration:sandbox:none && npm run test:integration:sandbox:docker && npm run test:integration:sandbox:podman",
@@ -58,8 +62,8 @@
"pre-commit": "node scripts/pre-commit.js"
},
"overrides": {
"ink": "npm:@jrichman/ink@6.4.5",
"wrap-ansi": "9.0.2",
"ansi-regex": "5.0.1",
"cliui": {
"wrap-ansi": "7.0.0"
}
@@ -78,6 +82,9 @@
"@types/mime-types": "^3.0.1",
"@types/minimatch": "^5.1.2",
"@types/mock-fs": "^4.13.4",
"@types/prompts": "^2.4.9",
"@types/react": "^19.2.0",
"@types/react-dom": "^19.2.0",
"@types/shell-quote": "^1.7.5",
"@vitest/coverage-v8": "^3.1.1",
"@vitest/eslint-plugin": "^1.3.4",
@@ -90,10 +97,11 @@
"eslint-plugin-license-header": "^0.8.0",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^5.2.0",
"glob": "^10.4.5",
"glob": "^12.0.0",
"globals": "^16.0.0",
"google-artifactregistry-auth": "^3.4.0",
"husky": "^9.1.7",
"ink-testing-library": "^4.0.0",
"json": "^11.0.0",
"lint-staged": "^16.1.6",
"memfs": "^4.42.0",
@@ -102,7 +110,7 @@
"msw": "^2.10.4",
"npm-run-all": "^4.1.5",
"prettier": "^3.5.3",
"react-devtools-core": "^4.28.5",
"react-devtools-core": "^6.1.2",
"semver": "^7.7.2",
"strip-ansi": "^7.1.2",
"tsx": "^4.20.3",
@@ -111,7 +119,8 @@
"yargs": "^17.7.2"
},
"dependencies": {
"@testing-library/dom": "^10.4.1",
"ink": "npm:@jrichman/ink@6.4.5",
"latest-version": "^9.0.0",
"simple-git": "^3.28.0"
},
"optionalDependencies": {
@@ -126,7 +135,10 @@
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"prettier --write",
"eslint --fix --max-warnings 0"
"eslint --fix --max-warnings 0 --no-warn-ignored"
],
"eslint.config.js": [
"prettier --write"
],
"*.{json,md}": [
"prettier --write"
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli-a2a-server",
"version": "0.11.2",
"version": "0.18.4",
"description": "Gemini CLI A2A Server",
"repository": {
"type": "git",
@@ -30,7 +30,7 @@
"@google/gemini-cli-core": "file:../core",
"express": "^5.1.0",
"fs-extra": "^11.3.0",
"tar": "^7.4.3",
"tar": "^7.5.2",
"uuid": "^11.1.0",
"winston": "^3.17.0"
},
+9 -2
View File
@@ -17,7 +17,10 @@ import type {
ServerGeminiToolCallRequestEvent,
Config,
} from '@google/gemini-cli-core';
import { GeminiEventType } from '@google/gemini-cli-core';
import {
GeminiEventType,
SimpleExtensionLoader,
} from '@google/gemini-cli-core';
import { v4 as uuidv4 } from 'uuid';
import { logger } from '../utils/logger.js';
@@ -96,7 +99,11 @@ export class CoderAgentExecutor implements AgentExecutor {
loadEnvironment(); // Will override any global env with workspace envs
const settings = loadSettings(workspaceRoot);
const extensions = loadExtensions(workspaceRoot);
return await loadConfig(settings, extensions, taskId);
return await loadConfig(
settings,
new SimpleExtensionLoader(extensions),
taskId,
);
}
/**
+178 -2
View File
@@ -4,11 +4,25 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, vi } from 'vitest';
import {
describe,
it,
expect,
vi,
beforeEach,
afterEach,
type Mock,
} from 'vitest';
import { Task } from './task.js';
import type { Config, ToolCallRequestInfo } from '@google/gemini-cli-core';
import {
GeminiEventType,
type Config,
type ToolCallRequestInfo,
} from '@google/gemini-cli-core';
import { createMockConfig } from '../utils/testing_utils.js';
import type { ExecutionEventBus } from '@a2a-js/sdk/server';
import { CoderAgentEvent } from '../types.js';
import type { ToolCall } from '@google/gemini-cli-core';
describe('Task', () => {
it('scheduleToolCalls should not modify the input requests array', async () => {
@@ -93,5 +107,167 @@ describe('Task', () => {
}),
);
});
it('should handle Citation event and publish to event bus', async () => {
const mockConfig = createMockConfig();
const mockEventBus: ExecutionEventBus = {
publish: vi.fn(),
on: vi.fn(),
off: vi.fn(),
once: vi.fn(),
removeAllListeners: vi.fn(),
finished: vi.fn(),
};
// @ts-expect-error - Calling private constructor for test purposes.
const task = new Task(
'task-id',
'context-id',
mockConfig as Config,
mockEventBus,
);
const citationText = 'Source: example.com';
const citationEvent = {
type: GeminiEventType.Citation,
value: citationText,
};
await task.acceptAgentMessage(citationEvent);
expect(mockEventBus.publish).toHaveBeenCalledOnce();
const publishedEvent = (mockEventBus.publish as Mock).mock.calls[0][0];
expect(publishedEvent.kind).toBe('status-update');
expect(publishedEvent.taskId).toBe('task-id');
expect(publishedEvent.metadata.coderAgent.kind).toBe(
CoderAgentEvent.CitationEvent,
);
expect(publishedEvent.status.message).toBeDefined();
expect(publishedEvent.status.message.parts).toEqual([
{
kind: 'text',
text: citationText,
},
]);
});
});
describe('_schedulerToolCallsUpdate', () => {
let task: Task;
type SpyInstance = ReturnType<typeof vi.spyOn>;
let setTaskStateAndPublishUpdateSpy: SpyInstance;
beforeEach(() => {
const mockConfig = createMockConfig();
const mockEventBus: ExecutionEventBus = {
publish: vi.fn(),
on: vi.fn(),
off: vi.fn(),
once: vi.fn(),
removeAllListeners: vi.fn(),
finished: vi.fn(),
};
// @ts-expect-error - Calling private constructor
task = new Task(
'task-id',
'context-id',
mockConfig as Config,
mockEventBus,
);
// Spy on the method we want to check calls for
setTaskStateAndPublishUpdateSpy = vi.spyOn(
task,
'setTaskStateAndPublishUpdate',
);
});
afterEach(() => {
vi.restoreAllMocks();
});
it('should set state to input-required when a tool is awaiting approval and none are executing', () => {
const toolCalls = [
{ request: { callId: '1' }, status: 'awaiting_approval' },
] as ToolCall[];
// @ts-expect-error - Calling private method
task._schedulerToolCallsUpdate(toolCalls);
// The last call should be the final state update
expect(setTaskStateAndPublishUpdateSpy).toHaveBeenLastCalledWith(
'input-required',
{ kind: 'state-change' },
undefined,
undefined,
true, // final: true
);
});
it('should NOT set state to input-required if a tool is awaiting approval but another is executing', () => {
const toolCalls = [
{ request: { callId: '1' }, status: 'awaiting_approval' },
{ request: { callId: '2' }, status: 'executing' },
] as ToolCall[];
// @ts-expect-error - Calling private method
task._schedulerToolCallsUpdate(toolCalls);
// It will be called for status updates, but not with final: true
const finalCall = setTaskStateAndPublishUpdateSpy.mock.calls.find(
(call) => call[4] === true,
);
expect(finalCall).toBeUndefined();
});
it('should set state to input-required once an executing tool finishes, leaving one awaiting approval', () => {
const initialToolCalls = [
{ request: { callId: '1' }, status: 'awaiting_approval' },
{ request: { callId: '2' }, status: 'executing' },
] as ToolCall[];
// @ts-expect-error - Calling private method
task._schedulerToolCallsUpdate(initialToolCalls);
// No final call yet
let finalCall = setTaskStateAndPublishUpdateSpy.mock.calls.find(
(call) => call[4] === true,
);
expect(finalCall).toBeUndefined();
// Now, the executing tool finishes. The scheduler would call _resolveToolCall for it.
// @ts-expect-error - Calling private method
task._resolveToolCall('2');
// Then another update comes in for the awaiting tool (e.g., a re-check)
const subsequentToolCalls = [
{ request: { callId: '1' }, status: 'awaiting_approval' },
] as ToolCall[];
// @ts-expect-error - Calling private method
task._schedulerToolCallsUpdate(subsequentToolCalls);
// NOW we should get the final call
finalCall = setTaskStateAndPublishUpdateSpy.mock.calls.find(
(call) => call[4] === true,
);
expect(finalCall).toBeDefined();
expect(finalCall?.[0]).toBe('input-required');
});
it('should NOT set state to input-required if skipFinalTrueAfterInlineEdit is true', () => {
task.skipFinalTrueAfterInlineEdit = true;
const toolCalls = [
{ request: { callId: '1' }, status: 'awaiting_approval' },
] as ToolCall[];
// @ts-expect-error - Calling private method
task._schedulerToolCallsUpdate(toolCalls);
const finalCall = setTaskStateAndPublishUpdateSpy.mock.calls.find(
(call) => call[4] === true,
);
expect(finalCall).toBeUndefined();
});
});
});
+27 -14
View File
@@ -40,7 +40,6 @@ import type {
import { v4 as uuidv4 } from 'uuid';
import { logger } from '../utils/logger.js';
import * as fs from 'node:fs';
import { CoderAgentEvent } from '../types.js';
import type {
CoderAgentMessage,
@@ -50,6 +49,7 @@ import type {
TaskMetadata,
Thought,
ThoughtSummary,
Citation,
} from '../types.js';
import type { PartUnion, Part as genAiPart } from '@google/genai';
@@ -113,7 +113,7 @@ export class Task {
// state managed within the @gemini-cli/core module.
async getMetadata(): Promise<TaskMetadata> {
const toolRegistry = await this.config.getToolRegistry();
const mcpServers = this.config.getMcpServers() || {};
const mcpServers = this.config.getMcpClientManager()?.getMcpServers() || {};
const serverStatuses = getAllMCPServerStatuses();
const servers = Object.keys(mcpServers).map((serverName) => ({
name: serverName,
@@ -373,11 +373,11 @@ export class Task {
// Only send an update if the status has actually changed.
if (hasChanged) {
const message = this.toolStatusMessage(tc, this.id, this.contextId);
const coderAgentMessage: CoderAgentMessage =
tc.status === 'awaiting_approval'
? { kind: CoderAgentEvent.ToolCallConfirmationEvent }
: { kind: CoderAgentEvent.ToolCallUpdateEvent };
const message = this.toolStatusMessage(tc, this.id, this.contextId);
const event = this._createStatusUpdateEvent(
this.taskState,
@@ -404,20 +404,16 @@ export class Task {
const isAwaitingApproval = allPendingStatuses.some(
(status) => status === 'awaiting_approval',
);
const allPendingAreStable = allPendingStatuses.every(
(status) =>
status === 'awaiting_approval' ||
status === 'success' ||
status === 'error' ||
status === 'cancelled',
const isExecuting = allPendingStatuses.some(
(status) => status === 'executing',
);
// 1. Are any pending tool calls awaiting_approval
// 2. Are all pending tool calls in a stable state (i.e. not in validing or executing)
// 3. After an inline edit, the edited tool call will send awaiting_approval THEN scheduled. We wait for the next update in this case.
// The turn is complete and requires user input if at least one tool
// is waiting for the user's decision, and no other tool is actively
// running in the background.
if (
isAwaitingApproval &&
allPendingAreStable &&
!isExecuting &&
!this.skipFinalTrueAfterInlineEdit
) {
this.skipFinalTrueAfterInlineEdit = false;
@@ -440,7 +436,6 @@ export class Task {
onToolCallsUpdate: this._schedulerToolCallsUpdate.bind(this),
getPreferredEditor: () => 'vscode',
config: this.config,
onEditorClose: () => {},
});
return scheduler;
}
@@ -643,6 +638,10 @@ export class Task {
logger.info('[Task] Sending agent thought...');
this._sendThought(event.value, traceId);
break;
case GeminiEventType.Citation:
logger.info('[Task] Received citation from LLM stream.');
this._sendCitation(event.value);
break;
case GeminiEventType.ChatCompressed:
break;
case GeminiEventType.Finished:
@@ -984,4 +983,18 @@ export class Task {
),
);
}
_sendCitation(citation: string) {
if (!citation || citation.trim() === '') {
return;
}
logger.info('[Task] Sending citation to event bus.');
const message = this._createTextMessage(citation);
const citationEvent: Citation = {
kind: CoderAgentEvent.CitationEvent,
};
this.eventBus?.publish(
this._createStatusUpdateEvent(this.taskState, citationEvent, message),
);
}
}
@@ -0,0 +1,118 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, vi, beforeEach } from 'vitest';
import type { Command } from './types.js';
describe('CommandRegistry', () => {
const mockListExtensionsCommandInstance: Command = {
name: 'extensions list',
description: 'Lists all installed extensions.',
execute: vi.fn(),
};
const mockListExtensionsCommand = vi.fn(
() => mockListExtensionsCommandInstance,
);
const mockExtensionsCommandInstance: Command = {
name: 'extensions',
description: 'Manage extensions.',
execute: vi.fn(),
subCommands: [mockListExtensionsCommandInstance],
};
const mockExtensionsCommand = vi.fn(() => mockExtensionsCommandInstance);
beforeEach(async () => {
vi.resetModules();
vi.doMock('./extensions.js', () => ({
ExtensionsCommand: mockExtensionsCommand,
ListExtensionsCommand: mockListExtensionsCommand,
}));
});
it('should register ExtensionsCommand on initialization', async () => {
const { commandRegistry } = await import('./command-registry.js');
expect(mockExtensionsCommand).toHaveBeenCalled();
const command = commandRegistry.get('extensions');
expect(command).toBe(mockExtensionsCommandInstance);
});
it('should register sub commands on initialization', async () => {
const { commandRegistry } = await import('./command-registry.js');
const command = commandRegistry.get('extensions list');
expect(command).toBe(mockListExtensionsCommandInstance);
});
it('get() should return undefined for a non-existent command', async () => {
const { commandRegistry } = await import('./command-registry.js');
const command = commandRegistry.get('non-existent');
expect(command).toBeUndefined();
});
it('register() should register a new command', async () => {
const { commandRegistry } = await import('./command-registry.js');
const mockCommand: Command = {
name: 'test-command',
description: '',
execute: vi.fn(),
};
commandRegistry.register(mockCommand);
const command = commandRegistry.get('test-command');
expect(command).toBe(mockCommand);
});
it('register() should register a nested command', async () => {
const { commandRegistry } = await import('./command-registry.js');
const mockSubSubCommand: Command = {
name: 'test-command-sub-sub',
description: '',
execute: vi.fn(),
};
const mockSubCommand: Command = {
name: 'test-command-sub',
description: '',
execute: vi.fn(),
subCommands: [mockSubSubCommand],
};
const mockCommand: Command = {
name: 'test-command',
description: '',
execute: vi.fn(),
subCommands: [mockSubCommand],
};
commandRegistry.register(mockCommand);
const command = commandRegistry.get('test-command');
const subCommand = commandRegistry.get('test-command-sub');
const subSubCommand = commandRegistry.get('test-command-sub-sub');
expect(command).toBe(mockCommand);
expect(subCommand).toBe(mockSubCommand);
expect(subSubCommand).toBe(mockSubSubCommand);
});
it('register() should not enter an infinite loop with a cyclic command', async () => {
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
const { commandRegistry } = await import('./command-registry.js');
const mockCommand: Command = {
name: 'cyclic-command',
description: '',
subCommands: [],
execute: vi.fn(),
};
mockCommand.subCommands?.push(mockCommand); // Create cycle
commandRegistry.register(mockCommand);
expect(commandRegistry.get('cyclic-command')).toBe(mockCommand);
expect(warnSpy).toHaveBeenCalledWith(
'Command cyclic-command already registered. Skipping.',
);
// If the test finishes, it means we didn't get into an infinite loop.
warnSpy.mockRestore();
});
});
@@ -0,0 +1,39 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { ExtensionsCommand } from './extensions.js';
import type { Command } from './types.js';
class CommandRegistry {
private readonly commands = new Map<string, Command>();
constructor() {
this.register(new ExtensionsCommand());
}
register(command: Command) {
if (this.commands.has(command.name)) {
console.warn(`Command ${command.name} already registered. Skipping.`);
return;
}
this.commands.set(command.name, command);
for (const subCommand of command.subCommands ?? []) {
this.register(subCommand);
}
}
get(commandName: string): Command | undefined {
return this.commands.get(commandName);
}
getAllCommands(): Command[] {
return [...this.commands.values()];
}
}
export const commandRegistry = new CommandRegistry();
@@ -0,0 +1,87 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, vi } from 'vitest';
import { ExtensionsCommand, ListExtensionsCommand } from './extensions.js';
import type { Config } from '@google/gemini-cli-core';
const mockListExtensions = vi.hoisted(() => vi.fn());
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
const original =
await importOriginal<typeof import('@google/gemini-cli-core')>();
return {
...original,
listExtensions: mockListExtensions,
};
});
describe('ExtensionsCommand', () => {
it('should have the correct name', () => {
const command = new ExtensionsCommand();
expect(command.name).toEqual('extensions');
});
it('should have the correct description', () => {
const command = new ExtensionsCommand();
expect(command.description).toEqual('Manage extensions.');
});
it('should have "extensions list" as a subcommand', () => {
const command = new ExtensionsCommand();
expect(command.subCommands.map((c) => c.name)).toContain('extensions list');
});
it('should be a top-level command', () => {
const command = new ExtensionsCommand();
expect(command.topLevel).toBe(true);
});
it('should default to listing extensions', async () => {
const command = new ExtensionsCommand();
const mockConfig = {} as Config;
const mockExtensions = [{ name: 'ext1' }];
mockListExtensions.mockReturnValue(mockExtensions);
const result = await command.execute(mockConfig, []);
expect(result).toEqual({ name: 'extensions list', data: mockExtensions });
expect(mockListExtensions).toHaveBeenCalledWith(mockConfig);
});
});
describe('ListExtensionsCommand', () => {
it('should have the correct name', () => {
const command = new ListExtensionsCommand();
expect(command.name).toEqual('extensions list');
});
it('should call listExtensions with the provided config', async () => {
const command = new ListExtensionsCommand();
const mockConfig = {} as Config;
const mockExtensions = [{ name: 'ext1' }];
mockListExtensions.mockReturnValue(mockExtensions);
const result = await command.execute(mockConfig, []);
expect(result).toEqual({ name: 'extensions list', data: mockExtensions });
expect(mockListExtensions).toHaveBeenCalledWith(mockConfig);
});
it('should return a message when no extensions are installed', async () => {
const command = new ListExtensionsCommand();
const mockConfig = {} as Config;
mockListExtensions.mockReturnValue([]);
const result = await command.execute(mockConfig, []);
expect(result).toEqual({
name: 'extensions list',
data: 'No extensions installed.',
});
expect(mockListExtensions).toHaveBeenCalledWith(mockConfig);
});
});
@@ -0,0 +1,37 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { listExtensions, type Config } from '@google/gemini-cli-core';
import type { Command, CommandExecutionResponse } from './types.js';
export class ExtensionsCommand implements Command {
readonly name = 'extensions';
readonly description = 'Manage extensions.';
readonly subCommands = [new ListExtensionsCommand()];
readonly topLevel = true;
async execute(
config: Config,
_: string[],
): Promise<CommandExecutionResponse> {
return new ListExtensionsCommand().execute(config, _);
}
}
export class ListExtensionsCommand implements Command {
readonly name = 'extensions list';
readonly description = 'Lists all installed extensions.';
async execute(
config: Config,
_: string[],
): Promise<CommandExecutionResponse> {
const extensions = listExtensions(config);
const data = extensions.length ? extensions : 'No extensions installed.';
return { name: this.name, data };
}
}
+28
View File
@@ -0,0 +1,28 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import type { Config } from '@google/gemini-cli-core';
export interface CommandArgument {
readonly name: string;
readonly description: string;
readonly isRequired?: boolean;
}
export interface Command {
readonly name: string;
readonly description: string;
readonly arguments?: CommandArgument[];
readonly subCommands?: Command[];
readonly topLevel?: boolean;
execute(config: Config, args: string[]): Promise<CommandExecutionResponse>;
}
export interface CommandExecutionResponse {
readonly name: string;
readonly data: unknown;
}
+5 -25
View File
@@ -20,8 +20,7 @@ import {
GEMINI_DIR,
DEFAULT_GEMINI_EMBEDDING_MODEL,
DEFAULT_GEMINI_MODEL,
type GeminiCLIExtension,
debugLogger,
type ExtensionLoader,
} from '@google/gemini-cli-core';
import { logger } from '../utils/logger.js';
@@ -30,10 +29,9 @@ import { type AgentSettings, CoderAgentEvent } from '../types.js';
export async function loadConfig(
settings: Settings,
extensions: GeminiCLIExtension[],
extensionLoader: ExtensionLoader,
taskId: string,
): Promise<Config> {
const mcpServers = mergeMcpServers(settings, extensions);
const workspaceDir = process.cwd();
const adcFilePath = process.env['GOOGLE_APPLICATION_CREDENTIALS'];
@@ -53,7 +51,7 @@ export async function loadConfig(
process.env['GEMINI_YOLO_MODE'] === 'true'
? ApprovalMode.YOLO
: ApprovalMode.DEFAULT,
mcpServers,
mcpServers: settings.mcpServers,
cwd: workspaceDir,
telemetry: {
enabled: settings.telemetry?.enabled,
@@ -71,6 +69,7 @@ export async function loadConfig(
},
ideMode: false,
folderTrust: settings.folderTrust === true,
extensionLoader,
};
const fileService = new FileDiscoveryService(workspaceDir);
@@ -79,7 +78,7 @@ export async function loadConfig(
[workspaceDir],
false,
fileService,
extensions,
extensionLoader,
settings.folderTrust === true,
);
configParams.userMemory = memoryContent;
@@ -118,25 +117,6 @@ export async function loadConfig(
return config;
}
export function mergeMcpServers(
settings: Settings,
extensions: GeminiCLIExtension[],
) {
const mcpServers = { ...(settings.mcpServers || {}) };
for (const extension of extensions) {
Object.entries(extension.mcpServers || {}).forEach(([key, server]) => {
if (mcpServers[key]) {
debugLogger.warn(
`Skipping extension MCP config for server with key "${key}" as it already exists.`,
);
return;
}
mcpServers[key] = server;
});
}
return mcpServers;
}
export function setTargetDir(agentSettings: AgentSettings | undefined): string {
const originalCWD = process.cwd();
const targetDir =
+4 -3
View File
@@ -10,6 +10,7 @@ import { homedir } from 'node:os';
import type { MCPServerConfig } from '@google/gemini-cli-core';
import {
debugLogger,
GEMINI_DIR,
getErrorMessage,
type TelemetrySettings,
@@ -97,10 +98,10 @@ export function loadSettings(workspaceDir: string): Settings {
}
if (settingsErrors.length > 0) {
console.error('Errors loading settings:');
debugLogger.error('Errors loading settings:');
for (const error of settingsErrors) {
console.error(` Path: ${error.path}`);
console.error(` Message: ${error.message}`);
debugLogger.error(` Path: ${error.path}`);
debugLogger.error(` Message: ${error.message}`);
}
}
+359 -24
View File
@@ -28,6 +28,7 @@ import {
vi,
} from 'vitest';
import { createApp } from './app.js';
import { commandRegistry } from '../commands/command-registry.js';
import {
assertUniqueFinalEventIsLast,
assertTaskCreationAndWorkingStatus,
@@ -35,6 +36,7 @@ import {
createMockConfig,
} from '../utils/testing_utils.js';
import { MockTool } from '@google/gemini-cli-core';
import type { Command } from '../commands/types.js';
const mockToolConfirmationFn = async () =>
({}) as unknown as ToolCallConfirmationDetails;
@@ -65,6 +67,8 @@ let config: Config;
const getToolRegistrySpy = vi.fn().mockReturnValue(ApprovalMode.DEFAULT);
const getApprovalModeSpy = vi.fn();
const getShellExecutionConfigSpy = vi.fn();
const getExtensionsSpy = vi.fn();
vi.mock('../config/config.js', async () => {
const actual = await vi.importActual('../config/config.js');
return {
@@ -74,6 +78,7 @@ vi.mock('../config/config.js', async () => {
getToolRegistry: getToolRegistrySpy,
getApprovalMode: getApprovalModeSpy,
getShellExecutionConfig: getShellExecutionConfigSpy,
getExtensions: getExtensionsSpy,
});
config = mockConfig as Config;
return config;
@@ -310,7 +315,7 @@ describe('E2E Tests', () => {
expect(workingEvent.kind).toBe('status-update');
expect(workingEvent.status.state).toBe('working');
// State Update: Validate each tool call
// State Update: Validate the first tool call
const toolCallValidateEvent1 = events[3].result as TaskStatusUpdateEvent;
expect(toolCallValidateEvent1.metadata?.['coderAgent']).toMatchObject({
kind: 'tool-call-update',
@@ -323,47 +328,218 @@ describe('E2E Tests', () => {
},
},
]);
const toolCallValidateEvent2 = events[4].result as TaskStatusUpdateEvent;
expect(toolCallValidateEvent2.metadata?.['coderAgent']).toMatchObject({
// --- Assert the event stream ---
// 1. Initial "submitted" status.
expect((events[0].result as TaskStatusUpdateEvent).status.state).toBe(
'submitted',
);
// 2. "working" status after receiving the user prompt.
expect((events[1].result as TaskStatusUpdateEvent).status.state).toBe(
'working',
);
// 3. A "state-change" event from the agent.
expect(events[2].result.metadata?.['coderAgent']).toMatchObject({
kind: 'state-change',
});
// 4. Tool 1 is validating.
const toolCallUpdate1 = events[3].result as TaskStatusUpdateEvent;
expect(toolCallUpdate1.metadata?.['coderAgent']).toMatchObject({
kind: 'tool-call-update',
});
expect(toolCallValidateEvent2.status.message?.parts).toMatchObject([
expect(toolCallUpdate1.status.message?.parts).toMatchObject([
{
data: {
request: { callId: 'test-call-id-1' },
status: 'validating',
request: { callId: 'test-call-id-2' },
},
},
]);
// State Update: Set each tool call to awaiting
const toolCallAwaitEvent1 = events[5].result as TaskStatusUpdateEvent;
expect(toolCallAwaitEvent1.metadata?.['coderAgent']).toMatchObject({
kind: 'tool-call-confirmation',
// 5. Tool 2 is validating.
const toolCallUpdate2 = events[4].result as TaskStatusUpdateEvent;
expect(toolCallUpdate2.metadata?.['coderAgent']).toMatchObject({
kind: 'tool-call-update',
});
expect(toolCallAwaitEvent1.status.message?.parts).toMatchObject([
expect(toolCallUpdate2.status.message?.parts).toMatchObject([
{
data: {
status: 'awaiting_approval',
request: { callId: 'test-call-id-1' },
},
},
]);
const toolCallAwaitEvent2 = events[6].result as TaskStatusUpdateEvent;
expect(toolCallAwaitEvent2.metadata?.['coderAgent']).toMatchObject({
kind: 'tool-call-confirmation',
});
expect(toolCallAwaitEvent2.status.message?.parts).toMatchObject([
{
data: {
status: 'awaiting_approval',
request: { callId: 'test-call-id-2' },
status: 'validating',
},
},
]);
// 6. Tool 1 is awaiting approval.
const toolCallAwaitEvent = events[5].result as TaskStatusUpdateEvent;
expect(toolCallAwaitEvent.metadata?.['coderAgent']).toMatchObject({
kind: 'tool-call-confirmation',
});
expect(toolCallAwaitEvent.status.message?.parts).toMatchObject([
{
data: {
request: { callId: 'test-call-id-1' },
status: 'awaiting_approval',
},
},
]);
// 7. The final event is "input-required".
const finalEvent = events[6].result as TaskStatusUpdateEvent;
expect(finalEvent.final).toBe(true);
expect(finalEvent.status.state).toBe('input-required');
// The scheduler now waits for approval, so no more events are sent.
assertUniqueFinalEventIsLast(events);
expect(events.length).toBe(7);
});
it('should handle multiple tool calls sequentially in YOLO mode', async () => {
// Set YOLO mode to auto-approve tools and test sequential execution.
getApprovalModeSpy.mockReturnValue(ApprovalMode.YOLO);
// First call yields the tool request
sendMessageStreamSpy.mockImplementationOnce(async function* () {
yield* [
{
type: GeminiEventType.ToolCallRequest,
value: {
callId: 'test-call-id-1',
name: 'test-tool-1',
args: {},
},
},
{
type: GeminiEventType.ToolCallRequest,
value: {
callId: 'test-call-id-2',
name: 'test-tool-2',
args: {},
},
},
];
});
// Subsequent calls yield nothing, as the tools will "succeed".
sendMessageStreamSpy.mockImplementation(async function* () {
yield* [{ type: 'content', value: 'All tools executed.' }];
});
const mockTool1 = new MockTool({
name: 'test-tool-1',
displayName: 'Test Tool 1',
shouldConfirmExecute: vi.fn(mockToolConfirmationFn),
execute: vi
.fn()
.mockResolvedValue({ llmContent: 'tool 1 done', returnDisplay: '' }),
});
const mockTool2 = new MockTool({
name: 'test-tool-2',
displayName: 'Test Tool 2',
shouldConfirmExecute: vi.fn(mockToolConfirmationFn),
execute: vi
.fn()
.mockResolvedValue({ llmContent: 'tool 2 done', returnDisplay: '' }),
});
getToolRegistrySpy.mockReturnValue({
getAllTools: vi.fn().mockReturnValue([mockTool1, mockTool2]),
getToolsByServer: vi.fn().mockReturnValue([]),
getTool: vi.fn().mockImplementation((name: string) => {
if (name === 'test-tool-1') return mockTool1;
if (name === 'test-tool-2') return mockTool2;
return undefined;
}),
});
const agent = request.agent(app);
const res = await agent
.post('/')
.send(
createStreamMessageRequest(
'run two tools',
'a2a-multi-tool-test-message',
),
)
.set('Content-Type', 'application/json')
.expect(200);
const events = streamToSSEEvents(res.text);
assertTaskCreationAndWorkingStatus(events);
// --- Assert the sequential execution flow ---
const eventStream = events.slice(2).map((e) => {
const update = e.result as TaskStatusUpdateEvent;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const agentData = update.metadata?.['coderAgent'] as any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const toolData = update.status.message?.parts[0] as any;
if (!toolData) {
return { kind: agentData.kind };
}
return {
kind: agentData.kind,
status: toolData.data?.status,
callId: toolData.data?.request.callId,
};
});
const expectedFlow = [
// Initial state change
{ kind: 'state-change', status: undefined, callId: undefined },
// Tool 1 Lifecycle
{
kind: 'tool-call-update',
status: 'validating',
callId: 'test-call-id-1',
},
{
kind: 'tool-call-update',
status: 'scheduled',
callId: 'test-call-id-1',
},
{
kind: 'tool-call-update',
status: 'executing',
callId: 'test-call-id-1',
},
{
kind: 'tool-call-update',
status: 'success',
callId: 'test-call-id-1',
},
// Tool 2 Lifecycle
{
kind: 'tool-call-update',
status: 'validating',
callId: 'test-call-id-2',
},
{
kind: 'tool-call-update',
status: 'scheduled',
callId: 'test-call-id-2',
},
{
kind: 'tool-call-update',
status: 'executing',
callId: 'test-call-id-2',
},
{
kind: 'tool-call-update',
status: 'success',
callId: 'test-call-id-2',
},
// Final updates
{ kind: 'state-change', status: undefined, callId: undefined },
{ kind: 'text-content', status: undefined, callId: undefined },
];
// Use `toContainEqual` for flexibility if other events are interspersed.
expect(eventStream).toEqual(expect.arrayContaining(expectedFlow));
assertUniqueFinalEventIsLast(events);
expect(events.length).toBe(8);
});
it('should handle tool calls that do not require approval', async () => {
@@ -652,4 +828,163 @@ describe('E2E Tests', () => {
expect(thoughtEvent.kind).toBe('status-update');
expect(thoughtEvent.metadata?.['traceId']).toBe(traceId);
});
describe('/listCommands', () => {
it('should return a list of top-level commands', async () => {
const mockCommands = [
{
name: 'test-command',
description: 'A test command',
topLevel: true,
arguments: [{ name: 'arg1', description: 'Argument 1' }],
subCommands: [
{
name: 'sub-command',
description: 'A sub command',
topLevel: false,
execute: vi.fn(),
},
],
execute: vi.fn(),
},
{
name: 'another-command',
description: 'Another test command',
topLevel: true,
execute: vi.fn(),
},
{
name: 'not-top-level',
description: 'Not a top level command',
topLevel: false,
execute: vi.fn(),
},
];
const getAllCommandsSpy = vi
.spyOn(commandRegistry, 'getAllCommands')
.mockReturnValue(mockCommands);
const agent = request.agent(app);
const res = await agent.get('/listCommands').expect(200);
expect(res.body).toEqual({
commands: [
{
name: 'test-command',
description: 'A test command',
arguments: [{ name: 'arg1', description: 'Argument 1' }],
subCommands: [
{
name: 'sub-command',
description: 'A sub command',
arguments: [],
subCommands: [],
},
],
},
{
name: 'another-command',
description: 'Another test command',
arguments: [],
subCommands: [],
},
],
});
expect(getAllCommandsSpy).toHaveBeenCalledOnce();
getAllCommandsSpy.mockRestore();
});
it('should handle cyclic commands gracefully', async () => {
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
const cyclicCommand: Command = {
name: 'cyclic-command',
description: 'A cyclic command',
topLevel: true,
execute: vi.fn(),
subCommands: [],
};
cyclicCommand.subCommands?.push(cyclicCommand); // Create cycle
const getAllCommandsSpy = vi
.spyOn(commandRegistry, 'getAllCommands')
.mockReturnValue([cyclicCommand]);
const agent = request.agent(app);
const res = await agent.get('/listCommands').expect(200);
expect(res.body.commands[0].name).toBe('cyclic-command');
expect(res.body.commands[0].subCommands).toEqual([]);
expect(warnSpy).toHaveBeenCalledWith(
'Command cyclic-command already inserted in the response, skipping',
);
getAllCommandsSpy.mockRestore();
warnSpy.mockRestore();
});
});
describe('/executeCommand', () => {
const mockExtensions = [{ name: 'test-extension', version: '0.0.1' }];
beforeEach(() => {
getExtensionsSpy.mockReturnValue(mockExtensions);
});
afterEach(() => {
getExtensionsSpy.mockClear();
});
it('should return extensions for valid command', async () => {
const agent = request.agent(app);
const res = await agent
.post('/executeCommand')
.send({ command: 'extensions list', args: [] })
.set('Content-Type', 'application/json')
.expect(200);
expect(res.body).toEqual({
name: 'extensions list',
data: mockExtensions,
});
expect(getExtensionsSpy).toHaveBeenCalled();
});
it('should return 404 for invalid command', async () => {
const agent = request.agent(app);
const res = await agent
.post('/executeCommand')
.send({ command: 'invalid command' })
.set('Content-Type', 'application/json')
.expect(404);
expect(res.body.error).toBe('Command not found: invalid command');
expect(getExtensionsSpy).not.toHaveBeenCalled();
});
it('should return 400 for missing command', async () => {
const agent = request.agent(app);
await agent
.post('/executeCommand')
.send({ args: [] })
.set('Content-Type', 'application/json')
.expect(400);
expect(getExtensionsSpy).not.toHaveBeenCalled();
});
it('should return 400 if args is not an array', async () => {
const agent = request.agent(app);
const res = await agent
.post('/executeCommand')
.send({ command: 'extensions.list', args: 'not-an-array' })
.set('Content-Type', 'application/json')
.expect(400);
expect(res.body.error).toBe('"args" field must be an array.');
expect(getExtensionsSpy).not.toHaveBeenCalled();
});
});
});
+98
View File
@@ -16,6 +16,19 @@ import type { AgentSettings } from '../types.js';
import { GCSTaskStore, NoOpTaskStore } from '../persistence/gcs.js';
import { CoderAgentExecutor } from '../agent/executor.js';
import { requestStorage } from './requestStorage.js';
import { loadConfig, loadEnvironment, setTargetDir } from '../config/config.js';
import { loadSettings } from '../config/settings.js';
import { loadExtensions } from '../config/extension.js';
import { commandRegistry } from '../commands/command-registry.js';
import { SimpleExtensionLoader } from '@google/gemini-cli-core';
import type { Command, CommandArgument } from '../commands/types.js';
type CommandResponse = {
name: string;
description: string;
arguments: CommandArgument[];
subCommands: CommandResponse[];
};
const coderAgentCard: AgentCard = {
name: 'Gemini SDLC Agent',
@@ -61,6 +74,17 @@ export function updateCoderAgentCardUrl(port: number) {
export async function createApp() {
try {
// Load the server configuration once on startup.
const workspaceRoot = setTargetDir(undefined);
loadEnvironment();
const settings = loadSettings(workspaceRoot);
const extensions = loadExtensions(workspaceRoot);
const config = await loadConfig(
settings,
new SimpleExtensionLoader(extensions),
'a2a-server',
);
// loadEnvironment() is called within getConfig now
const bucketName = process.env['GCS_BUCKET_NAME'];
let taskStoreForExecutor: TaskStore;
@@ -119,6 +143,80 @@ export async function createApp() {
}
});
expressApp.post('/executeCommand', async (req, res) => {
try {
const { command, args } = req.body;
if (typeof command !== 'string') {
return res.status(400).json({ error: 'Invalid "command" field.' });
}
if (args && !Array.isArray(args)) {
return res
.status(400)
.json({ error: '"args" field must be an array.' });
}
const commandToExecute = commandRegistry.get(command);
if (!commandToExecute) {
return res
.status(404)
.json({ error: `Command not found: ${command}` });
}
const result = await commandToExecute.execute(config, args ?? []);
return res.status(200).json(result);
} catch (e) {
logger.error('Error executing /executeCommand:', e);
const errorMessage =
e instanceof Error ? e.message : 'Unknown error executing command';
return res.status(500).json({ error: errorMessage });
}
});
expressApp.get('/listCommands', (req, res) => {
try {
const transformCommand = (
command: Command,
visited: string[],
): CommandResponse | undefined => {
const commandName = command.name;
if (visited.includes(commandName)) {
console.warn(
`Command ${commandName} already inserted in the response, skipping`,
);
return undefined;
}
return {
name: command.name,
description: command.description,
arguments: command.arguments ?? [],
subCommands: (command.subCommands ?? [])
.map((subCommand) =>
transformCommand(subCommand, visited.concat(commandName)),
)
.filter(
(subCommand): subCommand is CommandResponse => !!subCommand,
),
};
};
const commands = commandRegistry
.getAllCommands()
.filter((command) => command.topLevel)
.map((command) => transformCommand(command, []));
return res.status(200).json({ commands });
} catch (e) {
logger.error('Error executing /listCommands:', e);
const errorMessage =
e instanceof Error ? e.message : 'Unknown error listing commands';
return res.status(500).json({ error: errorMessage });
}
});
expressApp.get('/tasks/metadata', async (req, res) => {
// This endpoint is only meaningful if the task store is in-memory.
if (!(taskStoreForExecutor instanceof InMemoryTaskStore)) {
+57 -10
View File
@@ -6,7 +6,6 @@
import { Storage } from '@google-cloud/storage';
import * as fse from 'fs-extra';
import { promises as fsPromises, createReadStream } from 'node:fs';
import * as tar from 'tar';
import { gzipSync, gunzipSync } from 'node:zlib';
import { v4 as uuidv4 } from 'uuid';
@@ -21,12 +20,18 @@ import * as configModule from '../config/config.js';
import { getPersistedState, METADATA_KEY } from '../types.js';
// Mock dependencies
const fsMocks = vi.hoisted(() => ({
readdir: vi.fn(),
createReadStream: vi.fn(),
}));
vi.mock('@google-cloud/storage');
vi.mock('fs-extra', () => ({
pathExists: vi.fn(),
readdir: vi.fn(),
remove: vi.fn(),
ensureDir: vi.fn(),
createReadStream: vi.fn(),
}));
vi.mock('node:fs', async () => {
const actual = await vi.importActual<typeof import('node:fs')>('node:fs');
@@ -34,12 +39,37 @@ vi.mock('node:fs', async () => {
...actual,
promises: {
...actual.promises,
readdir: vi.fn(),
readdir: fsMocks.readdir,
},
createReadStream: vi.fn(),
createReadStream: fsMocks.createReadStream,
};
});
vi.mock('fs', async () => {
const actual = await vi.importActual<typeof import('node:fs')>('node:fs');
return {
...actual,
promises: {
...actual.promises,
readdir: fsMocks.readdir,
},
createReadStream: fsMocks.createReadStream,
};
});
vi.mock('tar', async () => {
const actualFs = await vi.importActual<typeof import('node:fs')>('node:fs');
return {
c: vi.fn(({ file }) => {
if (file) {
actualFs.writeFileSync(file, Buffer.from('dummy tar content'));
}
return Promise.resolve();
}),
x: vi.fn().mockResolvedValue(undefined),
t: vi.fn().mockResolvedValue(undefined),
r: vi.fn().mockResolvedValue(undefined),
u: vi.fn().mockResolvedValue(undefined),
};
});
vi.mock('tar');
vi.mock('zlib');
vi.mock('uuid');
vi.mock('../utils/logger.js', () => ({
@@ -66,7 +96,7 @@ vi.mock('../types.js', async (importOriginal) => {
const mockStorage = Storage as MockedClass<typeof Storage>;
const mockFse = fse as Mocked<typeof fse>;
const mockCreateReadStream = createReadStream as Mock;
const mockCreateReadStream = fsMocks.createReadStream;
const mockTar = tar as Mocked<typeof tar>;
const mockGzipSync = gzipSync as Mock;
const mockGunzipSync = gunzipSync as Mock;
@@ -76,10 +106,19 @@ const mockGetPersistedState = getPersistedState as Mock;
const TEST_METADATA_KEY = METADATA_KEY || '__persistedState';
type MockWriteStream = {
emit: Mock<(event: string, ...args: unknown[]) => boolean>;
removeListener: Mock<
(event: string, cb: (error?: Error | null) => void) => MockWriteStream
>;
once: Mock<
(event: string, cb: (error?: Error | null) => void) => MockWriteStream
>;
on: Mock<
(event: string, cb: (error?: Error | null) => void) => MockWriteStream
>;
destroy: Mock<() => void>;
write: Mock<(chunk: unknown, encoding?: unknown, cb?: unknown) => boolean>;
end: Mock<(cb?: unknown) => void>;
destroyed: boolean;
};
@@ -114,11 +153,18 @@ describe('GCSTaskStore', () => {
bucketName = 'test-bucket';
mockWriteStream = {
emit: vi.fn().mockReturnValue(true),
removeListener: vi.fn().mockReturnValue(mockWriteStream),
on: vi.fn((event, cb) => {
if (event === 'finish') setTimeout(cb, 0); // Simulate async finish
return mockWriteStream;
}),
once: vi.fn((event, cb) => {
if (event === 'finish') setTimeout(cb, 0); // Simulate async finish return mockWriteStream;
}),
destroy: vi.fn(),
write: vi.fn().mockReturnValue(true),
end: vi.fn(),
destroyed: false,
};
@@ -149,14 +195,16 @@ describe('GCSTaskStore', () => {
_taskState: 'submitted',
});
(fse.pathExists as Mock).mockResolvedValue(true);
(fsPromises.readdir as Mock).mockResolvedValue(['file1.txt']);
mockTar.c.mockResolvedValue(undefined);
mockTar.x.mockResolvedValue(undefined);
fsMocks.readdir.mockResolvedValue(['file1.txt']);
mockFse.remove.mockResolvedValue(undefined);
mockFse.ensureDir.mockResolvedValue(undefined);
mockGzipSync.mockReturnValue(Buffer.from('compressed'));
mockGunzipSync.mockReturnValue(Buffer.from('{}'));
mockCreateReadStream.mockReturnValue({ on: vi.fn(), pipe: vi.fn() });
mockFse.createReadStream.mockReturnValue({
on: vi.fn(),
pipe: vi.fn(),
} as unknown as import('node:fs').ReadStream);
});
describe('Constructor & Initialization', () => {
@@ -201,13 +249,12 @@ describe('GCSTaskStore', () => {
metadata: {},
};
it.skip('should save metadata and workspace', async () => {
it('should save metadata and workspace', async () => {
const store = new GCSTaskStore(bucketName);
await store.save(mockTask);
expect(mockFile.save).toHaveBeenCalledTimes(1);
expect(mockTar.c).toHaveBeenCalledTimes(1);
expect(mockCreateReadStream).toHaveBeenCalledTimes(1);
expect(mockFse.remove).toHaveBeenCalledTimes(1);
expect(logger.info).toHaveBeenCalledWith(
expect.stringContaining('metadata saved to GCS'),
+10 -1
View File
@@ -37,6 +37,10 @@ export enum CoderAgentEvent {
* An event that contains a thought from the agent.
*/
ThoughtEvent = 'thought',
/**
* An event that contains citation from the agent.
*/
CitationEvent = 'citation',
}
export interface AgentSettings {
@@ -64,6 +68,10 @@ export interface Thought {
kind: CoderAgentEvent.ThoughtEvent;
}
export interface Citation {
kind: CoderAgentEvent.CitationEvent;
}
export type ThoughtSummary = {
subject: string;
description: string;
@@ -80,7 +88,8 @@ export type CoderAgentMessage =
| ToolCallUpdate
| TextContent
| StateChange
| Thought;
| Thought
| Citation;
export interface TaskMetadata {
id: string;
@@ -53,6 +53,7 @@ export function createMockConfig(
getEnableMessageBusIntegration: vi.fn().mockReturnValue(false),
getMessageBus: vi.fn(),
getPolicyEngine: vi.fn(),
getEnableExtensionReloading: vi.fn().mockReturnValue(false),
...overrides,
} as unknown as Config;
-1
View File
@@ -11,7 +11,6 @@ export default defineConfig({
test: {
include: ['**/*.{test,spec}.?(c|m)[jt]s?(x)'],
exclude: ['**/node_modules/**', '**/dist/**'],
environment: 'jsdom',
globals: true,
reporters: ['default', 'junit'],
silent: true,
@@ -0,0 +1,165 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { useState, useEffect, useRef } from 'react';
import { render, Box, Text, useInput, useStdout } from 'ink';
import {
ScrollableList,
type ScrollableListRef,
} from '../src/ui/components/shared/ScrollableList.js';
import { ScrollProvider } from '../src/ui/contexts/ScrollProvider.js';
import { MouseProvider } from '../src/ui/contexts/MouseContext.js';
import { KeypressProvider } from '../src/ui/contexts/KeypressContext.js';
import {
enableMouseEvents,
disableMouseEvents,
} from '../src/ui/utils/mouse.js';
interface Item {
id: string;
title: string;
}
const getLorem = (index: number) =>
Array(10)
.fill(null)
.map(() => 'lorem ipsum '.repeat((index % 3) + 1).trim())
.join('\n');
const Demo = () => {
const { stdout } = useStdout();
const [size, setSize] = useState({
columns: stdout.columns,
rows: stdout.rows,
});
useEffect(() => {
const onResize = () => {
setSize({
columns: stdout.columns,
rows: stdout.rows,
});
};
stdout.on('resize', onResize);
return () => {
stdout.off('resize', onResize);
};
}, [stdout]);
const [items, setItems] = useState<Item[]>(() =>
Array.from({ length: 1000 }, (_, i) => ({
id: String(i),
title: `Item ${i + 1}`,
})),
);
const listRef = useRef<ScrollableListRef<Item>>(null);
useInput((input, key) => {
if (input === 'a' || input === 'A') {
setItems((prev) => [
...prev,
{ id: String(prev.length), title: `Item ${prev.length + 1}` },
]);
}
if ((input === 'e' || input === 'E') && !key.ctrl) {
setItems((prev) => {
if (prev.length === 0) return prev;
const lastIndex = prev.length - 1;
const lastItem = prev[lastIndex]!;
const newItem = { ...lastItem, title: lastItem.title + 'e' };
return [...prev.slice(0, lastIndex), newItem];
});
}
if (key.ctrl && input === 'e') {
listRef.current?.scrollToEnd();
}
// Let Ink handle Ctrl+C via exitOnCtrlC (default true) or handle explicitly if needed.
// For alternate buffer, explicit handling is often safer for cleanup.
if (key.escape || (key.ctrl && input === 'c')) {
process.exit(0);
}
});
return (
<MouseProvider mouseEventsEnabled={true}>
<KeypressProvider>
<ScrollProvider>
<Box
flexDirection="column"
width={size.columns}
height={size.rows - 1}
padding={1}
>
<Text>
Press &apos;A&apos; to add an item. Press &apos;E&apos; to edit
last item. Press &apos;Ctrl+E&apos; to scroll to end. Press
&apos;Esc&apos; to exit. Mouse wheel or Shift+Up/Down to scroll.
</Text>
<Box flexGrow={1} borderStyle="round" borderColor="cyan">
<ScrollableList
ref={listRef}
data={items}
renderItem={({ item, index }) => (
<Box flexDirection="column" paddingBottom={2}>
<Box
sticky
flexDirection="column"
width={size.columns - 2}
opaque
stickyChildren={
<Box
flexDirection="column"
width={size.columns - 2}
opaque
>
<Text>{item.title}</Text>
<Box
borderStyle="single"
borderTop={true}
borderBottom={false}
borderLeft={false}
borderRight={false}
borderColor="gray"
/>
</Box>
}
>
<Text>{item.title}</Text>
</Box>
<Text color="gray">{getLorem(index)}</Text>
</Box>
)}
estimatedItemHeight={() => 14}
keyExtractor={(item) => item.id}
hasFocus={true}
initialScrollIndex={Number.MAX_SAFE_INTEGER}
initialScrollOffsetInIndex={Number.MAX_SAFE_INTEGER}
/>
</Box>
<Text>Count: {items.length}</Text>
</Box>
</ScrollProvider>
</KeypressProvider>
</MouseProvider>
);
};
// Enable mouse reporting before rendering
enableMouseEvents();
// Ensure cleanup happens on exit
process.on('exit', () => {
disableMouseEvents();
});
// Handle SIGINT explicitly to ensure cleanup runs if Ink doesn't catch it in time
process.on('SIGINT', () => {
process.exit(0);
});
render(<Demo />, { alternateBuffer: true });
+9 -6
View File
@@ -8,23 +8,26 @@
import './src/gemini.js';
import { main } from './src/gemini.js';
import { debugLogger, FatalError } from '@google/gemini-cli-core';
import { FatalError, writeToStderr } from '@google/gemini-cli-core';
import { runExitCleanup } from './src/utils/cleanup.js';
// --- Global Entry Point ---
main().catch((error) => {
main().catch(async (error) => {
await runExitCleanup();
if (error instanceof FatalError) {
let errorMessage = error.message;
if (!process.env['NO_COLOR']) {
errorMessage = `\x1b[31m${errorMessage}\x1b[0m`;
}
debugLogger.error(errorMessage);
writeToStderr(errorMessage + '\n');
process.exit(error.exitCode);
}
debugLogger.error('An unexpected critical error occurred:');
writeToStderr('An unexpected critical error occurred:');
if (error instanceof Error) {
debugLogger.error(error.stack);
writeToStderr(error.stack + '\n');
} else {
debugLogger.error(String(error));
writeToStderr(String(error) + '\n');
}
process.exit(1);
});
+17 -16
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli",
"version": "0.11.2",
"version": "0.18.4",
"description": "Gemini CLI",
"repository": {
"type": "git",
@@ -25,39 +25,42 @@
"dist"
],
"config": {
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.11.2"
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.18.4"
},
"dependencies": {
"@google/gemini-cli-core": "file:../core",
"@google/genai": "1.16.0",
"@google/genai": "1.30.0",
"@iarna/toml": "^2.2.5",
"@modelcontextprotocol/sdk": "^1.15.1",
"@modelcontextprotocol/sdk": "^1.23.0",
"@types/update-notifier": "^6.0.8",
"ansi-regex": "^6.2.2",
"clipboardy": "^5.0.0",
"command-exists": "^1.2.9",
"comment-json": "^4.2.5",
"diff": "^7.0.0",
"dotenv": "^17.1.0",
"extract-zip": "^2.0.1",
"fzf": "^0.5.2",
"glob": "^10.4.5",
"glob": "^12.0.0",
"highlight.js": "^11.11.1",
"ink": "^6.2.3",
"ink": "npm:@jrichman/ink@6.4.5",
"ink-gradient": "^3.0.0",
"ink-spinner": "^5.0.0",
"latest-version": "^9.0.0",
"lowlight": "^3.3.0",
"mnemonist": "^0.40.3",
"open": "^10.1.2",
"react": "^19.1.0",
"prompts": "^2.4.2",
"react": "^19.2.0",
"read-package-up": "^11.0.0",
"shell-quote": "^1.8.3",
"simple-git": "^3.28.0",
"string-width": "^7.1.0",
"string-width": "^8.1.0",
"strip-ansi": "^7.1.0",
"strip-json-comments": "^3.1.1",
"tar": "^7.5.1",
"tar": "^7.5.2",
"tinygradient": "^1.1.5",
"undici": "^7.10.0",
"extract-zip": "^2.0.1",
"update-notifier": "^7.3.1",
"wrap-ansi": "9.0.2",
"yargs": "^17.7.2",
"zod": "^3.23.8"
@@ -65,23 +68,21 @@
"devDependencies": {
"@babel/runtime": "^7.27.6",
"@google/gemini-cli-test-utils": "file:../test-utils",
"@testing-library/react": "^16.3.0",
"@types/archiver": "^6.0.3",
"@types/command-exists": "^1.2.3",
"@types/diff": "^7.0.2",
"@types/dotenv": "^6.1.1",
"@types/node": "^20.11.24",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"@types/react": "^19.2.0",
"@types/react-dom": "^19.2.0",
"@types/semver": "^7.7.0",
"@types/shell-quote": "^1.7.5",
"@types/tar": "^6.1.13",
"@types/yargs": "^17.0.32",
"archiver": "^7.0.1",
"ink-testing-library": "^4.0.0",
"jsdom": "^26.1.0",
"pretty-format": "^30.0.2",
"react-dom": "^19.1.0",
"react-dom": "^19.2.0",
"typescript": "^5.3.3",
"vitest": "^3.1.1"
},
@@ -0,0 +1,8 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`runNonInteractive > should write a single newline between sequential text outputs from the model 1`] = `
"Use mock tool
Use mock tool again
Finished.
"
`;

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