Compare commits

...

314 Commits

Author SHA1 Message Date
gemini-cli-robot 997a5dbdd1 chore(release): v0.24.2 2026-01-16 20:41:37 +00:00
gemini-cli-robot 33f2b3a4e3 fix(patch): cherry-pick cfdc4cf to release/v0.24.0-pr-16759 to patch version v0.24.0 and create version 0.24.1 (#16865)
Co-authored-by: christine betts <chrstn@uw.edu>
Co-authored-by: Tommaso Sciortino <sciortino@gmail.com>
2026-01-16 11:19:45 -08:00
gemini-cli-robot b56a111594 chore(release): v0.24.0 2026-01-14 13:22:02 +00:00
gemini-cli-robot 48ee9bb308 chore(release): v0.24.0-preview.3 2026-01-14 06:24:32 +00:00
gemini-cli-robot 4b2701195a fix(patch): cherry-pick eda47f5 to release/v0.24.0-preview.2-pr-16557 [CONFLICTS] (#16577)
Co-authored-by: Abhi <43648792+abhipatel12@users.noreply.github.com>
Co-authored-by: Sandy Tao <sandytao520@icloud.com>
2026-01-14 14:04:14 +08:00
gemini-cli-robot df72e3af3d chore(release): v0.24.0-preview.2 2026-01-13 23:34:59 +00:00
gemini-cli-robot 334b813d81 fix(patch): cherry-pick 356f76e to release/v0.24.0-preview.1-pr-16252 to patch version v0.24.0-preview.1 and create version 0.24.0-preview.2 (#16552)
Co-authored-by: Gal Zahavi <38544478+galz10@users.noreply.github.com>
Co-authored-by: Abhi <43648792+abhipatel12@users.noreply.github.com>
2026-01-13 22:40:08 +00:00
gemini-cli-robot 314f67a326 chore(release): v0.24.0-preview.1 2026-01-13 05:44:29 +00:00
gemini-cli-robot 0f0d1d8fc0 fix(patch): cherry-pick b54e688 to release/v0.24.0-preview.0-pr-16284 to patch version v0.24.0-preview.0 and create version 0.24.0-preview.1 (#16466)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2026-01-13 11:43:02 +08:00
gemini-cli-robot cb15a238fe chore(release): v0.24.0-preview.0 2026-01-07 01:43:58 +00:00
N. Taylor Mullen 59a18e710d Agent Skills: Initial Documentation & Tutorial (#15869) 2026-01-07 00:59:56 +00:00
Krushna Korade 5f027cb63a fix: hide broken skills object from settings dialog (#15766) 2026-01-07 00:55:13 +00:00
Christian Gunderman 4086abf375 Fix test. (#16011) 2026-01-07 00:25:16 +00:00
Jerop Kipruto 8f5bf33eac ci(github-actions): triage all new issues automatically (#16018) 2026-01-07 00:15:00 +00:00
Jerop Kipruto 7eeb7bd74c fix: limit scheduled issue triage queries to prevent argument list too long error (#16021) 2026-01-07 00:03:23 +00:00
Adam Weidman 96b9be3ec4 feat(agents): add support for remote agents (#16013) 2026-01-06 23:45:05 +00:00
joshualitt 1e31427da8 Remove trailing whitespace in yaml. (#16036) 2026-01-06 15:41:58 -08:00
Bryan Morgan 7feb2f8f42 Add 'reopened' type to issue labeling workflow 2026-01-06 18:17:12 -05:00
Bryan Morgan 2122604b32 Refactor parent issue check to use URLs 2026-01-06 18:00:39 -05:00
Bryan Morgan d4b4aede2f Add debugging logs for issue parent checks
Added debugging logs to inspect issue object and parent information.
2026-01-06 17:56:39 -05:00
Bryan Morgan 4b5c044272 Fix label-backlog-child-issues workflow logic 2026-01-06 17:48:08 -05:00
Shreya Keshive 2fe45834dd feat(admin): Introduce remote admin settings & implement secureModeEnabled/mcpEnabled (#15935) 2026-01-06 21:38:07 +00:00
joshualitt 56092bd782 feat(hooks): Add a hooks.enabled setting. (#15933) 2026-01-06 21:33:37 +00:00
Jack Wotherspoon c31f05356a fix: image token estimation (#16004) 2026-01-06 21:19:29 +00:00
Abhi 61dbab03e0 feat(ui): add visual indicators for hook execution (#15408) 2026-01-06 20:52:12 +00:00
Bryan Morgan 86b5995f12 Add workflow to label child issues for rollup (#16002) 2026-01-06 16:00:47 -05:00
Tommaso Sciortino d2849fda8a properly disable keyboard modes on exit (#16006) 2026-01-06 20:11:43 +00:00
Tommaso Sciortino a61fb058b7 fix: writeTodo construction (#16014) 2026-01-06 20:08:25 +00:00
christine betts 7edd803034 Fix settings command fallback (#15926) 2026-01-06 19:50:27 +00:00
N. Taylor Mullen 0c54136244 Agent Skills: Add skill directory to WorkspaceContext upon activation (#15870) 2026-01-06 19:24:37 +00:00
Ishaan Gupta cce4574143 Use GetOperation to poll for OnboardUser completion (#15827)
Co-authored-by: Vedant Mahajan <vedant.04.mahajan@gmail.com>
Co-authored-by: Tommaso Sciortino <sciortino@gmail.com>
2026-01-06 19:08:59 +00:00
Sehoon Shon 9172e28315 Add description for each settings item in /settings (#15936) 2026-01-06 19:06:15 +00:00
Gal Zahavi 6f4b2ad0b9 fix: default folder trust to untrusted for enhanced security (#15943) 2026-01-06 18:09:09 +00:00
N. Taylor Mullen 9a3ff6510f feat(policy): allow 'modes' in user and admin policies (#15977) 2026-01-06 17:56:50 +00:00
christine betts 4c67eef0f2 Inform user of missing settings on extensions update (#15944)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-01-06 17:26:01 +00:00
ppergame 7d4f97de7a fix(core): use correct interactive check for system prompt (#15020)
Co-authored-by: Gal Zahavi <38544478+galz10@users.noreply.github.com>
Co-authored-by: Billy Biggs <bbiggs@google.com>
2026-01-06 16:21:36 +00:00
Sehoon Shon cbd2eee9c4 remove manual string when displaying manual model in the footer (#15967) 2026-01-06 04:07:44 +00:00
Tommaso Sciortino 384fb6a465 Add setting to support OSC 52 paste (#15336) 2026-01-06 00:11:50 +00:00
N. Taylor Mullen 2cb33b2f76 Agent Skills: Implement /skills reload (#15865) 2026-01-05 23:12:51 +00:00
Tommaso Sciortino 8f0324d868 fix(cli): resolve paste issue on Windows terminals. (#15932) 2026-01-05 22:46:23 +00:00
Christian Gunderman fd7b6bf40a Fix failing unit tests (#15940) 2026-01-05 21:41:35 +00:00
Tommaso Sciortino ed8bad8c26 Fix order of preflight (#15941) 2026-01-05 21:32:02 +00:00
Christian Gunderman 6d1e27633a Support context injection via SessionStart hook. (#15746) 2026-01-05 21:27:53 +00:00
Manoj Naik 2da911e4a0 fix: prevent /copy crash on Windows by skipping /dev/tty (#15657)
Co-authored-by: Jack Wotherspoon <jackwoth@google.com>
2026-01-05 20:33:03 +00:00
Sehoon Shon e5d183031a Opt-in to persist model from /model (#15820) 2026-01-05 20:31:13 +00:00
Abhi b13c6b57ae chore: rename smart-edit to edit (#15923) 2026-01-05 20:25:54 +00:00
Dev Randalpura 3c92666ec2 Make default settings apply (#15354) 2026-01-05 20:00:20 +00:00
Vedant Mahajan dc6dda5c37 fix: avoid SDK warning by not accessing .text getter in logging (#15706)
Co-authored-by: Ishaan Gupta <ishaankone@gmail.com>
2026-01-05 19:03:00 +00:00
Christian Gunderman 3997c7ff80 Fix terminal hang when user exits browser without logging in (#15748) 2026-01-05 19:02:55 +00:00
Abhi b4b49e7029 refactor(core): Extract and integrate ToolExecutor (#15900) 2026-01-05 05:48:41 +00:00
N. Taylor Mullen 615b218ff7 fix(cli): mock fs.readdir in consent tests for Windows compatibility (#15904) 2026-01-04 21:19:33 -08:00
Abhi f3625aab13 refactor: consolidate EditTool and SmartEditTool (#15857) 2026-01-05 04:52:14 +00:00
N. Taylor Mullen d3563e2f0e Agent Skills: Add gemini skills CLI management command (#15837) 2026-01-05 04:40:21 +00:00
Sandy Tao dd84c2fb83 feat(hooks): implement granular stop and block behavior for agent hooks (#15824) 2026-01-05 02:58:34 +00:00
N. Taylor Mullen bdb349e7f6 Agent Skills: Extension Support & Security Disclosure (#15834) 2026-01-04 22:45:07 +00:00
Abhi 12c7c9cc42 feat(core,cli): enforce mandatory MessageBus injection (Phase 3 Hard Migration) (#15776) 2026-01-04 22:11:43 +00:00
Abhi 90be9c3587 feat(core): Standardize Tool and Agent Invocation constructors (Phase 2) (#15775) 2026-01-04 20:51:23 +00:00
Abhi eec5d5ebf8 feat(core): restore MessageBus optionality for soft migration (Phase 1) (#15774) 2026-01-04 19:59:35 +00:00
Abhi d3c206c677 Unify shell security policy and remove legacy logic (#15770) 2026-01-04 05:19:00 +00:00
N. Taylor Mullen f0a039f7c0 Agent Skills: Unify Representation & Centralize Loading (#15833) 2026-01-03 16:24:36 -08:00
Gal Zahavi 30f5c4af4a fix(core): mock powershell output in shell-utils test (#15831) 2026-01-02 23:20:26 +00:00
N. Taylor Mullen e78c3fe4f0 Agent Skills: Status Bar Integration for Skill Counts (#15741) 2026-01-02 23:01:31 +00:00
Abhi c0ccb22460 chore: cleanup old smart edit settings (#15832) 2026-01-02 22:28:28 +00:00
N. Taylor Mullen 764b195977 Agent Skills: Implement Agent Integration and System Prompt Awareness (#15728) 2026-01-02 21:47:46 +00:00
Jack Wotherspoon 0f3555a4d2 feat: add folder suggestions to /dir add command (#15724) 2026-01-02 21:08:11 +00:00
Abhi 006de1dd31 Add security docs (#15739) 2026-01-02 20:22:30 +00:00
Gal Zahavi 18fef0db31 fix(core): improve shell command with redirection detection (#15683) 2026-01-02 19:36:59 +00:00
N. Taylor Mullen 958284dc24 Agent Skills: Implement Autonomous Activation Tool & Context Injection (#15725) 2026-01-02 19:15:06 +00:00
Sehoon Shon 788bb04f5c log fallback mode (#15817) 2026-01-02 18:19:43 +00:00
HyeongHo Jun 8a0190ca3b fix(core): handle unhandled promise rejection in mcp-client-manager (#14701) 2026-01-02 16:06:56 +00:00
Christian Gunderman c29a8c12b3 Fix build issues caused by year-specific linter rule (#15780) 2026-01-01 19:01:03 +00:00
Sandy Tao 05049b5abf feat(hooks): implement STOP_EXECUTION and enhance hook decision handling (#15685) 2025-12-30 23:22:53 +00:00
Sehoon Shon 3ebe4e6a8f feat(agents): Add remote agents to agent registry (#15711) 2025-12-30 23:11:51 +00:00
Abhi 0eb84f5133 chore: remove cot style comments (#15735) 2025-12-30 21:43:33 +00:00
N. Taylor Mullen de1233b8ca Agent Skills: Implement Core Skill Infrastructure & Tiered Discovery (#15698) 2025-12-30 13:35:52 -08:00
christine betts ec11b8afbf Add extension settings info to /extensions list (#14905) 2025-12-30 21:09:48 +00:00
christine betts ec79fe1ab2 Add instructions to the extensions update info notification (#14907) 2025-12-30 19:50:33 +00:00
Christian Gunderman 90eb1e0281 Implement support for tool input modification (#15492) 2025-12-30 19:37:43 +00:00
Abhi 15c9f88da6 fix(hooks): deduplicate agent hooks and add cross-platform integration tests (#15701) 2025-12-30 19:13:16 +00:00
Abhi 4e6fee7fcd Revert "Update wittyPhrases.ts (#15697)" (#15719) 2025-12-30 16:50:28 +00:00
Shreya Keshive 1e08b150f7 refactor(auth): Refactor non-interactive mode auth validation & refresh (#15679) 2025-12-30 16:09:00 +00:00
segyges fb22f5b8ee Update wittyPhrases.ts (#15697)
Co-authored-by: Tommaso Sciortino <sciortino@gmail.com>
2025-12-30 07:18:44 +00:00
Adam Weidman 848e8485cd feat(agents): add support for remote agents and multi-agent TOML files (#15437) 2025-12-30 00:12:16 +00:00
Sehoon Shon 07e597de40 Exponential back-off retries for retryable error without a specified … (#15684) 2025-12-29 22:23:23 +00:00
Adib234 10ae84869a Migrate console to coreEvents.emitFeedback or debugLogger (#15219) 2025-12-29 20:46:10 +00:00
Abhi dcd2449b1a refactor: deprecate legacy confirmation settings and enforce Policy Engine (#15626) 2025-12-29 19:22:42 +00:00
gemini-cli-robot a3d214f8d7 chore/release: bump version to 0.24.0-nightly.20251227.37be16243 (#15612) 2025-12-27 05:29:40 +00:00
Abhi 37be162435 fix(core): enable granular shell command allowlisting in policy engine (#15601) 2025-12-26 23:48:44 +00:00
Abhi 5566292cc8 refactor(core): extract static concerns from CoreToolScheduler (#15589) 2025-12-26 20:51:39 +00:00
Gal Zahavi 65e2144b3d Manual nightly version bump to 0.24.0-nightly.20251226.546baf993 (#15594) 2025-12-26 19:40:46 +00:00
Maple! a26d195404 fix(cli): add enableShellOutputEfficiency to settings schema (#15560) 2025-12-26 19:07:33 +00:00
Maple! 21388a0a40 fix(core): handle checkIsRepo failure in GitService.initialize (#15574) 2025-12-26 18:37:57 +00:00
Adib234 acecd80afa Resolve unhandled promise rejection in ide-client.ts (#15587) 2025-12-26 16:37:13 +00:00
Sehoon Shon 69fc75c0b2 do not persist the fallback model (#15483) 2025-12-26 16:08:44 +00:00
Sehoon Shon 9cdb267ba5 feat: Show snowfall animation for holiday theme (#15494)
Co-authored-by: Jack Wotherspoon <jackwoth@google.com>
2025-12-26 16:07:39 +00:00
Maple! 56b050422d chore(core): fix comment typo (#15558) 2025-12-26 04:31:43 +00:00
Vedant Mahajan 546baf9934 Added modifyOtherKeys protocol support for tmux (#15524)
Co-authored-by: Ishaan Gupta <ishaankone@gmail.com>
2025-12-25 19:09:38 +00:00
Sehoon Shon e6344a8c24 Security: Project-level hook warnings (#15470) 2025-12-23 21:10:46 +00:00
Pyush Sinha 873d10df42 feat: terse transformations of image paths in text buffer (#4924)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
Co-authored-by: owenofbrien <86964623+owenofbrien@users.noreply.github.com>
2025-12-23 20:46:09 +00:00
Adam Weidman 02a36afc38 feat: Add A2A Client Manager and tests (#15485) 2025-12-23 20:27:16 +00:00
christine betts 563d81e08e Add experimental in-CLI extension install and uninstall subcommands (#15178)
Co-authored-by: Christine Betts <chrstn@google.com>
2025-12-23 19:48:27 +00:00
Jack Wotherspoon 5f28614760 chore: limit MCP resources display to 10 by default (#15489) 2025-12-23 19:39:35 +00:00
Jacob Richman b6b0727e28 Make schema validation errors non-fatal (#15487) 2025-12-23 18:26:37 +00:00
Jack Wotherspoon e9a601c1fe fix: add missing type field to MCPServerConfig (#15465) 2025-12-23 16:59:32 +00:00
Stoyan Dimitrov 308aa70718 refactor(core): remove deprecated permission aliases from BeforeToolHookOutput (#14855)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Abhi <43648792+abhipatel12@users.noreply.github.com>
2025-12-23 16:13:20 +00:00
Niyas Hameed 6be034392f feat: automatic /model persistence across Gemini CLI sessions (#13199)
Co-authored-by: Jack Wotherspoon <jackwoth@google.com>
2025-12-23 14:23:43 +00:00
Christian Gunderman 3b1dbcd42d Implemented unified secrets sanitization and env. redaction options (#15348) 2025-12-23 03:18:27 +00:00
Jack Wotherspoon 2ac9fe08f7 chore: remove clipboard file (#15447) 2025-12-22 23:04:02 +00:00
Jack Wotherspoon 24c722454b chore: improve error messages for --resume (#15360) 2025-12-22 22:44:41 +00:00
ElecTwix 0a216b28f3 fix #15369, prevent crash on unhandled EIO error in readStdin cleanup (#15410)
Co-authored-by: Gaurav <39389231+gsquared94@users.noreply.github.com>
2025-12-22 20:50:43 +00:00
Sandy Tao 9c48cd849b feat(ui): Add security warning and improve layout for Hooks list (#15440) 2025-12-22 20:47:56 +00:00
N. Taylor Mullen 0843d9af58 fix(core): use debugLogger.debug for startup profiler logs (#15443) 2025-12-22 20:45:33 +00:00
Abhi b0d5c4c058 feat(policy): implement dynamic mode-aware policy evaluation (#15307) 2025-12-22 20:25:07 +00:00
Christian Gunderman d18c96d6a1 Record timestamp with code assist metrics. (#15439) 2025-12-22 20:04:06 +00:00
Sehoon Shon dced409ac4 Add Folder Trust Support To Hooks (#15325) 2025-12-22 16:46:38 +00:00
Adam Weidman d6a2f1d670 chore(core): refactor model resolution and cleanup fallback logic (#15228) 2025-12-22 15:18:51 +00:00
Serghei 58fd00a3df fix(core): Add .geminiignore support to SearchText tool (#13763)
Co-authored-by: Gaurav <39389231+gsquared94@users.noreply.github.com>
2025-12-22 04:25:26 +00:00
Abhi b923604602 feat(telemetry): add clearcut logging for hooks (#15405) 2025-12-21 21:18:42 +00:00
gemini-cli-robot 8643d60b88 chore/release: bump version to 0.21.0-nightly.20251220.41a1a3eed (#15352) 2025-12-21 03:59:46 +00:00
Ying-xi 8feeffb29b fix: prevent infinite relaunch loop when --resume fails (#14941) (#14951)
Co-authored-by: Jack Wotherspoon <jackwoth@google.com>
2025-12-20 03:22:01 +00:00
Jacob Richman e64146914a fix(policy): fix bug where accepting-edits continued after it was turned off (#15351) 2025-12-20 01:09:43 +00:00
Gal Zahavi 6084708cc2 fix(cli): fix right border overflow in trust dialogs (#15350) 2025-12-20 00:19:03 +00:00
Adam Weidman 8ed0f8981f fix(folder trust): add validation for trusted folder level (#12215)
Co-authored-by: Gal Zahavi <38544478+galz10@users.noreply.github.com>
2025-12-20 00:13:03 +00:00
Sandy Tao 41a1a3eed1 fix(core): sanitize hook command expansion and prevent injection (#15343) 2025-12-19 22:31:42 +00:00
Tommaso Sciortino 10ba348a3a Introspection agent demo (#15232) 2025-12-19 22:11:32 +00:00
Tommaso Sciortino db67bb106a more robust command parsing logs (#15339) 2025-12-19 21:53:13 +00:00
Shehab b828b4754b docs(cli): add System Prompt Override (GEMINI_SYSTEM_MD) (#9515) 2025-12-19 20:11:37 +00:00
Gal Zahavi 9383b54d50 Validate OAuth resource parameter matches MCP server URL (#15289) 2025-12-19 19:33:20 +00:00
JAYADITYA 181da07dd9 fix(cli):change the placeholder of input during the shell mode (#15135)
Co-authored-by: Gal Zahavi <38544478+galz10@users.noreply.github.com>
2025-12-19 18:07:45 +00:00
Jacob Richman 419464a8c2 feat(ui): Put "Allow for all future sessions" behind a setting off by default. (#15322) 2025-12-19 17:25:23 +00:00
Sandy Tao 2e229d3bb6 feat(core): Implement JIT context memory loading and UI sync (#14469) 2025-12-19 17:04:03 +00:00
gemini-cli-robot 3c92bdb1ad chore/release: bump version to 0.21.0-nightly.20251219.70696e364 (#15301) 2025-12-19 16:17:12 +00:00
N. Taylor Mullen e0f159085e feat: simplify tool confirmation labels for better UX (#15296) 2025-12-19 00:38:53 +00:00
Abhi 402148dbc4 feat(hooks): reduce log verbosity and improve error reporting in UI (#15297) 2025-12-19 00:30:45 +00:00
Sandy Tao 70696e364b fix(ui): show command suggestions even on perfect match and sort them (#15287) 2025-12-19 00:05:36 +00:00
Gal Zahavi 1e10492e55 fix: prevent infinite loop in prompt completion on error (#14548) 2025-12-18 23:34:46 +00:00
Sehoon Shon 9e6914d641 Handle all 429 as retryableQuotaError (#15288) 2025-12-18 23:25:42 +00:00
Tommaso Sciortino 0c4fb6afd2 Remove unnecessary dependencies (#15291) 2025-12-18 23:24:19 +00:00
Tommaso Sciortino edab97997a Remove unused code (#15290) 2025-12-18 23:04:29 +00:00
Gal Zahavi 2515b89e2b feat: Pass additional environment variables to shell execution (#15160) 2025-12-18 22:01:23 +00:00
Tommaso Sciortino 60f0f19d76 add 3.0 to allowed sensitive keywords (#15276) 2025-12-18 18:58:07 +00:00
Jacob Richman 322232e514 feat: Detect background color (#15132) 2025-12-18 18:36:48 +00:00
Abhi 54466a3ea8 feat(hooks): add support for friendly names and descriptions (#15174) 2025-12-18 16:09:24 +00:00
Abhi 7da060c149 (docs): Add reference section to hooks documentation (#15159) 2025-12-18 15:49:05 +00:00
Jacob Richman 088f48d60f Add core tool that adds all context from the core package. (#15238) 2025-12-18 15:45:35 +00:00
Abhi 2b426c1d91 feat: add agent toml parser (#15112) 2025-12-18 03:46:55 +00:00
Jacob Richman bc168bbae4 Change detailed model stats to use a new shared Table class to resolve robustness issues. (#15208) 2025-12-18 02:01:37 +00:00
gemini-cli-robot 124a6da743 chore/release: bump version to 0.21.0-nightly.20251218.739c02bd6 (#15231) 2025-12-17 16:54:26 -08:00
Sandy Tao 739c02bd6d fix(cli): correct initial history length handling for chat commands (#15223) 2025-12-18 00:04:02 +00:00
Christian Gunderman 5d13145995 Code Assist backend telemetry for user accept/reject of suggestions (#15206) 2025-12-17 23:12:59 +00:00
Jenna Inouye c28ff3d5a5 Docs: Update Changelog for Dec 17, 2025 (#15204)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-12-17 23:11:22 +00:00
Sandy Tao 6ddd5abd7b fix(ui): Prevent eager slash command completion hiding sibling commands (#15224) 2025-12-17 22:30:35 +00:00
Adam Weidman bf6d0485ce chore(core): remove legacy fallback flags and migrate loop detection (#15213) 2025-12-17 22:14:33 +00:00
Jacob Richman 3d486ec1e9 feat(ui): add Windows clipboard image support and Alt+V paste workaround (#15218)
Co-authored-by: sgeraldes <sgeraldes@users.noreply.github.com>
2025-12-17 22:05:25 +00:00
Sehoon Shon a6d1245a54 Respect previewFeatures value from the remote flag if undefined (#15214) 2025-12-17 21:36:50 +00:00
joshualitt de7e1937f6 feat(core): Late resolve GenerateContentConfigs and reduce mutation. (#14920) 2025-12-17 20:29:26 +00:00
Adam Weidman b465e12747 chore(core): remove redundant isModelAvailabilityServiceEnabled toggle and clean up dead code (#15207) 2025-12-17 19:23:56 +00:00
Adam Weidman 948401a450 chore: update a2a-js to 0.3.7 (#15197) 2025-12-17 18:07:52 +00:00
Adib234 bb8f181ef1 Refactor: Migrate console.error in ripGrep.ts to debugLogger (#15201) 2025-12-17 17:40:28 +00:00
Tommaso Sciortino bf90b59935 feat: launch Gemini 3 Flash in Gemini CLI ️ (#15196)
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
Co-authored-by: joshualitt <joshualitt@google.com>
Co-authored-by: Sehoon Shon <sshon@google.com>
Co-authored-by: Adam Weidman <65992621+adamfweidman@users.noreply.github.com>
Co-authored-by: Adib234 <30782825+Adib234@users.noreply.github.com>
Co-authored-by: Jenna Inouye <jinouye@google.com>
2025-12-17 09:43:21 -08:00
Daan Versavel 18698d6929 (fix) Automated pr labeller (#14885) 2025-12-17 17:17:26 +00:00
Scars 80c4225286 feat(cli): Add /auth logout command to clear credentials and auth state (#13383) 2025-12-17 17:07:13 +00:00
Adam Weidman d02f3f6809 feat(core): introduce remote agent infrastructure and rename local executor (#15110) 2025-12-17 17:06:38 +00:00
Jacob Richman da85aed5aa Add one to the padding in settings dialog to avoid flicker. (#15173) 2025-12-17 16:53:51 +00:00
Christian Gunderman 7f2d33458a Disallow unnecessary awaits. (#15172) 2025-12-17 05:28:18 +00:00
Jack Wotherspoon 3e9a0a7628 chore: remove user query from footer in debug mode (#15169) 2025-12-17 01:44:04 +00:00
Jack Wotherspoon 26c115a4fb chore: remove repo specific tips (#15164) 2025-12-17 00:34:42 +00:00
Jacob Richman db643e9166 Remove foreground for themes other than shades of purple and holiday. (#14606)
Co-authored-by: Miguel Solorio <miguelsolorio@google.com>
2025-12-16 23:44:46 +00:00
Steffen Deusch ba100642e3 Use official ACP SDK and support HTTP/SSE based MCP servers (#13856) 2025-12-16 23:28:27 +00:00
Roaim cc52839f19 Docs (#15103)
Co-authored-by: Abhi <abhipatel@google.com>
2025-12-16 21:51:47 +00:00
gemini-cli-robot e79b149985 chore/release: bump version to 0.21.0-nightly.20251216.bb0c0d8ee (#15121) 2025-12-16 17:46:42 +00:00
Christian Gunderman 5e21c8c03c Code assist service metrics. (#15024) 2025-12-16 17:34:05 +00:00
Jacob Richman 5ea5107d05 refactor(cli): fix settings merging so that settings using the new json format take priority over ones using the old format (#15116) 2025-12-16 03:59:24 +00:00
Sehoon Shon 2995af6a21 use previewFeatures to determine which pro model to use for A2A (#15131) 2025-12-16 03:51:23 +00:00
Mayur Vaid 0c3eb82671 fix: Mark A2A requests as interactive (#15108) 2025-12-16 02:54:41 +00:00
Jacob Richman 79f664d593 Show raw input token counts in json output. (#15021)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-12-16 02:47:39 +00:00
Tommaso Sciortino bb0c0d8ee3 Simplify method signature. (#15114) 2025-12-15 21:18:04 +00:00
Abhi d236df5b21 Fix tool output fragmentation by encapsulating content in functionResponse (#13082) 2025-12-15 20:36:34 +00:00
N. Taylor Mullen 13944b9bb1 docs: update policy engine getting started and defaults (#15105) 2025-12-15 20:17:48 +00:00
Tommaso Sciortino ec665ef405 Clean up processes in integration tests (#15102) 2025-12-15 19:11:08 +00:00
Mayur Vaid 217e2b0eb4 fix: similar to policy-engine, throw error in case of requiring tool execution confirmation for non-interactive mode (#14702)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-12-15 18:34:27 +00:00
Shardul Natu d030a1f62f update(telemetry): add hashed_extension_name to field to extension events (#15025)
Co-authored-by: Shnatu <snatu@google.com>
2025-12-15 17:40:54 +00:00
Allen Hutchison fc2a5a42ce fix: use zod for safety check result validation (#15026)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-12-15 03:54:28 +00:00
Gal Zahavi 57c7b9ccce fix(auth): prioritize GEMINI_API_KEY env var and skip unnecessary key… (#14745) 2025-12-13 01:50:21 +00:00
Christian Gunderman 942bcfc61e Disallow redundant typecasts. (#15030) 2025-12-13 01:43:43 +00:00
gemini-cli-robot fcc3b2b5ec chore/release: bump version to 0.21.0-nightly.20251213.977248e09 (#15029) 2025-12-12 17:30:29 -08:00
Gaurav Sehgal d9f94103cd Add clarity to error messages (#14879) 2025-12-13 01:01:12 +00:00
JAYADITYA ad60cbfc2c chore : remove a redundant tip (#14947)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-12-13 01:00:09 +00:00
Tommaso Sciortino 126c32aca4 Refresh hooks when refreshing extensions. (#14918) 2025-12-12 16:43:46 -08:00
Jacob Richman 977248e095 chore(docs): add 'Maintainers only' label info to CONTRIBUTING.md (#14914) 2025-12-12 23:15:29 +00:00
Allen Hutchison a47af8e261 fix(core): commandPrefix word boundary and compound command safety (#15006) 2025-12-12 23:02:19 +00:00
gemini-cli-robot 5b56920f0a chore/release: bump version to 0.21.0-nightly.20251212.54de67536 (#14969) 2025-12-12 14:34:18 -08:00
Allen Hutchison 5f298c17d7 feat: Persistent "Always Allow" policies with granular shell & MCP support (#14737)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-12-12 21:45:39 +00:00
jb-perez d2a1a45646 Add license field into package.json (#14473) 2025-12-12 20:31:46 +00:00
Jacob Richman 6dea66f1f5 Remove flex from stats display. See snapshots for diffs. (#14983) 2025-12-12 20:19:39 +00:00
Kevin Ramdass 20164ebcda test: update ide detection tests to make them more robust when run in an ide (#15008) 2025-12-12 19:53:07 +00:00
Abhi edbe5480ca fix: temp fix for subagent invocation until subagent delegation is merged to stable (#15007) 2025-12-12 19:43:46 +00:00
Shreya Keshive e84c4bfb81 fix(vscode-ide-companion): correct license generation for workspace dependencies (#15004) 2025-12-12 19:06:58 +00:00
Shreya Keshive 3b2a4ba27a refactor(ide ext): Update port file name + switch to 1-based index for characters + remove truncation text (#10501) 2025-12-12 17:39:15 +00:00
Abhi 12cbe320e4 fix(policy): allow codebase_investigator by default in read-only policy (#15000) 2025-12-12 17:22:27 +00:00
Jack Wotherspoon 1e734d7e60 feat: support multi-file drag and drop of images (#14832) 2025-12-12 17:14:35 +00:00
Coco Sheng 299cc9bebf feat(a2a): Introduce /init command for a2a server (#13419) 2025-12-12 17:09:04 +00:00
Shreya Keshive a02abcf578 Revert "chore(deps): bump express from 5.1.0 to 5.2.0" (#14998) 2025-12-12 16:49:45 +00:00
Shreya Keshive bb33e281c0 fix(ide): Update IDE extension to write auth token in env var (#14999) 2025-12-12 16:42:49 +00:00
Nemo 86134e9970 feat(settings-validation): add validation for settings schema (#12929) 2025-12-12 06:30:34 +00:00
Jacob Richman 54de67536d feat(cli): polish cached token stats and simplify stats display when quota is present. (#14961) 2025-12-11 23:17:14 +00:00
Jacob Richman d818fb1d91 tool(cli): tweak the frontend tool to be aware of more core files from the cli (#14962) 2025-12-11 22:15:35 +00:00
Tommaso Sciortino 927102ea1b increase labeler timeout (#14922) 2025-12-11 17:05:20 +00:00
Kevin Ramdass 2d3db97067 Fix: Correctly detect MCP tool errors (#14937) 2025-12-11 16:41:02 +00:00
gemini-cli-robot 24fca1b745 chore/release: bump version to 0.21.0-nightly.20251211.8c83e1ea9 (#14924) 2025-12-11 04:24:47 +00:00
Adam Weidman 8c83e1ea9a feat: reset availabilityService on /auth (#14911) 2025-12-10 21:53:12 +00:00
Abhi 91b15fc9dc refactor: implement DelegateToAgentTool with discriminated union (#14769) 2025-12-10 21:14:13 +00:00
Sasha Varlamov d4506e0fc0 feat(core): Add support for transcript_path in hooks for git-ai/Gemini extension (#14663)
Co-authored-by: Christian Gunderman <gundermanc@gmail.com>
2025-12-10 20:44:30 +00:00
Christian Gunderman 648041c62a Add missing await. (#14910) 2025-12-10 19:52:31 +00:00
Adam Weidman 22e6af414a feat: attempt more error parsing (#14899) 2025-12-10 19:14:07 +00:00
joshualitt c8b688655c feat(core): Plumbing for late resolution of model configs. (#14597) 2025-12-10 17:36:27 +00:00
破茧 68ebf5d655 Fix: Correct typo in code comment (#14671)
Co-authored-by: Tommaso Sciortino <sciortino@gmail.com>
2025-12-10 17:11:54 +00:00
Tommaso Sciortino 1954f45c19 Limit search depth in path corrector (#14869) 2025-12-10 04:08:39 +00:00
Jack Wotherspoon ee6556cbd2 refactor(sessions): move session summary generation to startup (#14691) 2025-12-10 03:25:22 +00:00
Christian Gunderman d2a6b30398 Send the model and CLI version with the user agent (#14865) 2025-12-10 00:38:33 +00:00
Gal Zahavi d90356e8a3 chore: add closing reason to stale bug workflow (#14861) 2025-12-09 23:13:42 +00:00
Abhi aced401071 feat: ensure codebase investigator uses preview model when main agent does (#14412) 2025-12-09 21:07:50 +00:00
Sehoon Shon 2800187355 feat: display quota stats for unused models in /stats (#14764) 2025-12-09 20:09:46 +00:00
Shreya Keshive 3f5f030d01 feat(ide): fallback to GEMINI_CLI_IDE_AUTH_TOKEN env var (#14843) 2025-12-09 19:41:32 +00:00
dependabot[bot] 364b12e2fa chore(deps): bump express from 5.1.0 to 5.2.0 (#14325)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Tommaso Sciortino <sciortino@gmail.com>
2025-12-09 19:34:56 +00:00
Jenna Inouye 6877dd0de7 Docs: Fix errors preventing site rebuild. (#14842) 2025-12-09 18:31:54 +00:00
Kirill Klashtorny 6be32e9383 Update CODEOWNERS (#14830) 2025-12-09 18:22:52 +00:00
Daan Versavel f919783173 (fix) Automated pr labeller (#14788) 2025-12-09 17:19:16 +00:00
David Pierce 674494e80b allow final:true to be returned on a2a server edit calls. (#14747) 2025-12-09 15:30:50 +00:00
Coco Sheng 1f813f6a06 feat(a2a): Introduce restore command for a2a server (#13015)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Shreya Keshive <shreyakeshive@google.com>
2025-12-09 15:08:23 +00:00
Jack Wotherspoon afd4829f10 fix: use Gemini API supported image formats for clipboard (#14762) 2025-12-09 12:29:30 +00:00
Allen Hutchison 03be923e05 fix(lint): upgrade pip and use public pypi for yamllint (#14746) 2025-12-09 03:25:41 +00:00
Tommaso Sciortino bc542cbca8 Always set pending status in E2E tests (#14756) 2025-12-09 02:58:52 +00:00
Alex Gavrilescu 560550f5df feat: Add support for MCP Resources (#13178)
Co-authored-by: Jack Wotherspoon <jackwoth@google.com>
2025-12-09 02:43:12 +00:00
gemini-cli-robot 720b31cb8b chore/release: bump version to 0.21.0-nightly.20251209.ec9a8c7a7 (#14751) 2025-12-09 01:58:12 +00:00
Abhi d35a1fdec7 fix: handle missing local extension config and skip hooks when disabled (#14744) 2025-12-09 01:51:42 +00:00
Tommaso Sciortino 8f43d4851d Remove old E2E Workflows (#14749) 2025-12-09 00:32:29 +00:00
Gal Zahavi 171103aedc refactor(core): Improve environment variable handling in shell execution (#14742)
Co-authored-by: Jack Wotherspoon <jackwoth@google.com>
Co-authored-by: christine betts <chrstn@uw.edu>
2025-12-09 00:22:46 +00:00
christine betts ec9a8c7a72 Add support for user-scoped extension settings (#13748) 2025-12-08 22:51:26 +00:00
Jenna Inouye 91c46311c8 Docs: Proper release notes (#14405) 2025-12-08 22:26:43 +00:00
Jack Wotherspoon 89570aef06 feat: auto-execute on slash command completion functions (#14584) 2025-12-08 21:32:39 +00:00
Kevin Ramdass 17b5b40765 feat: Add OTEL log event gemini_cli.startup_stats for startup stats. (#14734) 2025-12-08 20:29:50 +00:00
Tommaso Sciortino cdc74a742a Always set status in chained_e2e workflow (#14730) 2025-12-08 19:37:47 +00:00
Marat Boshernitsan 4322137cee refactor(telemetry): Improve previous PR that allows telemetry to use the CLI auth and add testing (#14589) 2025-12-08 11:20:13 -08:00
Daan Versavel 8651c53923 feat(github action) Triage and Label Pull Requests by Size and Comple… (#5571)
Co-authored-by: Tommaso Sciortino <sciortino@gmail.com>
2025-12-08 18:19:49 +00:00
Shreya Keshive b2a821c81a Update automated triage workflow to stop assigning priority labels (#14717) 2025-12-08 18:17:26 +00:00
Tommaso Sciortino 3888a592cf set failed status when chained e2e fails (#14725) 2025-12-08 18:10:39 +00:00
Jack Wotherspoon 84c07c8fa1 fix(audio): improve reading of audio files (#14658) 2025-12-08 17:46:33 +00:00
parthasaradhi 389cadb06a Fix: Prevent freezing in non-interactive Gemini CLI when debug mode is enabled (#14580) 2025-12-08 17:18:11 +00:00
Paweł Dec 6e51bbc215 Add prompt_id propagation in a2a-server task (#14581) 2025-12-08 15:12:47 +00:00
Adam Weidman 8f4f8baa81 feat(modelAvailabilityService): integrate model availability service into backend logic (#14470) 2025-12-08 14:44:34 +00:00
gemini-cli-robot 7a72037572 chore/release: bump version to 0.21.0-nightly.20251207.025e450ac (#14662) 2025-12-07 01:34:24 +00:00
Christian Gunderman 025e450ac2 Disallow floating promises. (#14605) 2025-12-06 00:12:49 +00:00
Jenna Inouye 3cf44acc08 Docs: Update Gemini 3 on Gemini CLI documentation (#14601) 2025-12-05 22:37:35 +00:00
Tommaso Sciortino bdd15e8911 Fully detach autoupgrade process (#14595) 2025-12-05 22:12:54 +00:00
Jacob Richman 04cbae5b5f Fix emoji width in debug console. (#14593) 2025-12-05 20:58:54 +00:00
Sehoon Shon 2c4ec31ed1 expose previewFeatures flag in a2a (#14550) 2025-12-05 18:28:52 +00:00
Jenna Inouye 996cbcb680 Docs: Model routing clarification (#14373) 2025-12-05 18:10:34 +00:00
matt korwel dd3fd73ffe fix(core): improve API response error handling and retry logic (#14563) 2025-12-05 17:49:08 +00:00
Tommaso Sciortino 738354ff65 Use Robot PAT for chained e2e merge queue skipper (#14585) 2025-12-05 17:45:18 +00:00
Jack Wotherspoon 616d6f6667 feat(sessions): use 1-line generated session summary to describe sessions (#14467) 2025-12-05 17:20:15 +00:00
gemini-cli-robot 8341256d1e chore/release: bump version to 0.21.0-nightly.20251204.3da4fd5f7 (#14476) 2025-12-05 15:52:09 +00:00
Jacob Richman 934b309b4c Fix issue where we were passing the model content reflecting terminal line wrapping. (#14566) 2025-12-05 04:42:57 +00:00
Jacob Richman d284fa66c0 Fix bug in the shellExecutionService resulting in both truncation and 3X bloat (#14545) 2025-12-04 18:26:12 -08:00
Tommaso Sciortino bedce8aacf Trigger chained e2e tests on all pull requests (#14551) 2025-12-05 01:28:50 +00:00
Silvio Junior 7db5abdecf Fixes [API Error: Cannot read properties of undefined (reading 'error')] (#14553) 2025-12-05 01:18:37 +00:00
Tommaso Sciortino 290ede93e2 Write e2e status to local repo not forked repo (#14549) 2025-12-05 00:35:08 +00:00
Tommaso Sciortino f4f2bcbd98 Make trigger_e2e manually fireable. (#14547) 2025-12-04 23:58:45 +00:00
Tommaso Sciortino 1c5213788c Grant chained workflows proper permission. (#14534) 2025-12-04 22:11:37 +00:00
Gal Zahavi 0d5424fb79 feat: Exclude maintainer labeled issues from stale issue closer (#14532) 2025-12-04 21:54:26 +00:00
Tommaso Sciortino ae8694b303 Fix privacy screen for legacy tier users (#14522) 2025-12-04 21:24:00 +00:00
Adib234 5f60281d25 Add support for MCP dynamic tool update by notifications/tools/list_changed (#14375) 2025-12-04 21:04:38 +00:00
christine betts 0c7ae22f5d Disable flaky extension reloading test on linux (#14528)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-12-04 19:40:36 +00:00
Paresh Joshi 2d1c1ac567 fix(client): Correctly latch hasFailedCompressionAttempt flag (#13002) 2025-12-04 18:12:45 +00:00
Justin Poehnelt 205d0f456e fix(extensions): resolve GitHub API 415 error for source tarballs (#13319) 2025-12-04 17:16:08 +00:00
Jainam M 8b0a8f47c1 Adding session id as part of json o/p (#14504) 2025-12-04 17:06:20 +00:00
Aswin Ashok 84f521b1c6 fix(shell): cursor visibility when using interactive mode (#14095)
Co-authored-by: Gal Zahavi <38544478+galz10@users.noreply.github.com>
2025-12-04 16:58:03 +00:00
Jack Wotherspoon 1040c246f5 feat: add auto-execute on Enter behavior to argumentless MCP prompts (#14510) 2025-12-04 16:33:45 +00:00
Coco Sheng b27cf0b0a8 feat(cli): Move key restore logic to core (#13013) 2025-12-04 15:56:16 +00:00
Jack Wotherspoon 0a2971f9d3 feat(mcp): add --type alias for --transport flag in gemini mcp add (#14503) 2025-12-04 15:45:03 +00:00
Sehoon Shon 48e8c12476 remove unused isSearching field (#14509) 2025-12-04 15:44:21 +00:00
Megha Bansal 6f3b56c5b6 fix: improve retry logic for fetch errors and network codes (#14439) 2025-12-04 15:41:08 +00:00
Sehoon Shon b745d46395 do not toggle the setting item when entering space (#14489) 2025-12-04 14:48:18 +00:00
Daan Versavel 46bb07e4b7 Fix(cli): Improve Homebrew update instruction to specify gemini-cli (#14502) 2025-12-04 14:28:25 +00:00
Adib234 d5e5f58737 Update setting search UX (#14451) 2025-12-04 06:43:11 +00:00
ismellpillows 9bc5a4d64f feat(cli): support /copy in remote sessions using OSC52 (#13471) 2025-12-04 06:27:33 +00:00
Paresh Joshi 7b811a38a6 chore(tests): remove obsolete test for hierarchical memory (#13122) 2025-12-04 00:28:54 +00:00
christine betts e0a2227faf Add commands for listing and updating per-extension settings (#12664) 2025-12-04 00:16:16 +00:00
christine betts 470f3b057f Remove example extension (#14376) 2025-12-04 00:16:13 +00:00
Sandy Tao 518e73ac9f fix(cli): Fix word navigation for CJK characters (#14475) 2025-12-04 00:16:11 +00:00
Hirokazu Hata 3da4fd5f7d fix(acp): prevent unnecessary credential cache clearing on re-authent… (#9410)
Co-authored-by: Tommaso Sciortino <sciortino@gmail.com>
2025-12-03 23:43:15 +00:00
Mark Hansen 1e6243045e Markdown export: move the emoji to the end of the line (#12278)
Co-authored-by: Tommaso Sciortino <sciortino@gmail.com>
2025-12-03 23:30:29 +00:00
Victor Anderson a28be4a4e0 docs: fix typo 'socus' to 'focus' in todos.md (#14374) 2025-12-03 12:58:40 -08:00
Adam Weidman 153d01a01e feat: Add enableAgents experimental flag (#14371)
Co-authored-by: Abhi <43648792+abhipatel12@users.noreply.github.com>
2025-12-03 20:53:06 +00:00
Abhi eb3312e7ba feat: Support Extension Hooks with Security Warning (#14460) 2025-12-03 20:07:37 +00:00
Gal Zahavi 939cb67621 feat: add scheduled workflow to close stale issues (#14404) 2025-12-03 19:11:28 +00:00
Tommaso Sciortino 00705b14bd Fix tests (#14458) 2025-12-03 19:07:23 +00:00
Edilmo Palencia 8d4082ef2e feat(hooks): Hook System Documentation (#14307) 2025-12-03 18:57:05 +00:00
Allen Hutchison f588219bb9 fix: Bundle default policies for npx distribution (#14457) 2025-12-03 18:53:18 +00:00
Edilmo Palencia b8c038f41f feat(hooks): Hooks Commands Panel, Enable/Disable, and Migrate (#14225) 2025-12-03 18:01:57 +00:00
Jacob Richman 08067acc71 Avoid triggering refreshStatic unless there really is a banner to display. (#14328) 2025-12-03 17:08:32 +00:00
Edilmo Palencia 1c12da1fad feat(hooks): Hook Session Lifecycle & Compression Integration (#14151) 2025-12-03 17:04:13 +00:00
christine betts 7a6d3067c6 Add new enterprise instructions (#8641)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-12-03 15:32:34 +00:00
Alexander Farber 54c62d580c fix(security): Fix npm audit vulnerabilities in glob and body-parser (#14090) 2025-12-03 14:07:50 +00:00
CHAEWAN KIM 0857345945 refactor(editor): use const assertion for editor types with single source of truth (#8604) 2025-12-03 06:16:19 +00:00
Marat Boshernitsan b9b3b8050d Allow telemetry exporters to GCP to utilize user's login credentials, if requested (#13778) 2025-12-03 05:27:37 +00:00
Riddhi Dutta 92e95ed806 track github repository names in telemetry events (#13670)
Co-authored-by: riddhi <duttariddhi@google.com>
2025-12-03 05:26:12 +00:00
Tommaso Sciortino 035bea3699 Restrict integration tests tools (#14403) 2025-12-03 01:43:06 +00:00
Jack Wotherspoon bdbbe9232d feat: consolidate remote MCP servers to use url in config (#13762) 2025-12-03 01:01:33 +00:00
Billy Biggs 145fb246a6 docs: Recommend using --debug intead of --verbose for CLI debugging (#14334) 2025-12-03 00:44:23 +00:00
Allen Hutchison 533a3fb312 feat: enable message bus integration by default (#14329) 2025-12-03 00:05:54 +00:00
Sehoon Shon 344f2f26e7 implement fuzzy search inside settings (#13864) 2025-12-02 23:52:56 +00:00
gemini-cli-robot ed10edbf0d chore(release): bump version to 0.21.0-nightly.20251202.2d935b379 (#14409) 2025-12-02 23:39:40 +00:00
Allen Hutchison 828afe113e refactor(stdio): always patch stdout and use createWorkingStdio for clean output (#14159) 2025-12-02 23:08:25 +00:00
770 changed files with 59303 additions and 17158 deletions
+29
View File
@@ -0,0 +1,29 @@
description="Injects context of all relevant cli files"
prompt = """
The following output contains the complete
source code of packages/core.
**Pay extremely close attention to these files.** They define the project's
core architecture, component patterns, and testing standards.
The source code contains the content of absolutely every source code file in
packages/core.
You should very rarely need to read any other files from packages/core to resolve
prompts.
!{find packages/core \\( -path packages/cli/dist -o -path packages/core/dist -o -name node_modules \\) -prune -o -type f \\( -name "*.ts" -o -name "*.tsx" \\) ! -name "*.test.ts" ! -name "*.test.tsx" ! -name "*.d.ts" -exec echo "--- {} ---" \\; -exec cat {} \\;}
In addition to the code context, you MUST strictly adhere to the following frontend-specific development guidelines when writing code in packages/core.
## Configuration & Settings
* **Settings vs Args**: Use settings for user-configurable options; do not add new CLI arguments.
* **Schema**: Add new settings to `packages/cli/src/config/settingsSchema.ts`.
* **Documentation**:
* If `showInDialog: true`, document in `docs/get-started/configuration.md`.
* Ensure `requiresRestart` is correctly set.
## General
* **Logging**: Use `debugLogger` for errors. NEVER leave `console.log/warn/error` in the code.
* **TypeScript**: Avoid the non-null assertion operator (`!`).
{{args}}.
"""
+54
View File
@@ -0,0 +1,54 @@
description="Injects context of all relevant cli files"
prompt = """
The source code contains the content of absolutely every source code file in
packages/cli and key files from packages/core. In addition to the source code, the following test files are
included as they are test files that conform to the project's testing standards:
`packages/cli/src/ui/components/InputPrompt.test.tsx` and `packages/cli/src/ui/App.test.tsx`.
You should very rarely need to read any other files from packages/cli to resolve
prompts.
!{find packages/cli -path packages/cli/dist -prune -o -type f \\( -name "*.ts" -o -name "*.tsx" \\) ! -name "*.test.ts" ! -name "*.test.tsx" ! -name "*.d.ts" -exec echo "--- {} ---" \\; -exec cat {} \\; && echo "--- packages/cli/src/ui/components/InputPrompt.test.tsx ---" && cat packages/cli/src/ui/components/InputPrompt.test.tsx && echo "--- packages/cli/src/ui/App.test.tsx ---" && cat packages/cli/src/ui/App.test.tsx && echo "--- packages/core/src/core/coreToolScheduler.ts ---" && cat packages/core/src/core/coreToolScheduler.ts && echo "--- packages/core/src/core/nonInteractiveToolExecutor.ts ---" && cat packages/core/src/core/nonInteractiveToolExecutor.ts && echo "--- packages/core/src/confirmation-bus/message-bus.ts ---" && cat packages/core/src/confirmation-bus/message-bus.ts && echo "--- packages/core/src/confirmation-bus/types.ts ---" && cat packages/core/src/confirmation-bus/types.ts && echo "--- packages/core/src/utils/events.ts ---" && cat packages/core/src/utils/events.ts && echo "--- packages/core/src/core/client.ts ---" && cat packages/core/src/core/client.ts && echo "--- packages/core/src/core/geminiChat.ts ---" && cat packages/core/src/core/geminiChat.ts && echo "--- packages/core/src/core/turn.ts ---" && cat packages/core/src/core/turn.ts && echo "--- packages/core/src/agents/executor.ts ---" && cat packages/core/src/agents/executor.ts && echo "--- packages/core/src/telemetry/types.ts ---" && cat packages/core/src/telemetry/types.ts && echo "--- packages/core/src/telemetry/loggers.ts ---" && cat packages/core/src/telemetry/loggers.ts && echo "--- packages/core/src/telemetry/uiTelemetry.ts ---" && cat packages/core/src/telemetry/uiTelemetry.ts}
**Pay extremely close attention to these files.** They define the project's
core architecture, component patterns, and testing standards.
In addition to the code context, you MUST strictly adhere to the following frontend-specific development guidelines while adding code to packages/cli.
## Testing Standards
* **Async Testing**: ALWAYS use `waitFor` from `packages/cli/src/test-utils/async.ts` instead of `vi.waitFor` to prevent flakiness and `act` warnings. NEVER use fixed waits (e.g., `await delay(100)`).
* **State Changes**: Wrap all blocks that change component state in `act`.
* **Snapshots**: Use `toMatchSnapshot` to verify rendering.
* **Rendering**: Use `render` or `renderWithProviders` from `packages/cli/src/test-utils/render.tsx` instead of `ink-testing-library` directly.
* **Mocking**:
* Reuse existing mocks/fakes where possible.
* **Parameterized Tests**: Use parameterized tests with explicit types to reduce duplication and ensure type safety.
* Avoid mocking the file system, os, or child_process if at all possible; if you have to mock the Mock critical dependencies (`fs`, `os`, `child_process`) do so ONLY at the top of the file.
## React & Ink Architecture
* **Keyboard Handling**: You MUST use `useKeyPress.ts` from Gemini CLI for keyboard handling, NOT the standard ink library. This is critical for supporting slow terminals and multiple events per frame.
* Handle multiple events gracefully (often requires a reducer pattern, see `text-buffer.ts`).
* **State Management**:
* NEVER trigger side effects from within the body of a `setState` callback. Use a reducer or `useRef` if necessary.
* Initialize state explicitly (e.g., use `undefined` rather than `true` if unknown).
* **Performance**:
* Avoid synchronous file I/O in components.
* Do not introduce excessive property drilling; leverage or extend existing providers.
## Configuration & Settings
* **Settings vs Args**: Use settings for user-configurable options; do not add new CLI arguments.
* **Schema**: Add new settings to `packages/cli/src/config/settingsSchema.ts`.
* **Documentation**:
* If `showInDialog: true`, document in `docs/get-started/configuration.md`.
* Ensure `requiresRestart` is correctly set.
## Keyboard Shortcuts
* **Registration**: Define new shortcuts in `packages/cli/src/config/keyBindings.ts`.
* **Documentation**: Document all new shortcuts in `docs/cli/keyboard-shortcuts.md`.
* **Compatibility**: Avoid function keys and common VSCode shortcuts. Be cautious with `Meta` key (Mac-limited support).
## General
* **Logging**: Use `debugLogger` for errors. NEVER leave `console.log/warn/error` in the code.
* **TypeScript**: Avoid the non-null assertion operator (`!`).
{{args}}.
"""
+57
View File
@@ -0,0 +1,57 @@
description="Injects context of all relevant cli files"
prompt = """
The following output contains the complete
source code of the gemini cli library (`packages/cli`), and {packages/core} and representative test files
(`packages/cli/src/ui/components/InputPrompt.test.tsx` and `packages/cli/src/ui/App.test.tsx`) that best conform to the project's testing standards.
**Pay extremely close attention to these files.** They define the project's
core architecture, component patterns, and testing standards.
The source code contains the content of absolutely every source code file in
packages/cli and packages/core. In addition to the source code, the following test files are
included as they are test files that conform to the project's testing standards:
`packages/cli/src/ui/components/InputPrompt.test.tsx` and `packages/cli/src/ui/App.test.tsx`.
You should very rarely need to read any other files from packages/cli to resolve
prompts.
!{find packages/cli packages/core \\( -path packages/cli/dist -o -path packages/core/dist -o -name node_modules \\) -prune -o -type f \\( -name "*.ts" -o -name "*.tsx" \\) ! -name "*.test.ts" ! -name "*.test.tsx" ! -name "*.d.ts" -exec echo "--- {} ---" \\; -exec cat {} \\; && echo "--- packages/cli/src/ui/components/InputPrompt.test.tsx ---" && cat packages/cli/src/ui/components/InputPrompt.test.tsx && echo "--- packages/cli/src/ui/App.test.tsx ---" && cat packages/cli/src/ui/App.test.tsx}
In addition to the code context, you MUST strictly adhere to the following frontend-specific development guidelines when writing code in packages/cli.
## Testing Standards
* **Async Testing**: ALWAYS use `waitFor` from `packages/cli/src/test-utils/async.ts` instead of `vi.waitFor` to prevent flakiness and `act` warnings. NEVER use fixed waits (e.g., `await delay(100)`).
* **State Changes**: Wrap all blocks that change component state in `act`.
* **Snapshots**: Use `toMatchSnapshot` to verify rendering.
* **Rendering**: Use `render` or `renderWithProviders` from `packages/cli/src/test-utils/render.tsx` instead of `ink-testing-library` directly.
* **Mocking**:
* Reuse existing mocks/fakes where possible.
* **Parameterized Tests**: Use parameterized tests with explicit types to reduce duplication and ensure type safety.
* Avoid mocking the file system, os, or child_process if at all possible; if you have to mock the Mock critical dependencies (`fs`, `os`, `child_process`) do so ONLY at the top of the file.
## React & Ink Architecture
* **Keyboard Handling**: You MUST use `useKeyPress.ts` from Gemini CLI for keyboard handling, NOT the standard ink library. This is critical for supporting slow terminals and multiple events per frame.
* Handle multiple events gracefully (often requires a reducer pattern, see `text-buffer.ts`).
* **State Management**:
* NEVER trigger side effects from within the body of a `setState` callback. Use a reducer or `useRef` if necessary.
* Initialize state explicitly (e.g., use `undefined` rather than `true` if unknown).
* **Performance**:
* Avoid synchronous file I/O in components.
* Do not introduce excessive property drilling; leverage or extend existing providers.
## Configuration & Settings
* **Settings vs Args**: Use settings for user-configurable options; do not add new CLI arguments.
* **Schema**: Add new settings to `packages/cli/src/config/settingsSchema.ts`.
* **Documentation**:
* If `showInDialog: true`, document in `docs/get-started/configuration.md`.
* Ensure `requiresRestart` is correctly set.
## Keyboard Shortcuts
* **Registration**: Define new shortcuts in `packages/cli/src/config/keyBindings.ts`.
* **Documentation**: Document all new shortcuts in `docs/cli/keyboard-shortcuts.md`.
* **Compatibility**: Avoid function keys and common VSCode shortcuts. Be cautious with `Meta` key (Mac-limited support).
## General
* **Logging**: Use `debugLogger` for errors. NEVER leave `console.log/warn/error` in the code.
* **TypeScript**: Avoid the non-null assertion operator (`!`).
{{args}}.
"""
+3
View File
@@ -11,3 +11,6 @@
/.github/workflows/ @google-gemini/gemini-cli-askmode-approvers
/packages/cli/package.json @google-gemini/gemini-cli-askmode-approvers
/packages/core/package.json @google-gemini/gemini-cli-askmode-approvers
# Docs have a dedicated approver group in addition to maintainers
/docs/ @google-gemini/gemini-cli-maintainers @google-gemini/gemini-cli-docs
@@ -1,4 +1,4 @@
name: 'Test Chained E2E'
name: 'Testing: E2E (Chained)'
on:
push:
@@ -37,14 +37,15 @@ jobs:
- id: 'merge-queue-e2e-skipper'
uses: 'cariad-tech/merge-queue-ci-skipper@1032489e59437862c90a08a2c92809c903883772' # ratchet:cariad-tech/merge-queue-ci-skipper@main
with:
secret: '${{ secrets.GITHUB_TOKEN }}'
secret: '${{ secrets.GEMINI_CLI_ROBOT_GITHUB_PAT }}'
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'"
if: "${{github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_run'}}"
outputs:
repo_name: '${{ steps.output-repo-name.outputs.repo_name }}'
head_sha: '${{ steps.output-repo-name.outputs.head_sha }}'
steps:
- name: 'Mock Repo Artifact'
if: "${{ github.event_name == 'workflow_dispatch' }}"
@@ -66,7 +67,7 @@ jobs:
name: 'repo_name'
run-id: '${{ env.RUN_ID }}'
path: '${{ runner.temp }}/artifacts'
- name: 'Output Repo Name'
- name: 'Output Repo Name and SHA'
id: 'output-repo-name'
uses: 'actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd' # ratchet:actions/github-script@v8
with:
@@ -75,8 +76,16 @@ jobs:
const fs = require('fs');
const path = require('path');
const temp = '${{ runner.temp }}/artifacts';
const repo_name = String(fs.readFileSync(path.join(temp, 'repo_name')));
core.setOutput('repo_name', repo_name);
const repoPath = path.join(temp, 'repo_name');
if (fs.existsSync(repoPath)) {
const repo_name = String(fs.readFileSync(repoPath)).trim();
core.setOutput('repo_name', repo_name);
}
const shaPath = path.join(temp, 'head_sha');
if (fs.existsSync(shaPath)) {
const head_sha = String(fs.readFileSync(shaPath)).trim();
core.setOutput('head_sha', head_sha);
}
parse_run_context:
name: 'Parse run context'
@@ -91,7 +100,7 @@ jobs:
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 }}'
SHA: '${{ needs.download_repo_name.outputs.head_sha || github.event.inputs.head_sha || github.event.workflow_run.head_sha || github.sha }}'
shell: 'bash'
run: |
echo "REPO=$REPO" >> "$GITHUB_OUTPUT"
@@ -100,19 +109,20 @@ jobs:
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'
if: 'always()'
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 }}'
repo: '${{ github.repository }}'
sha: '${{ needs.parse_run_context.outputs.sha }}'
token: '${{ secrets.GITHUB_TOKEN }}'
token: '${{ secrets.GEMINI_CLI_ROBOT_GITHUB_PAT }}'
status: 'pending'
context: 'E2E (Chained)'
e2e_linux:
name: 'E2E Test (Linux) - ${{ matrix.sandbox }}'
@@ -121,7 +131,7 @@ jobs:
- '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')
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip != 'true')
strategy:
fail-fast: false
matrix:
@@ -150,7 +160,7 @@ jobs:
run: 'npm run build'
- name: 'Set up Docker'
if: "matrix.sandbox == 'sandbox:docker'"
if: "${{matrix.sandbox == 'sandbox:docker'}}"
uses: 'docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435' # ratchet:docker/setup-buildx-action@v3
- name: 'Run E2E tests'
@@ -173,7 +183,7 @@ jobs:
- 'parse_run_context'
runs-on: 'macos-latest'
if: |
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip == 'false')
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip != 'true')
steps:
- name: 'Checkout'
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
@@ -193,11 +203,11 @@ jobs:
run: 'npm run build'
- name: 'Fix rollup optional dependencies on macOS'
if: "runner.os == 'macOS'"
if: "${{runner.os == 'macOS'}}"
run: |
npm cache clean --force
- name: 'Run E2E tests (non-Windows)'
if: "runner.os != 'Windows'"
if: "${{runner.os != 'Windows'}}"
env:
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
KEEP_OUTPUT: 'true'
@@ -211,7 +221,7 @@ jobs:
- 'merge_queue_skipper'
- 'parse_run_context'
if: |
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip == 'false')
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip != 'true')
runs-on: 'gemini-cli-windows-16-core'
continue-on-error: true
@@ -269,7 +279,7 @@ jobs:
e2e:
name: 'E2E'
if: |
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip == 'false')
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip != 'true')
needs:
- 'e2e_linux'
- 'e2e_mac'
@@ -288,7 +298,7 @@ jobs:
set_workflow_status:
runs-on: 'gemini-cli-ubuntu-16-core'
permissions: 'write-all'
if: "github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_run'"
if: 'always()'
needs:
- 'parse_run_context'
- 'e2e'
@@ -298,7 +308,8 @@ jobs:
if: 'always()'
with:
allowForks: 'true'
repo: '${{ needs.parse_run_context.outputs.repository }}'
repo: '${{ github.repository }}'
sha: '${{ needs.parse_run_context.outputs.sha }}'
token: '${{ secrets.GITHUB_TOKEN }}'
status: '${{ job.status }}'
status: '${{ needs.e2e.result }}'
context: 'E2E (Chained)'
+1 -1
View File
@@ -43,7 +43,7 @@ jobs:
- id: 'merge-queue-ci-skipper'
uses: 'cariad-tech/merge-queue-ci-skipper@1032489e59437862c90a08a2c92809c903883772' # ratchet:cariad-tech/merge-queue-ci-skipper@main
with:
secret: '${{ secrets.GITHUB_TOKEN }}'
secret: '${{ secrets.GEMINI_CLI_ROBOT_GITHUB_PAT }}'
lint:
name: 'Lint'
-247
View File
@@ -1,247 +0,0 @@
name: 'Testing: E2E'
on:
push:
branches:
- 'main'
# This will run for PRs from the base repository, providing secrets.
pull_request:
branches:
- 'main'
- 'release/**'
# This will run for PRs from forks when a label is added.
pull_request_target:
types: ['labeled']
merge_group:
workflow_dispatch:
inputs:
branch_ref:
description: 'Branch to run on'
required: true
default: 'main'
type: 'string'
concurrency:
group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}'
cancel-in-progress: |-
${{ github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/heads/release/') }}
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:
- id: 'merge-queue-e2e-skipper'
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.result !='success' || needs.merge_queue_skipper.outputs.skip == 'false') &&
(github.event_name == 'push' ||
github.event_name == 'merge_group' ||
github.event_name == 'workflow_dispatch' ||
(github.event.pull_request.head.repo.full_name == github.repository) ||
(github.event.label.name == 'maintainer:e2e:ok'))
runs-on: 'gemini-cli-ubuntu-16-core'
strategy:
fail-fast: false
matrix:
sandbox:
- 'sandbox:none'
- 'sandbox:docker'
node-version:
- '20.x'
steps:
- name: 'Checkout (fork)'
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
if: "github.event_name == 'pull_request_target'"
with:
ref: '${{ github.event.pull_request.head.sha }}'
repository: '${{ github.event.pull_request.head.repo.full_name }}'
- name: 'Checkout (internal)'
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
if: "github.event_name != 'pull_request_target'"
with:
ref: '${{ github.event.inputs.branch_ref || github.ref }}'
- 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 }}'
KEEP_OUTPUT: 'true'
VERBOSE: 'true'
shell: 'bash'
run: |
if [[ "${{ matrix.sandbox }}" == "sandbox:docker" ]]; then
npm run test:integration:sandbox:docker
else
npm run test:integration:sandbox:none
fi
e2e_mac:
name: 'E2E Test (macOS)'
needs: 'merge_queue_skipper'
if: |
(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' ||
(github.event.pull_request.head.repo.full_name == github.repository) ||
(github.event.label.name == 'maintainer:e2e:ok'))
runs-on: 'macos-latest'
steps:
- name: 'Checkout (fork)'
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
if: "github.event_name == 'pull_request_target'"
with:
ref: '${{ github.event.pull_request.head.sha }}'
repository: '${{ github.event.pull_request.head.repo.full_name }}'
- name: 'Checkout (internal)'
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
if: "github.event_name != 'pull_request_target'"
with:
ref: '${{ github.event.inputs.branch_ref || github.ref }}'
- 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'
SANDBOX: 'sandbox:none'
VERBOSE: 'true'
run: 'npm run test:integration:sandbox:none'
e2e_windows:
name: 'Slow E2E - Win'
needs: 'merge_queue_skipper'
if: |
(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' ||
(github.event.pull_request.head.repo.full_name == github.repository) ||
(github.event.label.name == 'maintainer:e2e:ok'))
runs-on: 'gemini-cli-windows-16-core'
continue-on-error: true
steps:
- name: 'Checkout (fork)'
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
if: "github.event_name == 'pull_request_target'"
with:
ref: '${{ github.event.pull_request.head.sha }}'
repository: '${{ github.event.pull_request.head.repo.full_name }}'
- name: 'Checkout (internal)'
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
if: "github.event_name != 'pull_request_target'"
with:
ref: '${{ github.event.inputs.branch_ref || github.ref }}'
- 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'
shell: 'pwsh'
run: 'npm run test:integration:sandbox:none'
e2e:
name: 'E2E'
if: |
always() && (
github.event_name == 'push' ||
github.event_name == 'merge_group' ||
github.event_name == 'workflow_dispatch' ||
(github.event.pull_request.head.repo.full_name == github.repository) ||
(github.event.label.name == 'maintainer:e2e:ok')
)
needs:
- 'e2e_linux'
- 'e2e_mac'
runs-on: 'gemini-cli-ubuntu-16-core'
steps:
- name: 'Check E2E test results'
run: |
if [[ (${{ needs.e2e_linux.result }} != 'success' && ${{ needs.e2e_linux.result }} != 'skipped') || \
(${{ needs.e2e_mac.result }} != 'success' && ${{ needs.e2e_mac.result }} != 'skipped') ]]; then
echo "One or more E2E jobs failed."
exit 1
fi
echo "All required E2E jobs passed!"
@@ -39,15 +39,13 @@ jobs:
github.event_name == 'workflow_dispatch' ||
(
(github.event_name == 'issues' || github.event_name == 'issue_comment') &&
contains(github.event.issue.labels.*.name, 'status/need-triage') &&
(github.event_name != 'issue_comment' || (
contains(github.event.comment.body, '@gemini-cli /triage') &&
(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/')
!contains(github.event.issue.labels.*.name, 'area/')
timeout-minutes: 5
runs-on: 'ubuntu-latest'
steps:
@@ -76,13 +74,8 @@ jobs:
ISSUE_NUMBER_INPUT: '${{ github.event.inputs.issue_number }}'
LABELS: '${{ steps.get_issue_data.outputs.labels }}'
run: |
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."
if echo "${LABELS}" | grep -q 'area/'; then
echo "Issue #${ISSUE_NUMBER_INPUT} already has 'area/' label. Stopping workflow."
exit 1
fi
@@ -110,11 +103,6 @@ jobs:
repo: context.repo.repo,
});
const allowedLabels = [
'priority/p0',
'priority/p1',
'priority/p2',
'priority/p3',
'priority/unknown',
'area/agent',
'area/enterprise',
'area/non-interactive',
@@ -161,21 +149,19 @@ jobs:
prompt: |-
## Role
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.
You are an issue triage assistant. Your role is to analyze a GitHub issue and determine the single most appropriate area/ label based on the definitions provided.
## Steps
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:
4. 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"]}
5. Output your selected label in JSON format and nothing else. Example:
{"labels_to_set": ["area/core"]}
## Guidelines
- Your output must contain exactly one area/ label and exactly one priority/ label.
- Your output must contain exactly one area/ 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.
@@ -258,38 +244,6 @@ jobs:
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: |-
${{ steps.gemini_issue_analysis.outputs.summary != '' }}
@@ -327,8 +281,8 @@ jobs:
const issueNumber = parseInt(process.env.ISSUE_NUMBER);
const labelsToAdd = parsedLabels.labels_to_set || [];
if (labelsToAdd.length !== 2) {
core.setFailed(`Expected exactly 2 labels (one area/ and one priority/), but got ${labelsToAdd.length}. Labels: ${labelsToAdd.join(', ')}`);
if (labelsToAdd.length !== 1) {
core.setFailed(`Expected exactly 1 label (area/), but got ${labelsToAdd.length}. Labels: ${labelsToAdd.join(', ')}`);
return;
}
@@ -0,0 +1,127 @@
name: 'Gemini Automated PR Labeler'
on:
pull_request_target:
types: ['opened', 'reopened', 'synchronize']
jobs:
label-pr:
timeout-minutes: 10
if: |-
${{ github.repository == 'google-gemini/gemini-cli' }}
permissions:
pull-requests: 'write'
contents: 'read'
id-token: 'write'
concurrency:
group: '${{ github.workflow }}-${{ github.event.pull_request.number }}'
cancel-in-progress: true
runs-on: 'ubuntu-latest'
steps:
- name: 'Generate GitHub App Token'
id: 'generate_token'
uses: 'actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e' # v2
with:
app-id: '${{ secrets.APP_ID }}'
private-key: '${{ secrets.PRIVATE_KEY }}'
permission-pull-requests: 'write'
- name: 'Run Gemini PR size and complexity labeller'
uses: 'google-github-actions/run-gemini-cli@a3bf79042542528e91937b3a3a6fbc4967ee3c31' # Use the specific commit SHA
env:
GH_TOKEN: '${{ steps.generate_token.outputs.token }}'
PR_NUMBER: '${{ github.event.pull_request.number }}'
REPOSITORY: '${{ github.repository }}'
with:
gcp_workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}'
gcp_project_id: '${{ vars.GOOGLE_CLOUD_PROJECT }}'
gcp_location: '${{ vars.GOOGLE_CLOUD_LOCATION }}'
gcp_service_account: '${{ vars.SERVICE_ACCOUNT_EMAIL }}'
gemini_api_key: '${{ secrets.GEMINI_API_KEY }}'
use_vertex_ai: '${{ vars.GOOGLE_GENAI_USE_VERTEXAI }}'
use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}'
settings: |
{
"coreTools": [
"run_shell_command(gh pr diff)",
"run_shell_command(gh pr edit)",
"run_shell_command(gh pr comment)",
"run_shell_command(gh pr view)"
],
"telemetry": {
"enabled": true,
"target": "gcp"
},
"sandbox": false
}
prompt: |
You are a Pull Request labeller and Feedback Assistant. Your primary goal is to improve review velocity and help maintainers prioritize their work by automatically labeling pull requests based on size and complexity, and providing guidance for overly large PRs.
Steps:
1. Retrieve Pull Request Information:
- Use `gh pr diff ${{ env.PR_NUMBER }} --repo ${{ env.REPOSITORY }}` to get the diff content.
- Parse the output from `gh pr diff` to determine the total lines of code added and deleted. Calculate `TOTAL_LINES_CHANGED`.
2. Determine Pull Request Size:
- Use `gh pr view ${{ env.PR_NUMBER }} --repo ${{ env.REPOSITORY }} --json labels` to get the current labels on the PR.
- Check the current labels and identify if any `size/*` labels already exist (e.g., `size/xs`, `size/s`, etc.).
- If an old `size/*` label is found and it is different from the newly calculated size, remove it using:
`gh pr edit ${{ env.PR_NUMBER }} --repo ${{ env.REPOSITORY }} --remove-label "size-label-to-remove"`
- Based on `TOTAL_LINES_CHANGED`, select the appropriate new size label:
- `size/xs`: < 10 lines changed
- `size/s`: 10-50 lines changed
- `size/m`: 51-200 lines changed
- `size/l`: 201-1000 lines changed
- `size/xl`: > 1000 lines changed
- Do not invent new size labels.
- Apply the newly determined `size/*` label to the pull request using:
`gh pr edit ${{ env.PR_NUMBER }} --repo ${{ env.REPOSITORY }} --add-label "your-new-size-label"`
3. Analyze Pull Request Complexity:
- Perform Code Change Analysis: Examine the content of the code changes obtained from `gh pr diff ${{ env.PR_NUMBER }} --repo ${{ env.REPOSITORY }}`. Look for indicators of complexity such as:
- Number of files changed (can be inferred from the diff headers).
- Diversity of file types (e.g., changes across different languages, configuration files, documentation).
- Presence of new external dependencies.
- Introduction of new architectural components or significant refactoring.
- Complexity of individual code changes (e.g., deeply nested logic, complex algorithms, extensive conditional statements).
- Apply Heuristic-based Complexity Assessment:
- If the PR touches a small number of files with minor changes (e.g., typos, simple bug fixes, small feature additions), categorize it as `review/quick`.
- If the PR involves changes across multiple files, introduces new features, significantly refactors existing code, or has a high line count (even within `size/l`), categorize it as `review/involved`.
- Pay close attention to changes in critical or core modules as these inherently increase complexity.
- **Only use the labels `review/quick` or `review/involved` for complexity. Do not invent new complexity labels.**
- **Remove any previous `review/*` labels if they no longer apply, similar to the size label process.**
- Apply the determined `review/*` label to the pull request using:
`gh pr edit ${{ env.PR_NUMBER }} --repo ${{ env.REPOSITORY }} --add-label "your-complexity-label"`
4. Handle Overly Large Pull Requests (`size/xl`):
- **Conditional Check:** If the pull request has been labeled `size/xl` (i.e., > 1000 lines of code changed) in Step 2, proceed to the next sub-step.
- **Post Constructive Comment:** Post a polite and helpful comment on the pull request using:
`gh pr comment ${{ env.PR_NUMBER }} --repo ${{ env.REPOSITORY }} --body "Your comment here"`
The comment body should be:
"""
Thanks for your hard work on this pull request!
This pull request is quite large, which can make it challenging and time-consuming for reviewers to go through thoroughly.
To help us review it more efficiently and get your changes merged faster, we kindly request you consider breaking this into smaller, more focused pull requests. Each smaller PR should ideally address a single logical change or a small set of related changes.
For example, you could separate out refactoring, new feature additions, and bug fixes into individual PRs. This makes it easier to understand, review, and test each component independently.
We appreciate your understanding and cooperation. Feel free to reach out if you need any assistance with this!
"""
Guidelines:
- Automation Focus: All actions should be automated and not require manual intervention.
- Non-intrusive: The system should add labels and comments but not modify the code or close the pull request.
- Polite and Constructive: All communication, especially for large PRs, must be polite, encouraging, and constructive.
- Prioritize Clarity: The labels applied should clearly convey the PR's size and complexity to reviewers.
- Adhere to Defined Labels: Only use the specified `size/*` and `review/*` labels. Do not create or apply any other labels.
- Utilize `gh CLI`: Interact with GitHub using the `gh` command-line tool for diffing, label management, and commenting.
- Execute commands strictly as described in the steps. Do not invent new commands.
- In no case should you change other pull request that are not the one you are working on. Which can be found by using env.PR_NUMBER
- Execute each step that is defined in the steps section.
- In no case should you execute code from the pull request because this could be malicious code.
- If you fail to do this step log the errors you received
@@ -45,11 +45,11 @@ jobs:
echo '🔍 Finding issues without labels...'
NO_LABEL_ISSUES="$(gh issue list --repo "${GITHUB_REPOSITORY}" \
--search 'is:open is:issue no:label' --json number,title,body)"
--search 'is:open is:issue no:label' --limit 10 --json number,title,body)"
echo '🏷️ Finding issues that need triage...'
NEED_TRIAGE_ISSUES="$(gh issue list --repo "${GITHUB_REPOSITORY}" \
--search "is:open is:issue label:\"status/need-triage\"" --limit 1000 --json number,title,body)"
--search "is:open is:issue label:\"status/need-triage\"" --limit 10 --json number,title,body)"
echo '🔄 Merging and deduplicating issues...'
ISSUES="$(echo "${NO_LABEL_ISSUES}" "${NEED_TRIAGE_ISSUES}" | jq -c -s 'add | unique_by(.number)')"
@@ -0,0 +1,152 @@
name: '🔒 Gemini Scheduled Stale Issue Closer'
on:
schedule:
- cron: '0 0 * * 0' # Every Sunday at midnight UTC
workflow_dispatch:
inputs:
dry_run:
description: 'Run in dry-run mode (no changes applied)'
required: false
default: false
type: 'boolean'
concurrency:
group: '${{ github.workflow }}'
cancel-in-progress: true
defaults:
run:
shell: 'bash'
jobs:
close-stale-issues:
runs-on: 'ubuntu-latest'
permissions:
issues: 'write'
steps:
- name: 'Generate GitHub App Token'
id: 'generate_token'
uses: 'actions/create-github-app-token@v1'
with:
app-id: '${{ secrets.APP_ID }}'
private-key: '${{ secrets.PRIVATE_KEY }}'
permission-issues: 'write'
- name: 'Process Stale Issues'
uses: 'actions/github-script@v7'
env:
DRY_RUN: '${{ inputs.dry_run }}'
with:
github-token: '${{ steps.generate_token.outputs.token }}'
script: |
const dryRun = process.env.DRY_RUN === 'true';
if (dryRun) {
core.info('DRY RUN MODE ENABLED: No changes will be applied.');
}
const batchLabel = 'Stale';
const threeMonthsAgo = new Date();
threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3);
const tenDaysAgo = new Date();
tenDaysAgo.setDate(tenDaysAgo.getDate() - 10);
core.info(`Cutoff date for creation: ${threeMonthsAgo.toISOString()}`);
core.info(`Cutoff date for updates: ${tenDaysAgo.toISOString()}`);
const query = `repo:${context.repo.owner}/${context.repo.repo} is:issue is:open created:<${threeMonthsAgo.toISOString()}`;
core.info(`Searching with query: ${query}`);
const itemsToCheck = await github.paginate(github.rest.search.issuesAndPullRequests, {
q: query,
sort: 'created',
order: 'asc',
per_page: 100
});
core.info(`Found ${itemsToCheck.length} open issues to check.`);
let processedCount = 0;
for (const issue of itemsToCheck) {
const createdAt = new Date(issue.created_at);
const updatedAt = new Date(issue.updated_at);
const reactionCount = issue.reactions.total_count;
// Basic thresholds
if (reactionCount >= 5) {
continue;
}
// Skip if it has a maintainer label
if (issue.labels.some(label => label.name.toLowerCase().includes('maintainer'))) {
continue;
}
let isStale = updatedAt < tenDaysAgo;
// If apparently active, check if it's only bot activity
if (!isStale) {
try {
const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
per_page: 100,
sort: 'created',
direction: 'desc'
});
const lastHumanComment = comments.data.find(comment => comment.user.type !== 'Bot');
if (lastHumanComment) {
isStale = new Date(lastHumanComment.created_at) < tenDaysAgo;
} else {
// No human comments. Check if creator is human.
if (issue.user.type !== 'Bot') {
isStale = createdAt < tenDaysAgo;
} else {
isStale = true; // Bot created, only bot comments
}
}
} catch (error) {
core.warning(`Failed to fetch comments for issue #${issue.number}: ${error.message}`);
continue;
}
}
if (isStale) {
processedCount++;
const message = `Closing stale issue #${issue.number}: "${issue.title}" (${issue.html_url})`;
core.info(message);
if (!dryRun) {
// Add label
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: [batchLabel]
});
// Add comment
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: 'Hello! As part of our effort to keep our backlog manageable and focus on the most active issues, we are tidying up older reports.\n\nIt looks like this issue hasn\'t been active for a while, so we are closing it for now. However, if you are still experiencing this bug on the latest stable build, please feel free to comment on this issue or create a new one with updated details.\n\nThank you for your contribution!'
});
// Close issue
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
state: 'closed',
state_reason: 'not_planned'
});
}
}
}
core.info(`\nTotal issues processed: ${processedCount}`);
@@ -0,0 +1,40 @@
name: 'Label Child Issues for Project Rollup'
on:
issues:
types: ['opened', 'edited', 'reopened']
jobs:
labeler:
runs-on: 'ubuntu-latest'
permissions:
issues: 'write'
steps:
- name: 'Check for Parent Workstream and Apply Label'
uses: 'actions/github-script@v7'
with:
script: |
const issue = context.payload.issue;
const labelToAdd = 'workstream-rollup';
// --- Define the FULL URLs of the allowed parent workstreams ---
const allowedParentUrls = [
'https://api.github.com/repos/google-gemini/gemini-cli/issues/15374',
'https://api.github.com/repos/google-gemini/gemini-cli/issues/15456',
'https://api.github.com/repos/google-gemini/gemini-cli/issues/15324'
];
// Check if the issue has a parent_issue_url and if it's in our allowed list.
if (issue && issue.parent_issue_url && allowedParentUrls.includes(issue.parent_issue_url)) {
console.log(`SUCCESS: Issue #${issue.number} is a child of a target workstream (${issue.parent_issue_url}). Adding label.`);
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: [labelToAdd]
});
} else if (issue && issue.parent_issue_url) {
console.log(`FAILURE: Issue #${issue.number} has a parent, but it's not a target workstream. Parent URL: ${issue.parent_issue_url}`);
} else {
console.log(`FAILURE: Issue #${issue.number} is not a child of any issue. No action taken.`);
}
+11 -6
View File
@@ -3,11 +3,15 @@ name: 'Trigger E2E'
on:
workflow_dispatch:
inputs:
branch_ref:
description: 'Branch to run on'
required: true
default: 'main'
repo_name:
description: 'Repository name (e.g., owner/repo)'
required: false
type: 'string'
head_sha:
description: 'SHA of the commit to test'
required: false
type: 'string'
pull_request:
jobs:
save_repo_name:
@@ -15,11 +19,12 @@ jobs:
steps:
- name: 'Save Repo name'
env:
# 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 }}'
REPO_NAME: '${{ github.event.inputs.repo_name || github.event.pull_request.head.repo.full_name }}'
HEAD_SHA: '${{ github.event.inputs.head_sha || github.event.pull_request.head.sha }}'
run: |
mkdir -p ./pr
echo '${{ env.REPO_NAME }}' > ./pr/repo_name
echo '${{ env.HEAD_SHA }}' > ./pr/head_sha
- uses: 'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02' # ratchet:actions/upload-artifact@v4
with:
name: 'repo_name'
+1
View File
@@ -55,3 +55,4 @@ gha-creds-*.json
patch_output.log
.genkit
.gemini-clipboard/
+3 -1
View File
@@ -41,7 +41,9 @@ This project follows
The process for contributing code is as follows:
1. **Find an issue** that you want to work on.
1. **Find an issue** that you want to work on. If an issue is tagged as
"🔒Maintainers only", this means it is reserved for project maintainers. We
will not accept pull requests related to these issues.
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`.
+9
View File
@@ -391,6 +391,15 @@ When working in the `/docs` directory, follow the guidelines in this section:
Only write high-value comments if at all. Avoid talking to the user through
comments.
## Logging and Error Handling
- **Avoid Console Statements:** Do not use `console.log`, `console.error`, or
similar methods directly.
- **Non-User-Facing Logs:** For developer-facing debug messages, use
`debugLogger` (from `@google/gemini-cli-core`).
- **User-Facing Feedback:** To surface errors or warnings to the user, use
`coreEvents.emitFeedback` (from `@google/gemini-cli-core`).
## General requirements
- If there is something you do not understand or is ambiguous, seek confirmation
+1 -1
View File
@@ -1,7 +1,7 @@
# Gemini CLI
[![Gemini CLI CI](https://github.com/google-gemini/gemini-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/google-gemini/gemini-cli/actions/workflows/ci.yml)
[![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)
[![Gemini CLI E2E (Chained)](https://github.com/google-gemini/gemini-cli/actions/workflows/chained_e2e.yml/badge.svg)](https://github.com/google-gemini/gemini-cli/actions/workflows/chained_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)
[![View Code Wiki](https://www.gstatic.com/_/boq-sdlc-agents-ui/_/r/YUi5dj2UWvE.svg)](https://codewiki.google/github.com/google-gemini/gemini-cli)
+134 -18
View File
@@ -1,15 +1,131 @@
# Gemini CLI Changelog
# Gemini CLI release notes
Wondering what's new in Gemini CLI? This document provides key highlights and
notable changes to Gemini CLI.
Gemini CLI has three major release channels: nightly, preview, and stable. For
most users, we recommend the stable release.
## v0.15.0 - Gemini CLI weekly update - 2025-11-03
On this page, you can find information regarding the current releases and
announcements from each release.
- **🎉 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!
For the full changelog, refer to
[Releases - google-gemini/gemini-cli](https://github.com/google-gemini/gemini-cli/releases)
on GitHub.
## Current releases
| Release channel | Notes |
| :-------------------- | :---------------------------------------------- |
| Nightly | Nightly release with the most recent changes. |
| [Preview](preview.md) | Experimental features ready for early feedback. |
| [Stable](latest.md) | Stable, recommended for general use. |
## Announcements: v0.21.0 - 2025-12-15
- **⚡️⚡️⚡️ Gemini 3 Flash + Gemini CLI:** Better, faster and cheaper than 2.5
Pro - and in some scenarios better than 3 Pro! For paid tiers + free tier
users who were on the wait list enable **Preview Features** in `/settings.`
- For more information:
[Gemini 3 Flash is now available in Gemini CLI](https://developers.googleblog.com/gemini-3-flash-is-now-available-in-gemini-cli/).
- 🎉 Gemini CLI Extensions:
- Rill: Utilize natural language to analyze Rill data, enabling the
exploration of metrics and trends without the need for manual queries.
`gemini extensions install https://github.com/rilldata/rill-gemini-extension`
- Browserbase: Interact with web pages, take screenshots, extract information,
and perform automated actions with atomic precision.
`gemini extensions install https://github.com/browserbase/mcp-server-browserbase`
- Quota Visibility: The `/stats` command now displays quota information for all
available models, including those not used in the current session. (@sehoon38)
- Fuzzy Setting Search: Users can now quickly find settings using fuzzy search
within the settings dialog. (@sehoon38)
- MCP Resource Support: Users can now discover, view, and search through
resources using the @ command. (@MrLesk)
- Auto-execute Simple Slash Commands: Simple slash commands are now executed
immediately on enter. (@jackwotherspoon)
## Announcements: v0.20.0 - 2025-12-01
- **Multi-file Drag & Drop:** Users can now drag and drop multiple files into
the terminal, and the CLI will automatically prefix each valid path with `@`.
([pr](https://github.com/google-gemini/gemini-cli/pull/14832) by
[@jackwotherspoon](https://github.com/jackwotherspoon))
- **Persistent "Always Allow" Policies:** Users can now save "Always Allow"
decisions for tool executions, with granular control over specific shell
commands and multi-cloud platform tools.
([pr](https://github.com/google-gemini/gemini-cli/pull/14737) by
[@allenhutchison](https://github.com/allenhutchison))
## Announcements: v0.19.0 - 2025-11-24
- 🎉 **New extensions:**
- **Eleven Labs:** Create, play, manage your audio play tracks with the Eleven
Labs Gemini CLI extension:
`gemini extensions install https://github.com/elevenlabs/elevenlabs-mcp`
- **Zed integration:** Users can now leverage Gemini 3 within the Zed
integration after enabling "Preview Features" in their CLIs `/settings`.
([pr](https://github.com/google-gemini/gemini-cli/pull/13398) by
[@benbrandt](https://github.com/benbrandt))
- **Interactive shell:**
- **Click-to-Focus:** When "Use Alternate Buffer" setting is enabled, users
can click within the embedded shell output to focus it for input.
([pr](https://github.com/google-gemini/gemini-cli/pull/13341) by
[@galz10](https://github.com/galz10))
- **Loading phrase:** Clearly indicates when the interactive shell is awaiting
user input. ([vid](https://imgur.com/a/kjK8bUK),
[pr](https://github.com/google-gemini/gemini-cli/pull/12535) by
[@jackwotherspoon](https://github.com/jackwotherspoon))
## Announcements: v0.18.0 - 2025-11-17
- 🎉 **New extensions:**
- **Google Workspace**: Integrate Gemini CLI with your Workspace data. Write
docs, build slides, chat with others or even get your calc on in sheets:
`gemini extensions install https://github.com/gemini-cli-extensions/workspace`
- Blog:
[https://allen.hutchison.org/2025/11/19/bringing-the-office-to-the-terminal/](https://allen.hutchison.org/2025/11/19/bringing-the-office-to-the-terminal/)
- **Redis:** Manage and search data in Redis with natural language:
`gemini extensions install https://github.com/redis/mcp-redis`
- **Anomalo:** Query your data warehouse table metadata and quality status
through commands and natural language:
`gemini extensions install https://github.com/datagravity-ai/anomalo-gemini-extension`
- **Experimental permission improvements:** We are now experimenting with a new
policy engine in Gemini CLI. This allows users and administrators to create
fine-grained policy for tool calls. Currently behind a flag. See
[policy engine documentation](../core/policy-engine.md) for more information.
- Blog:
[https://allen.hutchison.org/2025/11/26/the-guardrails-of-autonomy/](https://allen.hutchison.org/2025/11/26/the-guardrails-of-autonomy/)
- **Gemini 3 support for paid:** Gemini 3 support has been rolled out to all API
key, Google AI Pro or Google AI Ultra (for individuals, not businesses) and
Gemini Code Assist Enterprise users. Enable it via `/settings` and toggling on
**Preview Features**.
- **Updated UI rollback:** Weve temporarily rolled back our updated UI to give
it more time to bake. This means for a time you wont have embedded scrolling
or mouse support. You can re-enable with `/settings` -> **Use Alternate Screen
Buffer** -> `true`.
- **Model in history:** Users can now toggle in `/settings` to display model in
their chat history. ([gif](https://imgur.com/a/uEmNKnQ),
[pr](https://github.com/google-gemini/gemini-cli/pull/13034) by
[@scidomino](https://github.com/scidomino))
- **Multi-uninstall:** Users can now uninstall multiple extensions with a single
command. ([pic](https://imgur.com/a/9Dtq8u2),
[pr](https://github.com/google-gemini/gemini-cli/pull/13016) by
[@JayadityaGit](https://github.com/JayadityaGit))
## Announcements: v0.16.0 - 2025-11-10
- **Gemini 3 + Gemini CLI:** launch 🚀🚀🚀
- **Data Commons Gemini CLI Extension** - A new Data Commons Gemini CLI
extension that lets you query open-source statistical data from
datacommons.org. **To get started, you'll need a Data Commons API key and uv
installed**. These and other details to get you started with the extension can
be found at
[https://github.com/gemini-cli-extensions/datacommons](https://github.com/gemini-cli-extensions/datacommons).
## Announcements: v0.15.0 - 2025-11-03
- **🎉 Seamless scrollable UI and 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:**
@@ -49,7 +165,7 @@ notable changes to Gemini CLI.
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
## Announcements: v0.12.0 - 2025-10-27
![Codebase investigator subagent in Gemini CLI.](https://i.imgur.com/4J1njsx.png)
@@ -112,7 +228,7 @@ notable changes to Gemini CLI.
([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
## Announcements: v0.11.0 - 2025-10-20
![Gemini CLI and Jules](https://storage.googleapis.com/gweb-developer-goog-blog-assets/images/Jules_Extension_-_Blog_Header_O346JNt.original.png)
@@ -155,7 +271,7 @@ notable changes to Gemini CLI.
([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
## Announcements: v0.10.0 - 2025-10-13
- **Polish:** The team has been heads down bug fixing and investing heavily into
polishing existing flows, tools, and interactions.
@@ -172,7 +288,7 @@ notable changes to Gemini CLI.
([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
## Announcements: v0.9.0 - 2025-10-06
- 🎉 **Interactive Shell:** Run interactive commands like `vim`, `rebase -i`, or
even `gemini` 😎 directly in Gemini CLI:
@@ -197,7 +313,7 @@ notable changes to Gemini CLI.
([pr](https://github.com/google-gemini/gemini-cli/pull/10108) by
[@sgnagnarella](https://github.com/sgnagnarella))
## v0.8.0 - Gemini CLI weekly update - 2025-09-29
## Announcements: v0.8.0 - 2025-09-29
- 🎉 **Announcing Gemini CLI Extensions** 🎉
- Completely customize your Gemini CLI experience to fit your workflow.
@@ -236,7 +352,7 @@ notable changes to Gemini CLI.
changes, smaller features, UI updates, reliability and bug fixes + general
polish made it in this week!
## v0.7.0 - Gemini CLI weekly update - 2025-09-22
## Announcements: v0.7.0 - 2025-09-22
- 🎉**Build your own Gemini CLI IDE plugin:** We've published a spec for
creating IDE plugins to enable rich context-aware experiences and native
@@ -274,7 +390,7 @@ notable changes to Gemini CLI.
changes, smaller features, UI updates, reliability and bug fixes + general
polish made it in this week!
## v0.6.0 - Gemini CLI weekly update - 2025-09-15
## Announcements: v0.6.0 - 2025-09-15
- 🎉 **Higher limits for Google AI Pro and Ultra subscribers:** Were psyched to
finally announce that Google AI Pro and AI Ultra subscribers now get access to
@@ -355,7 +471,7 @@ notable changes to Gemini CLI.
changes, smaller features, UI updates, reliability and bug fixes + general
polish made it in this week!
## v0.5.0 - Gemini CLI weekly update - 2025-09-08
## Announcements: v0.5.0 - 2025-09-08
- 🎉**FastMCP + Gemini CLI**🎉: Quickly install and manage your Gemini CLI MCP
servers with FastMCP ([video](https://imgur.com/a/m8QdCPh),
@@ -400,7 +516,7 @@ notable changes to Gemini CLI.
changes, smaller features, UI updates, reliability and bug fixes + general
polish made it in this week!
## v0.4.0 - Gemini CLI weekly update - 2025-09-01
## Announcements: v0.4.0 - 2025-09-01
- 🎉**Gemini CLI CloudRun and Security Integrations**🎉: Automate app deployment
and security analysis with CloudRun and Security extension integrations. Once
+225
View File
@@ -0,0 +1,225 @@
# Latest stable release: v0.21.0 - v0.21.1
Released: December 16, 2025
For most users, our latest stable release is the recommended release. Install
the latest stable version with:
```
npm install -g @google/gemini-cli
```
## Highlights
- **⚡️⚡️⚡️ Gemini 3 Flash + Gemini CLI:** If you are a paid user, you can now
enable Gemini 3 Pro and Gemini 3 Flash. Go to `/settings` and set **Preview
Features** to `true` to enable Gemini 3. For more information:
[Gemini 3 Flash is now available in Gemini CLI](https://developers.googleblog.com/gemini-3-flash-is-now-available-in-gemini-cli/).
## What's Changed
- refactor(stdio): always patch stdout and use createWorkingStdio for clean
output by @allenhutchison in
https://github.com/google-gemini/gemini-cli/pull/14159
- chore(release): bump version to 0.21.0-nightly.20251202.2d935b379 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/14409
- implement fuzzy search inside settings by @sehoon38 in
https://github.com/google-gemini/gemini-cli/pull/13864
- feat: enable message bus integration by default by @allenhutchison in
https://github.com/google-gemini/gemini-cli/pull/14329
- docs: Recommend using --debug intead of --verbose for CLI debugging by @bbiggs
in https://github.com/google-gemini/gemini-cli/pull/14334
- feat: consolidate remote MCP servers to use `url` in config by
@jackwotherspoon in https://github.com/google-gemini/gemini-cli/pull/13762
- Restrict integration tests tools by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14403
- track github repository names in telemetry events by @IamRiddhi in
https://github.com/google-gemini/gemini-cli/pull/13670
- Allow telemetry exporters to GCP to utilize user's login credentials, if
requested by @mboshernitsan in
https://github.com/google-gemini/gemini-cli/pull/13778
- refactor(editor): use const assertion for editor types with single source of
truth by @amsminn in https://github.com/google-gemini/gemini-cli/pull/8604
- fix(security): Fix npm audit vulnerabilities in glob and body-parser by
@afarber in https://github.com/google-gemini/gemini-cli/pull/14090
- Add new enterprise instructions by @chrstnb in
https://github.com/google-gemini/gemini-cli/pull/8641
- feat(hooks): Hook Session Lifecycle & Compression Integration by @Edilmo in
https://github.com/google-gemini/gemini-cli/pull/14151
- Avoid triggering refreshStatic unless there really is a banner to display. by
@jacob314 in https://github.com/google-gemini/gemini-cli/pull/14328
- feat(hooks): Hooks Commands Panel, Enable/Disable, and Migrate by @Edilmo in
https://github.com/google-gemini/gemini-cli/pull/14225
- fix: Bundle default policies for npx distribution by @allenhutchison in
https://github.com/google-gemini/gemini-cli/pull/14457
- feat(hooks): Hook System Documentation by @Edilmo in
https://github.com/google-gemini/gemini-cli/pull/14307
- Fix tests by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14458
- feat: add scheduled workflow to close stale issues by @galz10 in
https://github.com/google-gemini/gemini-cli/pull/14404
- feat: Support Extension Hooks with Security Warning by @abhipatel12 in
https://github.com/google-gemini/gemini-cli/pull/14460
- feat: Add enableAgents experimental flag by @adamfweidman in
https://github.com/google-gemini/gemini-cli/pull/14371
- docs: fix typo 'socus' to 'focus' in todos.md by @Viktor286 in
https://github.com/google-gemini/gemini-cli/pull/14374
- Markdown export: move the emoji to the end of the line by @mhansen in
https://github.com/google-gemini/gemini-cli/pull/12278
- fix(acp): prevent unnecessary credential cache clearing on re-authent… by
@h-michael in https://github.com/google-gemini/gemini-cli/pull/9410
- fix(cli): Fix word navigation for CJK characters by @SandyTao520 in
https://github.com/google-gemini/gemini-cli/pull/14475
- Remove example extension by @chrstnb in
https://github.com/google-gemini/gemini-cli/pull/14376
- Add commands for listing and updating per-extension settings by @chrstnb in
https://github.com/google-gemini/gemini-cli/pull/12664
- chore(tests): remove obsolete test for hierarchical memory by @pareshjoshij in
https://github.com/google-gemini/gemini-cli/pull/13122
- feat(cli): support /copy in remote sessions using OSC52 by @ismellpillows in
https://github.com/google-gemini/gemini-cli/pull/13471
- Update setting search UX by @Adib234 in
https://github.com/google-gemini/gemini-cli/pull/14451
- Fix(cli): Improve Homebrew update instruction to specify gemini-cli by
@DaanVersavel in https://github.com/google-gemini/gemini-cli/pull/14502
- do not toggle the setting item when entering space by @sehoon38 in
https://github.com/google-gemini/gemini-cli/pull/14489
- fix: improve retry logic for fetch errors and network codes by @megha1188 in
https://github.com/google-gemini/gemini-cli/pull/14439
- remove unused isSearching field by @sehoon38 in
https://github.com/google-gemini/gemini-cli/pull/14509
- feat(mcp): add `--type` alias for `--transport` flag in gemini mcp add by
@jackwotherspoon in https://github.com/google-gemini/gemini-cli/pull/14503
- feat(cli): Move key restore logic to core by @cocosheng-g in
https://github.com/google-gemini/gemini-cli/pull/13013
- feat: add auto-execute on Enter behavior to argumentless MCP prompts by
@jackwotherspoon in https://github.com/google-gemini/gemini-cli/pull/14510
- fix(shell): cursor visibility when using interactive mode by @aswinashok44 in
https://github.com/google-gemini/gemini-cli/pull/14095
- Adding session id as part of json o/p by @MJjainam in
https://github.com/google-gemini/gemini-cli/pull/14504
- fix(extensions): resolve GitHub API 415 error for source tarballs by
@jpoehnelt in https://github.com/google-gemini/gemini-cli/pull/13319
- fix(client): Correctly latch hasFailedCompressionAttempt flag by @pareshjoshij
in https://github.com/google-gemini/gemini-cli/pull/13002
- Disable flaky extension reloading test on linux by @chrstnb in
https://github.com/google-gemini/gemini-cli/pull/14528
- Add support for MCP dynamic tool update by `notifications/tools/list_changed`
by @Adib234 in https://github.com/google-gemini/gemini-cli/pull/14375
- Fix privacy screen for legacy tier users by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14522
- feat: Exclude maintainer labeled issues from stale issue closer by @galz10 in
https://github.com/google-gemini/gemini-cli/pull/14532
- Grant chained workflows proper permission. by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14534
- Make trigger_e2e manually fireable. by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14547
- Write e2e status to local repo not forked repo by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14549
- Fixes [API Error: Cannot read properties of undefined (reading 'error')] by
@silviojr in https://github.com/google-gemini/gemini-cli/pull/14553
- Trigger chained e2e tests on all pull requests by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14551
- Fix bug in the shellExecutionService resulting in both truncation and 3X bloat
by @jacob314 in https://github.com/google-gemini/gemini-cli/pull/14545
- Fix issue where we were passing the model content reflecting terminal line
wrapping. by @jacob314 in
https://github.com/google-gemini/gemini-cli/pull/14566
- chore/release: bump version to 0.21.0-nightly.20251204.3da4fd5f7 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/14476
- feat(sessions): use 1-line generated session summary to describe sessions by
@jackwotherspoon in https://github.com/google-gemini/gemini-cli/pull/14467
- Use Robot PAT for chained e2e merge queue skipper by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14585
- fix(core): improve API response error handling and retry logic by @mattKorwel
in https://github.com/google-gemini/gemini-cli/pull/14563
- Docs: Model routing clarification by @jkcinouye in
https://github.com/google-gemini/gemini-cli/pull/14373
- expose previewFeatures flag in a2a by @sehoon38 in
https://github.com/google-gemini/gemini-cli/pull/14550
- Fix emoji width in debug console. by @jacob314 in
https://github.com/google-gemini/gemini-cli/pull/14593
- Fully detach autoupgrade process by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14595
- Docs: Update Gemini 3 on Gemini CLI documentation by @jkcinouye in
https://github.com/google-gemini/gemini-cli/pull/14601
- Disallow floating promises. by @gundermanc in
https://github.com/google-gemini/gemini-cli/pull/14605
- chore/release: bump version to 0.21.0-nightly.20251207.025e450ac by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/14662
- feat(modelAvailabilityService): integrate model availability service into
backend logic by @adamfweidman in
https://github.com/google-gemini/gemini-cli/pull/14470
- Add prompt_id propagation in a2a-server task by @koxkox111 in
https://github.com/google-gemini/gemini-cli/pull/14581
- Fix: Prevent freezing in non-interactive Gemini CLI when debug mode is enabled
by @parthasaradhie in https://github.com/google-gemini/gemini-cli/pull/14580
- fix(audio): improve reading of audio files by @jackwotherspoon in
https://github.com/google-gemini/gemini-cli/pull/14658
- Update automated triage workflow to stop assigning priority labels by
@skeshive in https://github.com/google-gemini/gemini-cli/pull/14717
- set failed status when chained e2e fails by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14725
- feat(github action) Triage and Label Pull Requests by Size and Comple… by
@DaanVersavel in https://github.com/google-gemini/gemini-cli/pull/5571
- refactor(telemetry): Improve previous PR that allows telemetry to use the CLI
auth and add testing by @mboshernitsan in
https://github.com/google-gemini/gemini-cli/pull/14589
- Always set status in chained_e2e workflow by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14730
- feat: Add OTEL log event `gemini_cli.startup_stats` for startup stats. by
@kevin-ramdass in https://github.com/google-gemini/gemini-cli/pull/14734
- feat: auto-execute on slash command completion functions by @jackwotherspoon
in https://github.com/google-gemini/gemini-cli/pull/14584
- Docs: Proper release notes by @jkcinouye in
https://github.com/google-gemini/gemini-cli/pull/14405
- Add support for user-scoped extension settings by @chrstnb in
https://github.com/google-gemini/gemini-cli/pull/13748
- refactor(core): Improve environment variable handling in shell execution by
@galz10 in https://github.com/google-gemini/gemini-cli/pull/14742
- Remove old E2E Workflows by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14749
- fix: handle missing local extension config and skip hooks when disabled by
@abhipatel12 in https://github.com/google-gemini/gemini-cli/pull/14744
- chore/release: bump version to 0.21.0-nightly.20251209.ec9a8c7a7 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/14751
- feat: Add support for MCP Resources by @MrLesk in
https://github.com/google-gemini/gemini-cli/pull/13178
- Always set pending status in E2E tests by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14756
- fix(lint): upgrade pip and use public pypi for yamllint by @allenhutchison in
https://github.com/google-gemini/gemini-cli/pull/14746
- fix: use Gemini API supported image formats for clipboard by @jackwotherspoon
in https://github.com/google-gemini/gemini-cli/pull/14762
- feat(a2a): Introduce restore command for a2a server by @cocosheng-g in
https://github.com/google-gemini/gemini-cli/pull/13015
- allow final:true to be returned on a2a server edit calls. by @DavidAPierce in
https://github.com/google-gemini/gemini-cli/pull/14747
- (fix) Automated pr labeller by @DaanVersavel in
https://github.com/google-gemini/gemini-cli/pull/14788
- Update CODEOWNERS by @kklashtorny1 in
https://github.com/google-gemini/gemini-cli/pull/14830
- Docs: Fix errors preventing site rebuild. by @jkcinouye in
https://github.com/google-gemini/gemini-cli/pull/14842
- chore(deps): bump express from 5.1.0 to 5.2.0 by @dependabot[bot] in
https://github.com/google-gemini/gemini-cli/pull/14325
- fix(patch): cherry-pick 3f5f030 to release/v0.21.0-preview.0-pr-14843 to patch
version v0.21.0-preview.0 and create version 0.21.0-preview.1 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/14851
- fix(patch): cherry-pick ee6556c to release/v0.21.0-preview.1-pr-14691 to patch
version v0.21.0-preview.1 and create version 0.21.0-preview.2 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/14908
- fix(patch): cherry-pick 54de675 to release/v0.21.0-preview.2-pr-14961 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/14968
- fix(patch): cherry-pick 12cbe32 to release/v0.21.0-preview.3-pr-15000 to patch
version v0.21.0-preview.3 and create version 0.21.0-preview.4 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/15003
- fix(patch): cherry-pick edbe548 to release/v0.21.0-preview.4-pr-15007 to patch
version v0.21.0-preview.4 and create version 0.21.0-preview.5 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/15015
- fix(patch): cherry-pick 2995af6 to release/v0.21.0-preview.5-pr-15131 to patch
version v0.21.0-preview.5 and create version 0.21.0-preview.6 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/15153
**Full Changelog**:
https://github.com/google-gemini/gemini-cli/compare/v0.20.2...v0.21.0
+129
View File
@@ -0,0 +1,129 @@
# Preview release: Release v0.22.0-preview.0
Released: December 16, 2025
Our preview release includes the latest, new, and experimental features. This
release may not be as stable as our [latest weekly release](latest.md).
To install the preview release:
```
npm install -g @google/gemini-cli@preview
```
## What's Changed
- feat(ide): fallback to GEMINI_CLI_IDE_AUTH_TOKEN env var by @skeshive in
https://github.com/google-gemini/gemini-cli/pull/14843
- feat: display quota stats for unused models in /stats by @sehoon38 in
https://github.com/google-gemini/gemini-cli/pull/14764
- feat: ensure codebase investigator uses preview model when main agent does by
@abhipatel12 in https://github.com/google-gemini/gemini-cli/pull/14412
- chore: add closing reason to stale bug workflow by @galz10 in
https://github.com/google-gemini/gemini-cli/pull/14861
- Send the model and CLI version with the user agent by @gundermanc in
https://github.com/google-gemini/gemini-cli/pull/14865
- refactor(sessions): move session summary generation to startup by
@jackwotherspoon in https://github.com/google-gemini/gemini-cli/pull/14691
- Limit search depth in path corrector by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14869
- Fix: Correct typo in code comment by @kuishou68 in
https://github.com/google-gemini/gemini-cli/pull/14671
- feat(core): Plumbing for late resolution of model configs. by @joshualitt in
https://github.com/google-gemini/gemini-cli/pull/14597
- feat: attempt more error parsing by @adamfweidman in
https://github.com/google-gemini/gemini-cli/pull/14899
- Add missing await. by @gundermanc in
https://github.com/google-gemini/gemini-cli/pull/14910
- feat(core): Add support for transcript_path in hooks for git-ai/Gemini
extension by @svarlamov in
https://github.com/google-gemini/gemini-cli/pull/14663
- refactor: implement DelegateToAgentTool with discriminated union by
@abhipatel12 in https://github.com/google-gemini/gemini-cli/pull/14769
- feat: reset availabilityService on /auth by @adamfweidman in
https://github.com/google-gemini/gemini-cli/pull/14911
- chore/release: bump version to 0.21.0-nightly.20251211.8c83e1ea9 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/14924
- Fix: Correctly detect MCP tool errors by @kevin-ramdass in
https://github.com/google-gemini/gemini-cli/pull/14937
- increase labeler timeout by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14922
- tool(cli): tweak the frontend tool to be aware of more core files from the cli
by @jacob314 in https://github.com/google-gemini/gemini-cli/pull/14962
- feat(cli): polish cached token stats and simplify stats display when quota is
present. by @jacob314 in
https://github.com/google-gemini/gemini-cli/pull/14961
- feat(settings-validation): add validation for settings schema by @lifefloating
in https://github.com/google-gemini/gemini-cli/pull/12929
- fix(ide): Update IDE extension to write auth token in env var by @skeshive in
https://github.com/google-gemini/gemini-cli/pull/14999
- Revert "chore(deps): bump express from 5.1.0 to 5.2.0" by @skeshive in
https://github.com/google-gemini/gemini-cli/pull/14998
- feat(a2a): Introduce /init command for a2a server by @cocosheng-g in
https://github.com/google-gemini/gemini-cli/pull/13419
- feat: support multi-file drag and drop of images by @jackwotherspoon in
https://github.com/google-gemini/gemini-cli/pull/14832
- fix(policy): allow codebase_investigator by default in read-only policy by
@abhipatel12 in https://github.com/google-gemini/gemini-cli/pull/15000
- refactor(ide ext): Update port file name + switch to 1-based index for
characters + remove truncation text by @skeshive in
https://github.com/google-gemini/gemini-cli/pull/10501
- fix(vscode-ide-companion): correct license generation for workspace
dependencies by @skeshive in
https://github.com/google-gemini/gemini-cli/pull/15004
- fix: temp fix for subagent invocation until subagent delegation is merged to
stable by @abhipatel12 in
https://github.com/google-gemini/gemini-cli/pull/15007
- test: update ide detection tests to make them more robust when run in an ide
by @kevin-ramdass in https://github.com/google-gemini/gemini-cli/pull/15008
- Remove flex from stats display. See snapshots for diffs. by @jacob314 in
https://github.com/google-gemini/gemini-cli/pull/14983
- Add license field into package.json by @jb-perez in
https://github.com/google-gemini/gemini-cli/pull/14473
- feat: Persistent "Always Allow" policies with granular shell & MCP support by
@allenhutchison in https://github.com/google-gemini/gemini-cli/pull/14737
- chore/release: bump version to 0.21.0-nightly.20251212.54de67536 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/14969
- fix(core): commandPrefix word boundary and compound command safety by
@allenhutchison in https://github.com/google-gemini/gemini-cli/pull/15006
- chore(docs): add 'Maintainers only' label info to CONTRIBUTING.md by @jacob314
in https://github.com/google-gemini/gemini-cli/pull/14914
- Refresh hooks when refreshing extensions. by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14918
- Add clarity to error messages by @gsehgal in
https://github.com/google-gemini/gemini-cli/pull/14879
- chore : remove a redundant tip by @JayadityaGit in
https://github.com/google-gemini/gemini-cli/pull/14947
- chore/release: bump version to 0.21.0-nightly.20251213.977248e09 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/15029
- Disallow redundant typecasts. by @gundermanc in
https://github.com/google-gemini/gemini-cli/pull/15030
- fix(auth): prioritize GEMINI_API_KEY env var and skip unnecessary key… by
@galz10 in https://github.com/google-gemini/gemini-cli/pull/14745
- fix: use zod for safety check result validation by @allenhutchison in
https://github.com/google-gemini/gemini-cli/pull/15026
- update(telemetry): add hashed_extension_name to field to extension events by
@kiranani in https://github.com/google-gemini/gemini-cli/pull/15025
- fix: similar to policy-engine, throw error in case of requiring tool execution
confirmation for non-interactive mode by @MayV in
https://github.com/google-gemini/gemini-cli/pull/14702
- Clean up processes in integration tests by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/15102
- docs: update policy engine getting started and defaults by @NTaylorMullen in
https://github.com/google-gemini/gemini-cli/pull/15105
- Fix tool output fragmentation by encapsulating content in functionResponse by
@abhipatel12 in https://github.com/google-gemini/gemini-cli/pull/13082
- Simplify method signature. by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/15114
- Show raw input token counts in json output. by @jacob314 in
https://github.com/google-gemini/gemini-cli/pull/15021
- fix: Mark A2A requests as interactive by @MayV in
https://github.com/google-gemini/gemini-cli/pull/15108
- use previewFeatures to determine which pro model to use for A2A by @sehoon38
in https://github.com/google-gemini/gemini-cli/pull/15131
- refactor(cli): fix settings merging so that settings using the new json format
take priority over ones using the old format by @jacob314 in
https://github.com/google-gemini/gemini-cli/pull/15116
**Full Changelog**:
https://github.com/google-gemini/gemini-cli/compare/v0.21.0-preview.6...v0.22.0-preview.0
+896
View File
@@ -0,0 +1,896 @@
# Gemini CLI changelog
Gemini CLI has three major release channels: nightly, preview, and stable. For
most users, we recommend the stable release.
On this page, you can find information regarding the current releases and
highlights from each release.
For the full changelog, including nightly releases, refer to
[Releases - google-gemini/gemini-cli](https://github.com/google-gemini/gemini-cli/releases)
on GitHub.
## Current Releases
| Release channel | Notes |
| :------------------------------------------ | :---------------------------------------------- |
| Nightly | Nightly release with the most recent changes. |
| [Preview](#release-v0220-preview-0-preview) | Experimental features ready for early feedback. |
| [Latest](#release-v0210---v0211-latest) | Stable, recommended for general use. |
## Release v0.21.0 - v0.21.1 (Latest)
### Highlights
- **⚡️⚡️⚡️ Gemini 3 Flash + Gemini CLI:** If you are a paid user, you can now
enable Gemini 3 Pro and Gemini 3 Flash. Go to `/settings` and set **Preview
Features** to `true` to enable Gemini 3. For more information:
[Gemini 3 Flash is now available in Gemini CLI](https://developers.googleblog.com/gemini-3-flash-is-now-available-in-gemini-cli/).
### What's Changed
- refactor(stdio): always patch stdout and use createWorkingStdio for clean
output by @allenhutchison in
https://github.com/google-gemini/gemini-cli/pull/14159
- chore(release): bump version to 0.21.0-nightly.20251202.2d935b379 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/14409
- implement fuzzy search inside settings by @sehoon38 in
https://github.com/google-gemini/gemini-cli/pull/13864
- feat: enable message bus integration by default by @allenhutchison in
https://github.com/google-gemini/gemini-cli/pull/14329
- docs: Recommend using --debug intead of --verbose for CLI debugging by @bbiggs
in https://github.com/google-gemini/gemini-cli/pull/14334
- feat: consolidate remote MCP servers to use `url` in config by
@jackwotherspoon in https://github.com/google-gemini/gemini-cli/pull/13762
- Restrict integration tests tools by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14403
- track github repository names in telemetry events by @IamRiddhi in
https://github.com/google-gemini/gemini-cli/pull/13670
- Allow telemetry exporters to GCP to utilize user's login credentials, if
requested by @mboshernitsan in
https://github.com/google-gemini/gemini-cli/pull/13778
- refactor(editor): use const assertion for editor types with single source of
truth by @amsminn in https://github.com/google-gemini/gemini-cli/pull/8604
- fix(security): Fix npm audit vulnerabilities in glob and body-parser by
@afarber in https://github.com/google-gemini/gemini-cli/pull/14090
- Add new enterprise instructions by @chrstnb in
https://github.com/google-gemini/gemini-cli/pull/8641
- feat(hooks): Hook Session Lifecycle & Compression Integration by @Edilmo in
https://github.com/google-gemini/gemini-cli/pull/14151
- Avoid triggering refreshStatic unless there really is a banner to display. by
@jacob314 in https://github.com/google-gemini/gemini-cli/pull/14328
- feat(hooks): Hooks Commands Panel, Enable/Disable, and Migrate by @Edilmo in
https://github.com/google-gemini/gemini-cli/pull/14225
- fix: Bundle default policies for npx distribution by @allenhutchison in
https://github.com/google-gemini/gemini-cli/pull/14457
- feat(hooks): Hook System Documentation by @Edilmo in
https://github.com/google-gemini/gemini-cli/pull/14307
- Fix tests by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14458
- feat: add scheduled workflow to close stale issues by @galz10 in
https://github.com/google-gemini/gemini-cli/pull/14404
- feat: Support Extension Hooks with Security Warning by @abhipatel12 in
https://github.com/google-gemini/gemini-cli/pull/14460
- feat: Add enableAgents experimental flag by @adamfweidman in
https://github.com/google-gemini/gemini-cli/pull/14371
- docs: fix typo 'socus' to 'focus' in todos.md by @Viktor286 in
https://github.com/google-gemini/gemini-cli/pull/14374
- Markdown export: move the emoji to the end of the line by @mhansen in
https://github.com/google-gemini/gemini-cli/pull/12278
- fix(acp): prevent unnecessary credential cache clearing on re-authent… by
@h-michael in https://github.com/google-gemini/gemini-cli/pull/9410
- fix(cli): Fix word navigation for CJK characters by @SandyTao520 in
https://github.com/google-gemini/gemini-cli/pull/14475
- Remove example extension by @chrstnb in
https://github.com/google-gemini/gemini-cli/pull/14376
- Add commands for listing and updating per-extension settings by @chrstnb in
https://github.com/google-gemini/gemini-cli/pull/12664
- chore(tests): remove obsolete test for hierarchical memory by @pareshjoshij in
https://github.com/google-gemini/gemini-cli/pull/13122
- feat(cli): support /copy in remote sessions using OSC52 by @ismellpillows in
https://github.com/google-gemini/gemini-cli/pull/13471
- Update setting search UX by @Adib234 in
https://github.com/google-gemini/gemini-cli/pull/14451
- Fix(cli): Improve Homebrew update instruction to specify gemini-cli by
@DaanVersavel in https://github.com/google-gemini/gemini-cli/pull/14502
- do not toggle the setting item when entering space by @sehoon38 in
https://github.com/google-gemini/gemini-cli/pull/14489
- fix: improve retry logic for fetch errors and network codes by @megha1188 in
https://github.com/google-gemini/gemini-cli/pull/14439
- remove unused isSearching field by @sehoon38 in
https://github.com/google-gemini/gemini-cli/pull/14509
- feat(mcp): add `--type` alias for `--transport` flag in gemini mcp add by
@jackwotherspoon in https://github.com/google-gemini/gemini-cli/pull/14503
- feat(cli): Move key restore logic to core by @cocosheng-g in
https://github.com/google-gemini/gemini-cli/pull/13013
- feat: add auto-execute on Enter behavior to argumentless MCP prompts by
@jackwotherspoon in https://github.com/google-gemini/gemini-cli/pull/14510
- fix(shell): cursor visibility when using interactive mode by @aswinashok44 in
https://github.com/google-gemini/gemini-cli/pull/14095
- Adding session id as part of json o/p by @MJjainam in
https://github.com/google-gemini/gemini-cli/pull/14504
- fix(extensions): resolve GitHub API 415 error for source tarballs by
@jpoehnelt in https://github.com/google-gemini/gemini-cli/pull/13319
- fix(client): Correctly latch hasFailedCompressionAttempt flag by @pareshjoshij
in https://github.com/google-gemini/gemini-cli/pull/13002
- Disable flaky extension reloading test on linux by @chrstnb in
https://github.com/google-gemini/gemini-cli/pull/14528
- Add support for MCP dynamic tool update by `notifications/tools/list_changed`
by @Adib234 in https://github.com/google-gemini/gemini-cli/pull/14375
- Fix privacy screen for legacy tier users by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14522
- feat: Exclude maintainer labeled issues from stale issue closer by @galz10 in
https://github.com/google-gemini/gemini-cli/pull/14532
- Grant chained workflows proper permission. by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14534
- Make trigger_e2e manually fireable. by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14547
- Write e2e status to local repo not forked repo by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14549
- Fixes [API Error: Cannot read properties of undefined (reading 'error')] by
@silviojr in https://github.com/google-gemini/gemini-cli/pull/14553
- Trigger chained e2e tests on all pull requests by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14551
- Fix bug in the shellExecutionService resulting in both truncation and 3X bloat
by @jacob314 in https://github.com/google-gemini/gemini-cli/pull/14545
- Fix issue where we were passing the model content reflecting terminal line
wrapping. by @jacob314 in
https://github.com/google-gemini/gemini-cli/pull/14566
- chore/release: bump version to 0.21.0-nightly.20251204.3da4fd5f7 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/14476
- feat(sessions): use 1-line generated session summary to describe sessions by
@jackwotherspoon in https://github.com/google-gemini/gemini-cli/pull/14467
- Use Robot PAT for chained e2e merge queue skipper by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14585
- fix(core): improve API response error handling and retry logic by @mattKorwel
in https://github.com/google-gemini/gemini-cli/pull/14563
- Docs: Model routing clarification by @jkcinouye in
https://github.com/google-gemini/gemini-cli/pull/14373
- expose previewFeatures flag in a2a by @sehoon38 in
https://github.com/google-gemini/gemini-cli/pull/14550
- Fix emoji width in debug console. by @jacob314 in
https://github.com/google-gemini/gemini-cli/pull/14593
- Fully detach autoupgrade process by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14595
- Docs: Update Gemini 3 on Gemini CLI documentation by @jkcinouye in
https://github.com/google-gemini/gemini-cli/pull/14601
- Disallow floating promises. by @gundermanc in
https://github.com/google-gemini/gemini-cli/pull/14605
- chore/release: bump version to 0.21.0-nightly.20251207.025e450ac by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/14662
- feat(modelAvailabilityService): integrate model availability service into
backend logic by @adamfweidman in
https://github.com/google-gemini/gemini-cli/pull/14470
- Add prompt_id propagation in a2a-server task by @koxkox111 in
https://github.com/google-gemini/gemini-cli/pull/14581
- Fix: Prevent freezing in non-interactive Gemini CLI when debug mode is enabled
by @parthasaradhie in https://github.com/google-gemini/gemini-cli/pull/14580
- fix(audio): improve reading of audio files by @jackwotherspoon in
https://github.com/google-gemini/gemini-cli/pull/14658
- Update automated triage workflow to stop assigning priority labels by
@skeshive in https://github.com/google-gemini/gemini-cli/pull/14717
- set failed status when chained e2e fails by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14725
- feat(github action) Triage and Label Pull Requests by Size and Comple… by
@DaanVersavel in https://github.com/google-gemini/gemini-cli/pull/5571
- refactor(telemetry): Improve previous PR that allows telemetry to use the CLI
auth and add testing by @mboshernitsan in
https://github.com/google-gemini/gemini-cli/pull/14589
- Always set status in chained_e2e workflow by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14730
- feat: Add OTEL log event `gemini_cli.startup_stats` for startup stats. by
@kevin-ramdass in https://github.com/google-gemini/gemini-cli/pull/14734
- feat: auto-execute on slash command completion functions by @jackwotherspoon
in https://github.com/google-gemini/gemini-cli/pull/14584
- Docs: Proper release notes by @jkcinouye in
https://github.com/google-gemini/gemini-cli/pull/14405
- Add support for user-scoped extension settings by @chrstnb in
https://github.com/google-gemini/gemini-cli/pull/13748
- refactor(core): Improve environment variable handling in shell execution by
@galz10 in https://github.com/google-gemini/gemini-cli/pull/14742
- Remove old E2E Workflows by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14749
- fix: handle missing local extension config and skip hooks when disabled by
@abhipatel12 in https://github.com/google-gemini/gemini-cli/pull/14744
- chore/release: bump version to 0.21.0-nightly.20251209.ec9a8c7a7 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/14751
- feat: Add support for MCP Resources by @MrLesk in
https://github.com/google-gemini/gemini-cli/pull/13178
- Always set pending status in E2E tests by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14756
- fix(lint): upgrade pip and use public pypi for yamllint by @allenhutchison in
https://github.com/google-gemini/gemini-cli/pull/14746
- fix: use Gemini API supported image formats for clipboard by @jackwotherspoon
in https://github.com/google-gemini/gemini-cli/pull/14762
- feat(a2a): Introduce restore command for a2a server by @cocosheng-g in
https://github.com/google-gemini/gemini-cli/pull/13015
- allow final:true to be returned on a2a server edit calls. by @DavidAPierce in
https://github.com/google-gemini/gemini-cli/pull/14747
- (fix) Automated pr labeller by @DaanVersavel in
https://github.com/google-gemini/gemini-cli/pull/14788
- Update CODEOWNERS by @kklashtorny1 in
https://github.com/google-gemini/gemini-cli/pull/14830
- Docs: Fix errors preventing site rebuild. by @jkcinouye in
https://github.com/google-gemini/gemini-cli/pull/14842
- chore(deps): bump express from 5.1.0 to 5.2.0 by @dependabot[bot] in
https://github.com/google-gemini/gemini-cli/pull/14325
- fix(patch): cherry-pick 3f5f030 to release/v0.21.0-preview.0-pr-14843 to patch
version v0.21.0-preview.0 and create version 0.21.0-preview.1 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/14851
- fix(patch): cherry-pick ee6556c to release/v0.21.0-preview.1-pr-14691 to patch
version v0.21.0-preview.1 and create version 0.21.0-preview.2 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/14908
- fix(patch): cherry-pick 54de675 to release/v0.21.0-preview.2-pr-14961 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/14968
- fix(patch): cherry-pick 12cbe32 to release/v0.21.0-preview.3-pr-15000 to patch
version v0.21.0-preview.3 and create version 0.21.0-preview.4 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/15003
- fix(patch): cherry-pick edbe548 to release/v0.21.0-preview.4-pr-15007 to patch
version v0.21.0-preview.4 and create version 0.21.0-preview.5 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/15015
- fix(patch): cherry-pick 2995af6 to release/v0.21.0-preview.5-pr-15131 to patch
version v0.21.0-preview.5 and create version 0.21.0-preview.6 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/15153
**Full Changelog**:
https://github.com/google-gemini/gemini-cli/compare/v0.20.2...v0.21.0
## Release v0.22.0-preview-0 (Preview)
### What's Changed
- feat(ide): fallback to GEMINI_CLI_IDE_AUTH_TOKEN env var by @skeshive in
https://github.com/google-gemini/gemini-cli/pull/14843
- feat: display quota stats for unused models in /stats by @sehoon38 in
https://github.com/google-gemini/gemini-cli/pull/14764
- feat: ensure codebase investigator uses preview model when main agent does by
@abhipatel12 in https://github.com/google-gemini/gemini-cli/pull/14412
- chore: add closing reason to stale bug workflow by @galz10 in
https://github.com/google-gemini/gemini-cli/pull/14861
- Send the model and CLI version with the user agent by @gundermanc in
https://github.com/google-gemini/gemini-cli/pull/14865
- refactor(sessions): move session summary generation to startup by
@jackwotherspoon in https://github.com/google-gemini/gemini-cli/pull/14691
- Limit search depth in path corrector by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14869
- Fix: Correct typo in code comment by @kuishou68 in
https://github.com/google-gemini/gemini-cli/pull/14671
- feat(core): Plumbing for late resolution of model configs. by @joshualitt in
https://github.com/google-gemini/gemini-cli/pull/14597
- feat: attempt more error parsing by @adamfweidman in
https://github.com/google-gemini/gemini-cli/pull/14899
- Add missing await. by @gundermanc in
https://github.com/google-gemini/gemini-cli/pull/14910
- feat(core): Add support for transcript_path in hooks for git-ai/Gemini
extension by @svarlamov in
https://github.com/google-gemini/gemini-cli/pull/14663
- refactor: implement DelegateToAgentTool with discriminated union by
@abhipatel12 in https://github.com/google-gemini/gemini-cli/pull/14769
- feat: reset availabilityService on /auth by @adamfweidman in
https://github.com/google-gemini/gemini-cli/pull/14911
- chore/release: bump version to 0.21.0-nightly.20251211.8c83e1ea9 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/14924
- Fix: Correctly detect MCP tool errors by @kevin-ramdass in
https://github.com/google-gemini/gemini-cli/pull/14937
- increase labeler timeout by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14922
- tool(cli): tweak the frontend tool to be aware of more core files from the cli
by @jacob314 in https://github.com/google-gemini/gemini-cli/pull/14962
- feat(cli): polish cached token stats and simplify stats display when quota is
present. by @jacob314 in
https://github.com/google-gemini/gemini-cli/pull/14961
- feat(settings-validation): add validation for settings schema by @lifefloating
in https://github.com/google-gemini/gemini-cli/pull/12929
- fix(ide): Update IDE extension to write auth token in env var by @skeshive in
https://github.com/google-gemini/gemini-cli/pull/14999
- Revert "chore(deps): bump express from 5.1.0 to 5.2.0" by @skeshive in
https://github.com/google-gemini/gemini-cli/pull/14998
- feat(a2a): Introduce /init command for a2a server by @cocosheng-g in
https://github.com/google-gemini/gemini-cli/pull/13419
- feat: support multi-file drag and drop of images by @jackwotherspoon in
https://github.com/google-gemini/gemini-cli/pull/14832
- fix(policy): allow codebase_investigator by default in read-only policy by
@abhipatel12 in https://github.com/google-gemini/gemini-cli/pull/15000
- refactor(ide ext): Update port file name + switch to 1-based index for
characters + remove truncation text by @skeshive in
https://github.com/google-gemini/gemini-cli/pull/10501
- fix(vscode-ide-companion): correct license generation for workspace
dependencies by @skeshive in
https://github.com/google-gemini/gemini-cli/pull/15004
- fix: temp fix for subagent invocation until subagent delegation is merged to
stable by @abhipatel12 in
https://github.com/google-gemini/gemini-cli/pull/15007
- test: update ide detection tests to make them more robust when run in an ide
by @kevin-ramdass in https://github.com/google-gemini/gemini-cli/pull/15008
- Remove flex from stats display. See snapshots for diffs. by @jacob314 in
https://github.com/google-gemini/gemini-cli/pull/14983
- Add license field into package.json by @jb-perez in
https://github.com/google-gemini/gemini-cli/pull/14473
- feat: Persistent "Always Allow" policies with granular shell & MCP support by
@allenhutchison in https://github.com/google-gemini/gemini-cli/pull/14737
- chore/release: bump version to 0.21.0-nightly.20251212.54de67536 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/14969
- fix(core): commandPrefix word boundary and compound command safety by
@allenhutchison in https://github.com/google-gemini/gemini-cli/pull/15006
- chore(docs): add 'Maintainers only' label info to CONTRIBUTING.md by @jacob314
in https://github.com/google-gemini/gemini-cli/pull/14914
- Refresh hooks when refreshing extensions. by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14918
- Add clarity to error messages by @gsehgal in
https://github.com/google-gemini/gemini-cli/pull/14879
- chore : remove a redundant tip by @JayadityaGit in
https://github.com/google-gemini/gemini-cli/pull/14947
- chore/release: bump version to 0.21.0-nightly.20251213.977248e09 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/15029
- Disallow redundant typecasts. by @gundermanc in
https://github.com/google-gemini/gemini-cli/pull/15030
- fix(auth): prioritize GEMINI_API_KEY env var and skip unnecessary key… by
@galz10 in https://github.com/google-gemini/gemini-cli/pull/14745
- fix: use zod for safety check result validation by @allenhutchison in
https://github.com/google-gemini/gemini-cli/pull/15026
- update(telemetry): add hashed_extension_name to field to extension events by
@kiranani in https://github.com/google-gemini/gemini-cli/pull/15025
- fix: similar to policy-engine, throw error in case of requiring tool execution
confirmation for non-interactive mode by @MayV in
https://github.com/google-gemini/gemini-cli/pull/14702
- Clean up processes in integration tests by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/15102
- docs: update policy engine getting started and defaults by @NTaylorMullen in
https://github.com/google-gemini/gemini-cli/pull/15105
- Fix tool output fragmentation by encapsulating content in functionResponse by
@abhipatel12 in https://github.com/google-gemini/gemini-cli/pull/13082
- Simplify method signature. by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/15114
- Show raw input token counts in json output. by @jacob314 in
https://github.com/google-gemini/gemini-cli/pull/15021
- fix: Mark A2A requests as interactive by @MayV in
https://github.com/google-gemini/gemini-cli/pull/15108
- use previewFeatures to determine which pro model to use for A2A by @sehoon38
in https://github.com/google-gemini/gemini-cli/pull/15131
- refactor(cli): fix settings merging so that settings using the new json format
take priority over ones using the old format by @jacob314 in
https://github.com/google-gemini/gemini-cli/pull/15116
**Full Changelog**:
https://github.com/google-gemini/gemini-cli/compare/v0.21.0-preview.6...v0.22.0-preview.0
## Release v0.20.0 - v0.20.2
### What's Changed
- Update error codes when process exiting the gemini cli by @megha1188 in
https://github.com/google-gemini/gemini-cli/pull/13728
- chore(release): bump version to 0.20.0-nightly.20251126.d2a6cff4d by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/13835
- feat(core): Improve request token calculation accuracy by @SandyTao520 in
https://github.com/google-gemini/gemini-cli/pull/13824
- Changes in system instruction to adapt to gemini 3.0 to ensure that the CLI
explains its actions before calling tools by @silviojr in
https://github.com/google-gemini/gemini-cli/pull/13810
- feat(hooks): Hook Tool Execution Integration by @Edilmo in
https://github.com/google-gemini/gemini-cli/pull/9108
- Add support for MCP server instructions behind config option by @chrstnb in
https://github.com/google-gemini/gemini-cli/pull/13432
- Update System Instructions for interactive vs non-interactive mode. by
@aishaneeshah in https://github.com/google-gemini/gemini-cli/pull/12315
- Add consent flag to Link command by @kevinjwang1 in
https://github.com/google-gemini/gemini-cli/pull/13832
- feat(mcp): Inject GoogleCredentialProvider headers in McpClient by
@sai-sunder-s in https://github.com/google-gemini/gemini-cli/pull/13783
- feat(core): implement towards policy-driven model fallback mechanism by
@adamfweidman in https://github.com/google-gemini/gemini-cli/pull/13781
- feat(core): Add configurable inactivity timeout for shell commands by @galz10
in https://github.com/google-gemini/gemini-cli/pull/13531
- fix(auth): improve API key authentication flow by @galz10 in
https://github.com/google-gemini/gemini-cli/pull/13829
- feat(hooks): Hook LLM Request/Response Integration by @Edilmo in
https://github.com/google-gemini/gemini-cli/pull/9110
- feat(ui): Show waiting MCP servers in ConfigInitDisplay by @werdnum in
https://github.com/google-gemini/gemini-cli/pull/13721
- Add usage limit remaining in /stats by @sehoon38 in
https://github.com/google-gemini/gemini-cli/pull/13843
- feat(shell): Standardize pager to 'cat' for shell execution by model by
@galz10 in https://github.com/google-gemini/gemini-cli/pull/13878
- chore/release: bump version to 0.20.0-nightly.20251127.5bed97064 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/13877
- Revert to default LICENSE (Revert #13449) by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/13876
- update(telemetry): OTel API response event with finish reasons by @kiranani in
https://github.com/google-gemini/gemini-cli/pull/13849
- feat(hooks): Hooks Comprehensive Integration Testing by @Edilmo in
https://github.com/google-gemini/gemini-cli/pull/9112
- chore: fix session browser test and skip hook system tests by @jackwotherspoon
in https://github.com/google-gemini/gemini-cli/pull/14099
- feat(telemetry): Add Semantic logging for to ApiRequestEvents by @kiranani in
https://github.com/google-gemini/gemini-cli/pull/13912
- test: Add verification for $schema property in settings schema by
@maryamariyan in https://github.com/google-gemini/gemini-cli/pull/13497
- Fixes `/clear` command to preserve input history for up-arrow navigation while
still clearing the context window and screen by @korade-krushna in
https://github.com/google-gemini/gemini-cli/pull/14182
- fix(core): handle EPIPE error in hook runner when writing to stdin by
@SandyTao520 in https://github.com/google-gemini/gemini-cli/pull/14231
- fix: Exclude web-fetch tool from executing in default non-interactive mode to
avoid CLI hang. by @MayV in
https://github.com/google-gemini/gemini-cli/pull/14244
- Always use MCP server instructions by @chrstnb in
https://github.com/google-gemini/gemini-cli/pull/14297
- feat: auto-execute simple slash commands on Enter by @jackwotherspoon in
https://github.com/google-gemini/gemini-cli/pull/13985
- chore/release: bump version to 0.20.0-nightly.20251201.2fe609cb6 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/14304
- feat: Add startup profiler to measure and record application initialization
phases. by @kevin-ramdass in
https://github.com/google-gemini/gemini-cli/pull/13638
- bug(core): Avoid stateful tool use in `executor`. by @joshualitt in
https://github.com/google-gemini/gemini-cli/pull/14305
- feat(themes): add built-in holiday theme 🎁 by @jackwotherspoon in
https://github.com/google-gemini/gemini-cli/pull/14301
- Updated ToC on docs intro; updated title casing to match Google style by
@pcoet in https://github.com/google-gemini/gemini-cli/pull/13717
- feat(a2a): Urgent fix - Process modelInfo agent message by @cocosheng-g in
https://github.com/google-gemini/gemini-cli/pull/14315
- feat(core): enhance availability routing with wrapped fallback and
single-model policies by @adamfweidman in
https://github.com/google-gemini/gemini-cli/pull/13874
- chore(logging): log the problematic event for #12122 by @briandealwis in
https://github.com/google-gemini/gemini-cli/pull/14092
- fix: remove invalid type key in bug_report.yml by @fancive in
https://github.com/google-gemini/gemini-cli/pull/13576
- update screenshot by @Transient-Onlooker in
https://github.com/google-gemini/gemini-cli/pull/13976
- docs: Fix grammar error in Release Cadence (Nightly section) by @JuanCS-Dev in
https://github.com/google-gemini/gemini-cli/pull/13866
- fix(async): prevent missed async errors from bypassing catch handlers by
@amsminn in https://github.com/google-gemini/gemini-cli/pull/13714
- fix(zed-integration): remove extra field from acp auth request by
@marcocondrache in https://github.com/google-gemini/gemini-cli/pull/13646
- feat(cli): Documentation for model configs. by @joshualitt in
https://github.com/google-gemini/gemini-cli/pull/12967
- fix(ui): misaligned markdown table rendering by @dumbbellcode in
https://github.com/google-gemini/gemini-cli/pull/8336
- docs: Update 4 files by @g-samroberts in
https://github.com/google-gemini/gemini-cli/pull/13628
- fix: Conditionally add set -eEuo pipefail in setup-github command by @Smetalo
in https://github.com/google-gemini/gemini-cli/pull/8550
- fix(cli): fix issue updating a component while rendering a different component
by @jacob314 in https://github.com/google-gemini/gemini-cli/pull/14319
- Increase flakey test timeout by @chrstnb in
https://github.com/google-gemini/gemini-cli/pull/14377
- Remove references to deleted kind/bug label by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14383
- Don't fail test if we can't cleanup by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/14389
- feat(core): Implement JIT context manager and setting by @SandyTao520 in
https://github.com/google-gemini/gemini-cli/pull/14324
- Use polling for extensions-reload integration test by @chrstnb in
https://github.com/google-gemini/gemini-cli/pull/14391
- Add docs directive to GEMINI.md by @g-samroberts in
https://github.com/google-gemini/gemini-cli/pull/14327
- Hide sessions that don't have user messages by @bl-ue in
https://github.com/google-gemini/gemini-cli/pull/13994
- chore(ci): mark GitHub release as pre-release if not on "latest" npm channel
by @ljxfstorm in https://github.com/google-gemini/gemini-cli/pull/7386
- fix(patch): cherry-pick d284fa6 to release/v0.20.0-preview.0-pr-14545
[CONFLICTS] by @gemini-cli-robot in
https://github.com/google-gemini/gemini-cli/pull/14559
- fix(patch): cherry-pick 828afe1 to release/v0.20.0-preview.1-pr-14159 to patch
version v0.20.0-preview.1 and create version 0.20.0-preview.2 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/14733
- fix(patch): cherry-pick 171103a to release/v0.20.0-preview.2-pr-14742 to patch
version v0.20.0-preview.2 and create version 0.20.0-preview.5 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/14752
**Full Changelog**:
https://github.com/google-gemini/gemini-cli/compare/v0.19.4...v0.20.0
## Release v0.19.0 - v0.19.4
## Highlights
- **Zed integration:** Users can now leverage Gemini 3 within the Zed
integration after enabling "Preview Features" in their CLIs `/settings`.
- **Interactive shell:**
- **Click-to-Focus:** Go to `/settings` and enable **Use Alternate Buffer**
When "Use Alternate Buffer" setting is enabled users can click within the
embedded shell output to focus it for input.
- **Loading phrase:** Clearly indicates when the interactive shell is awaiting
user input. ([vid](https://imgur.com/a/kjK8bUK)
[pr](https://github.com/google-gemini/gemini-cli/pull/12535) by
[@jackwotherspoon](https://github.com/jackwotherspoon))
### What's Changed
- Use lenient MCP output schema validator by @cornmander in
https://github.com/google-gemini/gemini-cli/pull/13521
- Update persistence state to track counts of messages instead of times banner
has been displayed by @Adib234 in
https://github.com/google-gemini/gemini-cli/pull/13428
- update docs for http proxy by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/13538
- move stdio by @jacob314 in
https://github.com/google-gemini/gemini-cli/pull/13528
- chore(release): bump version to 0.19.0-nightly.20251120.8e531dc02 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/13540
- Skip pre-commit hooks for shadow repo (#13331) by @vishvananda in
https://github.com/google-gemini/gemini-cli/pull/13488
- fix(ui): Correct mouse click cursor positioning for wide characters by
@SandyTao520 in https://github.com/google-gemini/gemini-cli/pull/13537
- fix(core): correct bash @P prompt transformation detection by @pyrytakala in
https://github.com/google-gemini/gemini-cli/pull/13544
- Optimize and improve test coverage for cli/src/config by @megha1188 in
https://github.com/google-gemini/gemini-cli/pull/13485
- Improve code coverage for cli/src/ui/privacy package by @megha1188 in
https://github.com/google-gemini/gemini-cli/pull/13493
- docs: fix typos in source code and documentation by @fancive in
https://github.com/google-gemini/gemini-cli/pull/13577
- Improved code coverage for cli/src/zed-integration by @megha1188 in
https://github.com/google-gemini/gemini-cli/pull/13570
- feat(ui): build interactive session browser component by @bl-ue in
https://github.com/google-gemini/gemini-cli/pull/13351
- Fix multiple bugs with auth flow including using the implemented but unused
restart support. by @jacob314 in
https://github.com/google-gemini/gemini-cli/pull/13565
- feat(core): add modelAvailabilityService for managing and tracking model
health by @adamfweidman in
https://github.com/google-gemini/gemini-cli/pull/13426
- docs: fix grammar typo "a MCP" to "an MCP" by @noahacgn in
https://github.com/google-gemini/gemini-cli/pull/13595
- feat: custom loading phrase when interactive shell requires input by
@jackwotherspoon in https://github.com/google-gemini/gemini-cli/pull/12535
- docs: Update uninstall command to reflect multiple extension support by
@JayadityaGit in https://github.com/google-gemini/gemini-cli/pull/13582
- bug(core): Ensure we use thinking budget on fallback to 2.5 by @joshualitt in
https://github.com/google-gemini/gemini-cli/pull/13596
- Remove useModelRouter experimental flag by @Adib234 in
https://github.com/google-gemini/gemini-cli/pull/13593
- feat(docs): Ensure multiline JS objects are rendered properly. by @joshualitt
in https://github.com/google-gemini/gemini-cli/pull/13535
- Fix exp id logging by @owenofbrien in
https://github.com/google-gemini/gemini-cli/pull/13430
- Moved client id logging into createBasicLogEvent by @owenofbrien in
https://github.com/google-gemini/gemini-cli/pull/13607
- Restore bracketed paste mode after external editor exit by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/13606
- feat(core): Add support for custom aliases for model configs. by @joshualitt
in https://github.com/google-gemini/gemini-cli/pull/13546
- feat(core): Add `BaseLlmClient.generateContent`. by @joshualitt in
https://github.com/google-gemini/gemini-cli/pull/13591
- Turn off alternate buffer mode by default. by @jacob314 in
https://github.com/google-gemini/gemini-cli/pull/13623
- fix(cli): Prevent stdout/stderr patching for extension commands by @chrstnb in
https://github.com/google-gemini/gemini-cli/pull/13600
- Improve test coverage for cli/src/ui/components by @megha1188 in
https://github.com/google-gemini/gemini-cli/pull/13598
- Update ink version to 6.4.6 by @jacob314 in
https://github.com/google-gemini/gemini-cli/pull/13631
- chore/release: bump version to 0.19.0-nightly.20251122.42c2e1b21 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/13637
- chore/release: bump version to 0.19.0-nightly.20251123.dadd606c0 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/13675
- chore/release: bump version to 0.19.0-nightly.20251124.e177314a4 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/13713
- fix(core): Fix context window overflow warning for PDF files by @kkitase in
https://github.com/google-gemini/gemini-cli/pull/13548
- feat :rephrasing the extension logging messages to run the explore command
when there are no extensions installed by @JayadityaGit in
https://github.com/google-gemini/gemini-cli/pull/13740
- Improve code coverage for cli package by @megha1188 in
https://github.com/google-gemini/gemini-cli/pull/13724
- Add session subtask in /stats command by @Adib234 in
https://github.com/google-gemini/gemini-cli/pull/13750
- feat(core): Migrate chatCompressionService to model configs. by @joshualitt in
https://github.com/google-gemini/gemini-cli/pull/12863
- feat(hooks): Hook Telemetry Infrastructure by @Edilmo in
https://github.com/google-gemini/gemini-cli/pull/9082
- fix: (some minor improvements to configs and getPackageJson return behaviour)
by @grMLEqomlkkU5Eeinz4brIrOVCUCkJuN in
https://github.com/google-gemini/gemini-cli/pull/12510
- feat(hooks): Hook Event Handling by @Edilmo in
https://github.com/google-gemini/gemini-cli/pull/9097
- feat(hooks): Hook Agent Lifecycle Integration by @Edilmo in
https://github.com/google-gemini/gemini-cli/pull/9105
- feat(core): Land bool for alternate system prompt. by @joshualitt in
https://github.com/google-gemini/gemini-cli/pull/13764
- bug(core): Add default chat compression config. by @joshualitt in
https://github.com/google-gemini/gemini-cli/pull/13766
- feat(model-availability): introduce ModelPolicy and PolicyCatalog by
@adamfweidman in https://github.com/google-gemini/gemini-cli/pull/13751
- feat(hooks): Hook System Orchestration by @Edilmo in
https://github.com/google-gemini/gemini-cli/pull/9102
- feat(config): add isModelAvailabilityServiceEnabled setting by @adamfweidman
in https://github.com/google-gemini/gemini-cli/pull/13777
- chore/release: bump version to 0.19.0-nightly.20251125.f6d97d448 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/13782
- chore: remove console.error by @adamfweidman in
https://github.com/google-gemini/gemini-cli/pull/13779
- fix: Add $schema property to settings.schema.json by @sacrosanctic in
https://github.com/google-gemini/gemini-cli/pull/12763
- fix(cli): allow non-GitHub SCP-styled URLs for extension installation by @m0ps
in https://github.com/google-gemini/gemini-cli/pull/13800
- fix(resume): allow passing a prompt via stdin while resuming using --resume by
@bl-ue in https://github.com/google-gemini/gemini-cli/pull/13520
- feat(sessions): add /resume slash command to open the session browser by
@bl-ue in https://github.com/google-gemini/gemini-cli/pull/13621
- docs(sessions): add documentation for chat recording and session management by
@bl-ue in https://github.com/google-gemini/gemini-cli/pull/13667
- Fix TypeError: "URL.parse is not a function" for Node.js < v22 by @macarronesc
in https://github.com/google-gemini/gemini-cli/pull/13698
- fallback to flash for TerminalQuota errors by @sehoon38 in
https://github.com/google-gemini/gemini-cli/pull/13791
- Update Code Wiki README badge by @PatoBeltran in
https://github.com/google-gemini/gemini-cli/pull/13768
- Add Databricks auth support and custom header option to gemini cli by
@AarushiShah in https://github.com/google-gemini/gemini-cli/pull/11893
- Update dependency for modelcontextprotocol/sdk to 1.23.0 by @bbiggs in
https://github.com/google-gemini/gemini-cli/pull/13827
- fix(patch): cherry-pick 576fda1 to release/v0.19.0-preview.0-pr-14099
[CONFLICTS] by @gemini-cli-robot in
https://github.com/google-gemini/gemini-cli/pull/14402
**Full Changelog**:
https://github.com/google-gemini/gemini-cli/compare/v0.18.4...v0.19.0
## Release v0.19.0-preview.0
### What's Changed
- Use lenient MCP output schema validator by @cornmander in
https://github.com/google-gemini/gemini-cli/pull/13521
- Update persistence state to track counts of messages instead of times banner
has been displayed by @Adib234 in
https://github.com/google-gemini/gemini-cli/pull/13428
- update docs for http proxy by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/13538
- move stdio by @jacob314 in
https://github.com/google-gemini/gemini-cli/pull/13528
- chore(release): bump version to 0.19.0-nightly.20251120.8e531dc02 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/13540
- Skip pre-commit hooks for shadow repo (#13331) by @vishvananda in
https://github.com/google-gemini/gemini-cli/pull/13488
- fix(ui): Correct mouse click cursor positioning for wide characters by
@SandyTao520 in https://github.com/google-gemini/gemini-cli/pull/13537
- fix(core): correct bash @P prompt transformation detection by @pyrytakala in
https://github.com/google-gemini/gemini-cli/pull/13544
- Optimize and improve test coverage for cli/src/config by @megha1188 in
https://github.com/google-gemini/gemini-cli/pull/13485
- Improve code coverage for cli/src/ui/privacy package by @megha1188 in
https://github.com/google-gemini/gemini-cli/pull/13493
- docs: fix typos in source code and documentation by @fancive in
https://github.com/google-gemini/gemini-cli/pull/13577
- Improved code coverage for cli/src/zed-integration by @megha1188 in
https://github.com/google-gemini/gemini-cli/pull/13570
- feat(ui): build interactive session browser component by @bl-ue in
https://github.com/google-gemini/gemini-cli/pull/13351
- Fix multiple bugs with auth flow including using the implemented but unused
restart support. by @jacob314 in
https://github.com/google-gemini/gemini-cli/pull/13565
- feat(core): add modelAvailabilityService for managing and tracking model
health by @adamfweidman in
https://github.com/google-gemini/gemini-cli/pull/13426
- docs: fix grammar typo "a MCP" to "an MCP" by @noahacgn in
https://github.com/google-gemini/gemini-cli/pull/13595
- feat: custom loading phrase when interactive shell requires input by
@jackwotherspoon in https://github.com/google-gemini/gemini-cli/pull/12535
- docs: Update uninstall command to reflect multiple extension support by
@JayadityaGit in https://github.com/google-gemini/gemini-cli/pull/13582
- bug(core): Ensure we use thinking budget on fallback to 2.5 by @joshualitt in
https://github.com/google-gemini/gemini-cli/pull/13596
- Remove useModelRouter experimental flag by @Adib234 in
https://github.com/google-gemini/gemini-cli/pull/13593
- feat(docs): Ensure multiline JS objects are rendered properly. by @joshualitt
in https://github.com/google-gemini/gemini-cli/pull/13535
- Fix exp id logging by @owenofbrien in
https://github.com/google-gemini/gemini-cli/pull/13430
- Moved client id logging into createBasicLogEvent by @owenofbrien in
https://github.com/google-gemini/gemini-cli/pull/13607
- Restore bracketed paste mode after external editor exit by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/13606
- feat(core): Add support for custom aliases for model configs. by @joshualitt
in https://github.com/google-gemini/gemini-cli/pull/13546
- feat(core): Add `BaseLlmClient.generateContent`. by @joshualitt in
https://github.com/google-gemini/gemini-cli/pull/13591
- Turn off alternate buffer mode by default. by @jacob314 in
https://github.com/google-gemini/gemini-cli/pull/13623
- fix(cli): Prevent stdout/stderr patching for extension commands by @chrstnb in
https://github.com/google-gemini/gemini-cli/pull/13600
- Improve test coverage for cli/src/ui/components by @megha1188 in
https://github.com/google-gemini/gemini-cli/pull/13598
- Update ink version to 6.4.6 by @jacob314 in
https://github.com/google-gemini/gemini-cli/pull/13631
- chore/release: bump version to 0.19.0-nightly.20251122.42c2e1b21 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/13637
- chore/release: bump version to 0.19.0-nightly.20251123.dadd606c0 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/13675
- chore/release: bump version to 0.19.0-nightly.20251124.e177314a4 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/13713
- fix(core): Fix context window overflow warning for PDF files by @kkitase in
https://github.com/google-gemini/gemini-cli/pull/13548
- feat :rephrasing the extension logging messages to run the explore command
when there are no extensions installed by @JayadityaGit in
https://github.com/google-gemini/gemini-cli/pull/13740
- Improve code coverage for cli package by @megha1188 in
https://github.com/google-gemini/gemini-cli/pull/13724
- Add session subtask in /stats command by @Adib234 in
https://github.com/google-gemini/gemini-cli/pull/13750
- feat(core): Migrate chatCompressionService to model configs. by @joshualitt in
https://github.com/google-gemini/gemini-cli/pull/12863
- feat(hooks): Hook Telemetry Infrastructure by @Edilmo in
https://github.com/google-gemini/gemini-cli/pull/9082
- fix: (some minor improvements to configs and getPackageJson return behaviour)
by @grMLEqomlkkU5Eeinz4brIrOVCUCkJuN in
https://github.com/google-gemini/gemini-cli/pull/12510
- feat(hooks): Hook Event Handling by @Edilmo in
https://github.com/google-gemini/gemini-cli/pull/9097
- feat(hooks): Hook Agent Lifecycle Integration by @Edilmo in
https://github.com/google-gemini/gemini-cli/pull/9105
- feat(core): Land bool for alternate system prompt. by @joshualitt in
https://github.com/google-gemini/gemini-cli/pull/13764
- bug(core): Add default chat compression config. by @joshualitt in
https://github.com/google-gemini/gemini-cli/pull/13766
- feat(model-availability): introduce ModelPolicy and PolicyCatalog by
@adamfweidman in https://github.com/google-gemini/gemini-cli/pull/13751
- feat(hooks): Hook System Orchestration by @Edilmo in
https://github.com/google-gemini/gemini-cli/pull/9102
- feat(config): add isModelAvailabilityServiceEnabled setting by @adamfweidman
in https://github.com/google-gemini/gemini-cli/pull/13777
- chore/release: bump version to 0.19.0-nightly.20251125.f6d97d448 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/13782
- chore: remove console.error by @adamfweidman in
https://github.com/google-gemini/gemini-cli/pull/13779
- fix: Add $schema property to settings.schema.json by @sacrosanctic in
https://github.com/google-gemini/gemini-cli/pull/12763
- fix(cli): allow non-GitHub SCP-styled URLs for extension installation by @m0ps
in https://github.com/google-gemini/gemini-cli/pull/13800
- fix(resume): allow passing a prompt via stdin while resuming using --resume by
@bl-ue in https://github.com/google-gemini/gemini-cli/pull/13520
- feat(sessions): add /resume slash command to open the session browser by
@bl-ue in https://github.com/google-gemini/gemini-cli/pull/13621
- docs(sessions): add documentation for chat recording and session management by
@bl-ue in https://github.com/google-gemini/gemini-cli/pull/13667
- Fix TypeError: "URL.parse is not a function" for Node.js < v22 by @macarronesc
in https://github.com/google-gemini/gemini-cli/pull/13698
- fallback to flash for TerminalQuota errors by @sehoon38 in
https://github.com/google-gemini/gemini-cli/pull/13791
- Update Code Wiki README badge by @PatoBeltran in
https://github.com/google-gemini/gemini-cli/pull/13768
- Add Databricks auth support and custom header option to gemini cli by
@AarushiShah in https://github.com/google-gemini/gemini-cli/pull/11893
- Update dependency for modelcontextprotocol/sdk to 1.23.0 by @bbiggs in
https://github.com/google-gemini/gemini-cli/pull/13827
**Full Changelog**:
https://github.com/google-gemini/gemini-cli/compare/v0.18.0-preview.4...v0.19.0-preview.0
## Release v0.18.0 - v0.18.4
### Highlights
- **Experimental permission improvements**: We're experimenting with a new
policy engine in Gemini CLI, letting users and administrators create
fine-grained policies for tool calls. This setting is currently behind a flag.
See our [policy engine documentation](../core/policy-engine.md) to learn how
to use this feature.
- **Gemini 3 support rolled out for some users**: Some users can now enable
Gemini 3 by using the `/settings` flag and toggling **Preview Features**. See
our [Gemini 3 on Gemini CLI documentation](../get-started/gemini-3.md) to find
out more about using Gemini 3.
- **Updated UI rollback:** We've temporarily rolled back a previous UI update,
which enabled embedded scrolling and mouse support. This can be re-enabled by
using the `/settings` command and setting **Use Alternate Screen Buffer** to
`true`.
- **Display your model in your chat history**: You can now go use `/settings`
and turn on **Show Model in Chat** to display the model in your chat history.
- **Uninstall multiple extensions**: You can uninstall multiple extensions with
a single command: `gemini extensions uninstall`.
![Uninstalling Gemini extensions with a single command](https://i.imgur.com/pi7nEBI.png)
### What's changed
- Remove obsolete reference to "help wanted" label in CONTRIBUTING.md by
@aswinashok44 in https://github.com/google-gemini/gemini-cli/pull/13291
- chore(release): v0.18.0-nightly.20251118.86828bb56 by @skeshive in
https://github.com/google-gemini/gemini-cli/pull/13309
- Docs: Access clarification. by @jkcinouye in
https://github.com/google-gemini/gemini-cli/pull/13304
- Fix links in Gemini 3 Pro documentation by @gmackall in
https://github.com/google-gemini/gemini-cli/pull/13312
- Improve keyboard code parsing by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/13307
- fix(core): Ensure `read_many_files` tool is available to zed. by @joshualitt
in https://github.com/google-gemini/gemini-cli/pull/13338
- Support 3-parameter modifyOtherKeys sequences by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/13342
- Improve pty resize error handling for Windows by @galz10 in
https://github.com/google-gemini/gemini-cli/pull/13353
- fix(ui): Clear input prompt on Escape key press by @SandyTao520 in
https://github.com/google-gemini/gemini-cli/pull/13335
- bug(ui) showLineNumbers had the wrong default value. by @jacob314 in
https://github.com/google-gemini/gemini-cli/pull/13356
- fix(cli): fix crash on startup in NO_COLOR mode (#13343) due to ungua… by
@avilladsen in https://github.com/google-gemini/gemini-cli/pull/13352
- fix: allow MCP prompts with spaces in name by @jackwotherspoon in
https://github.com/google-gemini/gemini-cli/pull/12910
- Refactor createTransport to duplicate less code by @davidmcwherter in
https://github.com/google-gemini/gemini-cli/pull/13010
- Followup from #10719 by @bl-ue in
https://github.com/google-gemini/gemini-cli/pull/13243
- Capturing github action workflow name if present and send it to clearcut by
@MJjainam in https://github.com/google-gemini/gemini-cli/pull/13132
- feat(sessions): record interactive-only errors and warnings to chat recording
JSON files by @bl-ue in https://github.com/google-gemini/gemini-cli/pull/13300
- fix(zed-integration): Correctly handle cancellation errors by @benbrandt in
https://github.com/google-gemini/gemini-cli/pull/13399
- docs: Add Code Wiki link to README by @holtskinner in
https://github.com/google-gemini/gemini-cli/pull/13289
- Restore keyboard mode when exiting the editor by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/13350
- feat(core, cli): Bump genai version to 1.30.0 by @joshualitt in
https://github.com/google-gemini/gemini-cli/pull/13435
- [cli-ui] Keep header ASCII art colored on non-gradient terminals (#13373) by
@bniladridas in https://github.com/google-gemini/gemini-cli/pull/13374
- Fix Copyright line in LICENSE by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/13449
- Fix typo in write_todos methodology instructions by @Smetalo in
https://github.com/google-gemini/gemini-cli/pull/13411
- feat: update thinking mode support to exclude gemini-2.0 models and simplify
logic. by @kevin-ramdass in
https://github.com/google-gemini/gemini-cli/pull/13454
- remove unneeded log by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/13456
- feat: add click-to-focus support for interactive shell by @galz10 in
https://github.com/google-gemini/gemini-cli/pull/13341
- Add User email detail to about box by @ptone in
https://github.com/google-gemini/gemini-cli/pull/13459
- feat(core): Wire up chat code path for model configs. by @joshualitt in
https://github.com/google-gemini/gemini-cli/pull/12850
- chore/release: bump version to 0.18.0-nightly.20251120.2231497b1 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/13476
- feat(core): Fix bug with incorrect model overriding. by @joshualitt in
https://github.com/google-gemini/gemini-cli/pull/13477
- Use synchronous writes when detecting keyboard modes by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/13478
- fix(cli): prevent race condition when restoring prompt after context overflow
by @SandyTao520 in https://github.com/google-gemini/gemini-cli/pull/13473
- Revert "feat(core): Fix bug with incorrect model overriding." by @adamfweidman
in https://github.com/google-gemini/gemini-cli/pull/13483
- Fix: Update system instruction when GEMINI.md memory is loaded or refreshed by
@lifefloating in https://github.com/google-gemini/gemini-cli/pull/12136
- fix(zed-integration): Ensure that the zed integration is classified as
interactive by @benbrandt in
https://github.com/google-gemini/gemini-cli/pull/13394
- Copy commands as part of setup-github by @gsehgal in
https://github.com/google-gemini/gemini-cli/pull/13464
- Update banner design by @Adib234 in
https://github.com/google-gemini/gemini-cli/pull/13420
- Protect stdout and stderr so JavaScript code can't accidentally write to
stdout corrupting ink rendering by @jacob314 in
https://github.com/google-gemini/gemini-cli/pull/13247
- Enable switching preview features on/off without restart by @Adib234 in
https://github.com/google-gemini/gemini-cli/pull/13515
- feat(core): Use thinking level for Gemini 3 by @joshualitt in
https://github.com/google-gemini/gemini-cli/pull/13445
- Change default compress threshold to 0.5 for api key users by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/13517
- remove duplicated mouse code by @scidomino in
https://github.com/google-gemini/gemini-cli/pull/13525
- feat(zed-integration): Use default model routing for Zed integration by
@benbrandt in https://github.com/google-gemini/gemini-cli/pull/13398
- feat(core): Incorporate Gemini 3 into model config hierarchy. by @joshualitt
in https://github.com/google-gemini/gemini-cli/pull/13447
- 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 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/13626
- 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 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/13813
- 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 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/13821
- 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 by
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/13826
**Full Changelog**:
https://github.com/google-gemini/gemini-cli/compare/v0.17.1...v0.18.0
+77 -1
View File
@@ -159,6 +159,50 @@ This results in the following merged configuration:
By using the system settings file, you can enforce the security and
configuration patterns described below.
### Enforcing system settings with a wrapper script
While the `GEMINI_CLI_SYSTEM_SETTINGS_PATH` environment variable provides
flexibility, a user could potentially override it to point to a different
settings file, bypassing the centrally managed configuration. To mitigate this,
enterprises can deploy a wrapper script or alias that ensures the environment
variable is always set to the corporate-controlled path.
This approach ensures that no matter how the user calls the `gemini` command,
the enterprise settings are always loaded with the highest precedence.
**Example wrapper script:**
Administrators can create a script named `gemini` and place it in a directory
that appears earlier in the user's `PATH` than the actual Gemini CLI binary
(e.g., `/usr/local/bin/gemini`).
```bash
#!/bin/bash
# Enforce the path to the corporate system settings file.
# This ensures that the company's configuration is always applied.
export GEMINI_CLI_SYSTEM_SETTINGS_PATH="/etc/gemini-cli/settings.json"
# Find the original gemini executable.
# This is a simple example; a more robust solution might be needed
# depending on the installation method.
REAL_GEMINI_PATH=$(type -aP gemini | grep -v "^$(type -P gemini)$" | head -n 1)
if [ -z "$REAL_GEMINI_PATH" ]; then
echo "Error: The original 'gemini' executable was not found." >&2
exit 1
fi
# Pass all arguments to the real Gemini CLI executable.
exec "$REAL_GEMINI_PATH" "$@"
```
By deploying this script, the `GEMINI_CLI_SYSTEM_SETTINGS_PATH` is set within
the script's environment, and the `exec` command replaces the script process
with the actual Gemini CLI process, which inherits the environment variable.
This makes it significantly more difficult for a user to bypass the enforced
settings.
## Restricting tool access
You can significantly enhance security by controlling which tools the Gemini
@@ -434,7 +478,39 @@ prompted to switch to the enforced method. In non-interactive mode, the CLI will
exit with an error if the configured authentication method does not match the
enforced one.
## Putting it all together: Example system `settings.json`
### Restricting logins to corporate domains
For enterprises using Google Workspace, you can enforce that users only
authenticate with their corporate Google accounts. This is a network-level
control that is configured on a proxy server, not within Gemini CLI itself. It
works by intercepting authentication requests to Google and adding a special
HTTP header.
This policy prevents users from logging in with personal Gmail accounts or other
non-corporate Google accounts.
For detailed instructions, see the Google Workspace Admin Help article on
[blocking access to consumer accounts](https://support.google.com/a/answer/1668854?hl=en#zippy=%2Cstep-choose-a-web-proxy-server%2Cstep-configure-the-network-to-block-certain-accounts).
The general steps are as follows:
1. **Intercept Requests**: Configure your web proxy to intercept all requests
to `google.com`.
2. **Add HTTP Header**: For each intercepted request, add the
`X-GoogApps-Allowed-Domains` HTTP header.
3. **Specify Domains**: The value of the header should be a comma-separated
list of your approved Google Workspace domain names.
**Example header:**
```
X-GoogApps-Allowed-Domains: my-corporate-domain.com, secondary-domain.com
```
When this header is present, Google's authentication service will only allow
logins from accounts belonging to the specified domains.
## Putting it all together: example system `settings.json`
Here is an example of a system `settings.json` file that combines several of the
patterns discussed above to create a secure, controlled environment for Gemini
+2
View File
@@ -38,6 +38,8 @@ overview of Gemini CLI, see the [main documentation page](../index.md).
files and directories from being accessed by tools.
- **[Context files (GEMINI.md)](./gemini-md.md):** Provide persistent,
hierarchical context to the model.
- **[System prompt override](./system-prompt.md):** Replace the builtin system
instructions using `GEMINI_SYSTEM_MD`.
## Non-interactive mode
+4 -4
View File
@@ -84,10 +84,10 @@ available combinations.
#### External Tools
| Action | Keys |
| ---------------------------------------------- | ---------- |
| Open the current prompt in an external editor. | `Ctrl + X` |
| Paste from the clipboard. | `Ctrl + V` |
| Action | Keys |
| ---------------------------------------------- | ------------------------- |
| Open the current prompt in an external editor. | `Ctrl + X` |
| Paste from the clipboard. | `Ctrl + V`<br />`Cmd + V` |
#### App Controls
+11 -15
View File
@@ -6,24 +6,20 @@ 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:
Model routing is managed by the `ModelAvailabilityService`, which monitors model
health and automatically routes requests to available models based on defined
policies.
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.
1. **Model failure:** If the currently selected model fails (e.g., due to quota
or server errors), the CLI will iniate 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`.
2. **User consent:** Depending on the failure and the model's policy, the CLI
may prompt you to switch to a fallback model (by default always prompts
you).
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.
3. **Model switch:** If approved, or if the policy allows for silent fallback,
the CLI will use an available fallback model for the current turn or the
remainder of the session.
### Model selection precedence
+31 -27
View File
@@ -1,8 +1,13 @@
# 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.
Select your Gemini CLI model. The `/model` command lets you configure the model
used by Gemini CLI, giving you more control over your results. Use **Pro**
models for complex tasks and reasoning, **Flash** models for high speed results,
or the (recommended) **Auto** setting to choose the best model for your tasks.
> **Note:** The `/model` command (and the `--model` flag) does not override the
> model used by sub-agents. Consequently, even when using the `/model` flag you
> may see other models used in your model usage reports.
## How to use the `/model` command
@@ -12,26 +17,25 @@ Use the following command in Gemini CLI:
/model
```
Running this command will open a dialog with your model options:
Running this command will open a dialog with your 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 |
| Option | Description | Models |
| ----------------- | -------------------------------------------------------------- | ---------------------------------------------------------------------- |
| Auto (Gemini 3) | Let the system choose the best Gemini 3 model for your task. | gemini-3-pro-preview (if enabled), gemini-3-flash-preview (if enabled) |
| Auto (Gemini 2.5) | Let the system choose the best Gemini 2.5 model for your task. | gemini-2.5-pro, gemini-2.5-flash |
| Manual | Select a specific model. | Any available model. |
### Gemini 3 Pro and preview features
We recommend selecting one of the above **Auto** options. However, you can
select **Manual** to select a specific model from those available.
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).
### Gemini 3 and preview features
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.
> **Note:** Gemini 3 is not currently available on all account types. To learn
> more about Gemini 3 access, refer to
> [Gemini 3 on Gemini CLI](../get-started/gemini-3.md).
To enable Gemini 3 Pro and Gemini 3 Flash (if available), enable
[**Preview Features** by using the `settings` command](../cli/settings.md).
You can also use the `--model` flag to specify a particular Gemini model on
startup. For more details, refer to the
@@ -42,16 +46,16 @@ 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).
- **Default to Auto.** For most users, the _Auto_ option 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.
need your model to be a little "smarter," you can manually select 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:
+8 -6
View File
@@ -94,15 +94,16 @@ they appear in the UI.
| 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` |
| 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` |
| Allowed Environment Variables | `security.environmentVariableRedaction.allowed` | Environment variables to always allow (bypass redaction). | `[]` |
| Blocked Environment Variables | `security.environmentVariableRedaction.blocked` | Environment variables to always redact. | `[]` |
### Experimental
@@ -110,3 +111,4 @@ they appear in the UI.
| ----------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------ | ------- |
| 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` |
| Agent Skills | `experimental.skills` | Enable Agent Skills (experimental). | `false` |
+156
View File
@@ -0,0 +1,156 @@
# Agent Skills
_Note: This is an experimental feature enabled via `experimental.skills`. You
can also search for "Skills" within the `/settings` interactive UI to toggle
this and manage other skill-related settings._
Agent Skills allow you to extend Gemini CLI with specialized expertise,
procedural workflows, and task-specific resources. Based on the
[Agent Skills](https://agentskills.io) open standard, a "skill" is a
self-contained directory that packages instructions and assets into a
discoverable capability.
## Overview
Unlike general context files ([`GEMINI.md`](./gemini-md.md)), which provide
persistent project-wide background, Skills represent **on-demand expertise**.
This allows Gemini to maintain a vast library of specialized capabilities—such
as security auditing, cloud deployments, or codebase migrations—without
cluttering the model's immediate context window.
Gemini autonomously decides when to employ a skill based on your request and the
skill's description. When a relevant skill is identified, the model "pulls in"
the full instructions and resources required to complete the task using the
`activate_skill` tool.
## Key Benefits
- **Shared Expertise:** Package complex workflows (like a specific team's PR
review process) into a folder that anyone can use.
- **Repeatable Workflows:** Ensure complex multi-step tasks are performed
consistently by providing a procedural framework.
- **Resource Bundling:** Include scripts, templates, or example data alongside
instructions so the agent has everything it needs.
- **Progressive Disclosure:** Only skill metadata (name and description) is
loaded initially. Detailed instructions and resources are only disclosed when
the model explicitly activates the skill, saving context tokens.
## Skill Discovery Tiers
Gemini CLI discovers skills from three primary locations:
1. **Project Skills** (`.gemini/skills/`): Project-specific skills that are
typically committed to version control and shared with the team.
2. **User Skills** (`~/.gemini/skills/`): Personal skills available across all
your projects.
3. **Extension Skills**: Skills bundled within installed
[extensions](../extensions/index.md).
**Precedence:** If multiple skills share the same name, higher-precedence
locations override lower ones: **Project > User > Extension**.
## Managing Skills
### In an Interactive Session
Use the `/skills` slash command to view and manage available expertise:
- `/skills list` (default): Shows all discovered skills and their status.
- `/skills disable <name>`: Prevents a specific skill from being used.
- `/skills enable <name>`: Re-enables a disabled skill.
- `/skills reload`: Refreshes the list of discovered skills from all tiers.
_Note: `/skills disable` and `/skills enable` default to the `user` scope. Use
`--scope project` to manage project-specific settings._
### From the Terminal
The `gemini skills` command provides management utilities:
```bash
# List all discovered skills
gemini skills list
# Enable/disable skills. Can use --scope to specify project or user
gemini skills enable my-expertise
gemini skills disable my-expertise
```
## Creating a Skill
A skill is a directory containing a `SKILL.md` file at its root. This file uses
YAML frontmatter for metadata and Markdown for instructions.
### Basic Structure
```markdown
---
name: <unique-name>
description: <what the skill does and when Gemini should use it>
---
<your instructions for how the agent should behave / use the skill>
```
- **`name`**: A unique identifier (lowercase, alphanumeric, and dashes).
- **`description`**: The most critical field. Gemini uses this to decide when
the skill is relevant. Be specific about the expertise provided.
- **Body**: Everything below the second `---` is injected as expert procedural
guidance for the model.
### Example: Team Code Reviewer
```markdown
---
name: code-reviewer
description:
Expertise in reviewing code for style, security, and performance. Use when the
user asks for "feedback," a "review," or to "check" their changes.
---
# Code Reviewer
You are an expert code reviewer. When reviewing code, follow this workflow:
1. **Analyze**: Review the staged changes or specific files provided. Ensure
that the changes are scoped properly and represent minimal changes required
to address the issue.
2. **Style**: Ensure code follows the project's conventions and idiomatic
patterns as described in the `GEMINI.md` file.
3. **Security**: Flag any potential security vulnerabilities.
4. **Tests**: Verify that new logic has corresponding test coverage and that
the test coverage adequately validates the changes.
Provide your feedback as a concise bulleted list of "Strengths" and
"Opportunities."
```
### Resource Conventions
While you can structure your skill directory however you like, the Agent Skills
standard encourages these conventions:
- **`scripts/`**: Executable scripts (bash, python, node) the agent can run.
- **`references/`**: Static documentation, schemas, or example data for the
agent to consult.
- **`assets/`**: Code templates, boilerplate, or binary resources.
When a skill is activated, Gemini CLI provides the model with a tree view of the
entire skill directory, allowing it to discover and utilize these assets.
## How it Works (Security & Privacy)
1. **Discovery**: At the start of a session, Gemini CLI scans the discovery
tiers and injects the name and description of all enabled skills into the
system prompt.
2. **Activation**: When Gemini identifies a task matching a skill's
description, it calls the `activate_skill` tool.
3. **Consent**: You will see a confirmation prompt in the UI detailing the
skill's name, purpose, and the directory path it will gain access to.
4. **Injection**: Upon your approval:
- The `SKILL.md` body and folder structure is added to the conversation
history.
- The skill's directory is added to the agent's allowed file paths, granting
it permission to read any bundled assets.
5. **Execution**: The model proceeds with the specialized expertise active. It
is instructed to prioritize the skill's procedural guidance within reason.
+93
View File
@@ -0,0 +1,93 @@
# System Prompt Override (GEMINI_SYSTEM_MD)
The core system instructions that guide Gemini CLI can be completely replaced
with your own Markdown file. This feature is controlled via the
`GEMINI_SYSTEM_MD` environment variable.
## Overview
The `GEMINI_SYSTEM_MD` variable instructs the CLI to use an external Markdown
file for its system prompt, completely overriding the built-in default. This is
a full replacement, not a merge. If you use a custom file, none of the original
core instructions will apply unless you include them yourself.
This feature is intended for advanced users who need to enforce strict,
project-specific behavior or create a customized persona.
> Tip: You can export the current default system prompt to a file first, review
> it, and then selectively modify or replace it (see
> [“Export the default prompt”](#export-the-default-prompt-recommended)).
## How to enable
You can set the environment variable temporarily in your shell, or persist it
via a `.gemini/.env` file. See
[Persisting Environment Variables](../get-started/authentication.md#persisting-environment-variables).
- Use the project default path (`.gemini/system.md`):
- `GEMINI_SYSTEM_MD=true` or `GEMINI_SYSTEM_MD=1`
- The CLI reads `./.gemini/system.md` (relative to your current project
directory).
- Use a custom file path:
- `GEMINI_SYSTEM_MD=/absolute/path/to/my-system.md`
- Relative paths are supported and resolved from the current working
directory.
- Tilde expansion is supported (e.g., `~/my-system.md`).
- Disable the override (use builtin prompt):
- `GEMINI_SYSTEM_MD=false` or `GEMINI_SYSTEM_MD=0` or unset the variable.
If the override is enabled but the target file does not exist, the CLI will
error with: `missing system prompt file '<path>'`.
## Quick examples
- Oneoff session using a project file:
- `GEMINI_SYSTEM_MD=1 gemini`
- Persist for a project using `.gemini/.env`:
- Create `.gemini/system.md`, then add to `.gemini/.env`:
- `GEMINI_SYSTEM_MD=1`
- Use a custom file under your home directory:
- `GEMINI_SYSTEM_MD=~/prompts/SYSTEM.md gemini`
## UI indicator
When `GEMINI_SYSTEM_MD` is active, the CLI shows a `|⌐■_■|` indicator in the UI
to signal custom systemprompt mode.
## Export the default prompt (recommended)
Before overriding, export the current default prompt so you can review required
safety and workflow rules.
- Write the builtin prompt to the project default path:
- `GEMINI_WRITE_SYSTEM_MD=1 gemini`
- Or write to a custom path:
- `GEMINI_WRITE_SYSTEM_MD=~/prompts/DEFAULT_SYSTEM.md gemini`
This creates the file and writes the current builtin system prompt to it.
## Best practices: SYSTEM.md vs GEMINI.md
- SYSTEM.md (firmware):
- Nonnegotiable operational rules: safety, tooluse protocols, approvals, and
mechanics that keep the CLI reliable.
- Stable across tasks and projects (or per project when needed).
- GEMINI.md (strategy):
- Persona, goals, methodologies, and project/domain context.
- Evolves per task; relies on SYSTEM.md for safe execution.
Keep SYSTEM.md minimal but complete for safety and tool operation. Keep
GEMINI.md focused on highlevel guidance and project specifics.
## Troubleshooting
- Error: `missing system prompt file '…'`
- Ensure the referenced path exists and is readable.
- For `GEMINI_SYSTEM_MD=1|true`, create `./.gemini/system.md` in your project.
- Override not taking effect
- Confirm the variable is loaded (use `.gemini/.env` or export in your shell).
- Paths are resolved from the current working directory; try an absolute path.
- Restore defaults
- Unset `GEMINI_SYSTEM_MD` or set it to `0`/`false`.
+41 -12
View File
@@ -74,15 +74,16 @@ observability framework — Gemini CLI's observability system provides:
All telemetry behavior is controlled through your `.gemini/settings.json` file.
Environment variables can be used to override the settings in the file.
| 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` |
| 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` |
| `useCliAuth` | `GEMINI_TELEMETRY_USE_CLI_AUTH` | Use CLI credentials for telemetry (GCP target only) | `true`/`false` | `false` |
**Note on boolean environment variables:** For the boolean settings (`enabled`,
`logPrompts`, `useCollector`), setting the corresponding environment variable to
@@ -130,6 +131,34 @@ Before using either method below, complete these steps:
--project="$OTLP_GOOGLE_CLOUD_PROJECT"
```
### Authenticating with CLI Credentials
By default, the telemetry collector for Google Cloud uses Application Default
Credentials (ADC). However, you can configure it to use the same OAuth
credentials that you use to log in to the Gemini CLI. This is useful in
environments where you don't have ADC set up.
To enable this, set the `useCliAuth` property in your `telemetry` settings to
`true`:
```json
{
"telemetry": {
"enabled": true,
"target": "gcp",
"useCliAuth": true
}
}
```
**Important:**
- This setting requires the use of **Direct Export** (in-process exporters).
- It **cannot** be used with `useCollector: true`. If you enable both, telemetry
will be disabled and an error will be logged.
- The CLI will automatically use your credentials to authenticate with Google
Cloud Trace, Metrics, and Logging APIs.
### Direct export (recommended)
Sends telemetry directly to Google Cloud services. No collector needed.
@@ -268,7 +297,7 @@ Captures startup configuration and user prompt submissions.
#### Tools
Captures tool executions, output truncation, and Smart Edit behavior.
Captures tool executions, output truncation, and Edit behavior.
- `gemini_cli.tool_call`: Emitted for each tool (function) call.
- **Attributes**:
@@ -296,11 +325,11 @@ Captures tool executions, output truncation, and Smart Edit behavior.
- `lines` (int)
- `prompt_id` (string)
- `gemini_cli.smart_edit_strategy`: Smart Edit strategy chosen.
- `gemini_cli.edit_strategy`: Edit strategy chosen.
- **Attributes**:
- `strategy` (string)
- `gemini_cli.smart_edit_correction`: Smart Edit correction result.
- `gemini_cli.edit_correction`: Edit correction result.
- **Attributes**:
- `correction` ("success" | "failure")
+4
View File
@@ -2,6 +2,10 @@
This page contains tutorials for interacting with Gemini CLI.
## Agent Skills
- [Getting Started with Agent Skills](./tutorials/skills-getting-started.md)
## Setting up a Model Context Protocol (MCP) server
> [!CAUTION] Before using a third-party MCP server, ensure you trust its source
@@ -0,0 +1,124 @@
# Getting Started with Agent Skills
Agent Skills allow you to extend Gemini CLI with specialized expertise. This
tutorial will guide you through creating your first skill, enabling it, and
using it in a session.
## 1. Enable Agent Skills
Agent Skills are currently an experimental feature and must be enabled in your
settings.
### Via the interactive UI
1. Start a Gemini CLI session by running `gemini`.
2. Type `/settings` to open the interactive settings dialog.
3. Search for "Skills".
4. Toggle **Agent Skills** to `true`.
5. Press `Esc` to save and exit. You may need to restart the CLI for the
changes to take effect.
### Via `settings.json`
Alternatively, you can manually edit your global settings file at
`~/.gemini/settings.json` (create it if it doesn't exist):
```json
{
"experimental": {
"skills": true
}
}
```
## 2. Create Your First Skill
A skill is a directory containing a `SKILL.md` file. Let's create an **API
Auditor** skill that helps you verify if local or remote endpoints are
responding correctly.
1. **Create the skill directory structure:**
```bash
mkdir -p .gemini/skills/api-auditor/scripts
```
2. **Create the `SKILL.md` file:** Create a file at
`.gemini/skills/api-auditor/SKILL.md` with the following content:
```markdown
---
name: api-auditor
description:
Expertise in auditing and testing API endpoints. Use when the user asks to
"check", "test", or "audit" a URL or API.
---
# API Auditor Instructions
You act as a QA engineer specialized in API reliability. When this skill is
active, you MUST:
1. **Audit**: Use the bundled `scripts/audit.js` utility to check the
status of the provided URL.
2. **Report**: Analyze the output (status codes, latency) and explain any
failures in plain English.
3. **Secure**: Remind the user if they are testing a sensitive endpoint
without an `https://` protocol.
```
3. **Create the bundled Node.js script:** Create a file at
`.gemini/skills/api-auditor/scripts/audit.js`. This script will be used by
the agent to perform the actual check:
```javascript
// .gemini/skills/api-auditor/scripts/audit.js
const url = process.argv[2];
if (!url) {
console.error('Usage: node audit.js <url>');
process.exit(1);
}
console.log(`Auditing ${url}...`);
fetch(url, { method: 'HEAD' })
.then((r) => console.log(`Result: Success (Status ${r.status})`))
.catch((e) => console.error(`Result: Failed (${e.message})`));
```
## 3. Verify the Skill is Discovered
Use the `/skills` slash command (or `gemini skills list` from your terminal) to
see if Gemini CLI has found your new skill.
In a Gemini CLI session:
```
/skills list
```
You should see `api-auditor` in the list of available skills.
## 4. Use the Skill in a Chat
Now, let's see the skill in action. Start a new session and ask a question about
an endpoint.
**User:** "Can you audit http://geminili.com"
Gemini will recognize the request matches the `api-auditor` description and will
ask for your permission to activate it.
**Model:** (After calling `activate_skill`) "I've activated the **api-auditor**
skill. I'll run the audit script now..."
Gemini will then use the `run_shell_command` tool to execute your bundled Node
script:
`node .gemini/skills/api-auditor/scripts/audit.js http://geminili.com`
## Next Steps
- Explore [Agent Skills Authoring Guide](../skills.md#creating-a-skill) to learn
about more advanced skill features.
- Learn how to share skills via [Extensions](../../extensions/index.md).
+24 -3
View File
@@ -1,13 +1,32 @@
# 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.
## Quick start
To create your first policy:
1. **Create the policy directory** if it doesn't exist:
```bash
mkdir -p ~/.gemini/policies
```
2. **Create a new policy file** (e.g., `~/.gemini/policies/my-rules.toml`). You
can use any filename ending in `.toml`; all such files in this directory
will be loaded and combined:
```toml
[[rule]]
toolName = "run_shell_command"
commandPrefix = "git status"
decision = "allow"
priority = 100
```
3. **Run a command** that triggers the policy (e.g., ask Gemini CLI to
`git status`). The tool will now execute automatically without prompting for
confirmation.
## Core concepts
The policy engine operates on a set of rules. Each rule is a combination of
@@ -239,6 +258,8 @@ 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**.
- **Agent delegation** (like `delegate_to_agent`) is **allowed** (sub-agent
actions are checked individually).
- **Write tools** (like `write_file`, `run_shell_command`) default to
**`ask_user`**.
- In **`yolo`** mode, a high-priority rule allows all tools.
+18 -3
View File
@@ -163,11 +163,11 @@ The file has the following structure:
your extension in the CLI. Note that we expect this name to match the
extension directory name.
- `version`: The version of the extension.
- `mcpServers`: A map of MCP servers to configure. The key is the name of the
- `mcpServers`: A map of MCP servers to settings. The key is the name of the
server, and the value is the server configuration. These servers will be
loaded on startup just like MCP servers configured in a
loaded on startup just like MCP servers settingsd in a
[`settings.json` file](../get-started/configuration.md). If both an extension
and a `settings.json` file configure an MCP server with the same name, the
and a `settings.json` file settings an MCP server with the same name, the
server defined in the `settings.json` file takes precedence.
- Note that all MCP server configuration options are supported except for
`trust`.
@@ -223,6 +223,21 @@ 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`).
You can view a list of an extension's settings by running:
```
gemini extensions settings list <extension name>
```
and you can update a given setting using:
```
gemini extensions settings set <extension name> <setting name> [--scope <scope>]
```
- `--scope`: The scope to set the setting in (`user` or `workspace`). This is
optional and will default to `user`.
### Custom commands
Extensions can provide [custom commands](../cli/custom-commands.md) by placing
+214 -28
View File
@@ -367,6 +367,12 @@ their corresponding top-level category object in your `settings.json` file.
"model": "gemini-3-pro-preview"
}
},
"gemini-3-flash-preview": {
"extends": "chat-base-3",
"modelConfig": {
"model": "gemini-3-flash-preview"
}
},
"gemini-2.5-pro": {
"extends": "chat-base-2.5",
"modelConfig": {
@@ -496,6 +502,11 @@ their corresponding top-level category object in your `settings.json` file.
"model": "gemini-3-pro-preview"
}
},
"chat-compression-3-flash": {
"modelConfig": {
"model": "gemini-3-flash-preview"
}
},
"chat-compression-2.5-pro": {
"modelConfig": {
"model": "gemini-2.5-pro"
@@ -524,6 +535,11 @@ their corresponding top-level category object in your `settings.json` file.
with (and override) the built-in aliases.
- **Default:** `{}`
- **`modelConfigs.customOverrides`** (array):
- **Description:** Custom model config overrides. These are merged with (and
added to) the built-in overrides.
- **Default:** `[]`
- **`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
@@ -605,6 +621,11 @@ their corresponding top-level category object in your `settings.json` file.
shell command. Defaults to 5 minutes.
- **Default:** `300`
- **`tools.shell.enableShellOutputEfficiency`** (boolean):
- **Description:** Enable shell output efficiency optimizations for better
performance.
- **Default:** `true`
- **`tools.autoAccept`** (boolean):
- **Description:** Automatically accept and execute tool calls that are
considered safe (e.g., read-only operations).
@@ -663,20 +684,10 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `1000`
- **Requires restart:** Yes
- **`tools.enableMessageBusIntegration`** (boolean):
- **Description:** Enable policy-based tool confirmation via message bus
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`
- **Description:** Enables the hooks system experiment. When disabled, the
hooks system is completely deactivated regardless of other settings.
- **Default:** `true`
- **Requires restart:** Yes
#### `mcp`
@@ -696,12 +707,6 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `undefined`
- **Requires restart:** Yes
#### `useSmartEdit`
- **`useSmartEdit`** (boolean):
- **Description:** Enable the smart-edit tool instead of the replace tool.
- **Default:** `true`
#### `useWriteTodos`
- **`useWriteTodos`** (boolean):
@@ -715,6 +720,11 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `false`
- **Requires restart:** Yes
- **`security.enablePermanentToolApproval`** (boolean):
- **Description:** Enable the "Allow for all future sessions" option in tool
confirmation dialogs.
- **Default:** `false`
- **`security.blockGitExtensions`** (boolean):
- **Description:** Blocks installing and loading extensions from Git.
- **Default:** `false`
@@ -725,6 +735,22 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `false`
- **Requires restart:** Yes
- **`security.environmentVariableRedaction.allowed`** (array):
- **Description:** Environment variables to always allow (bypass redaction).
- **Default:** `[]`
- **Requires restart:** Yes
- **`security.environmentVariableRedaction.blocked`** (array):
- **Description:** Environment variables to always redact.
- **Default:** `[]`
- **Requires restart:** Yes
- **`security.environmentVariableRedaction.enabled`** (boolean):
- **Description:** Enable redaction of environment variables that may contain
secrets.
- **Default:** `false`
- **Requires restart:** Yes
- **`security.auth.selectedType`** (string):
- **Description:** The currently selected authentication type.
- **Default:** `undefined`
@@ -767,6 +793,12 @@ their corresponding top-level category object in your `settings.json` file.
#### `experimental`
- **`experimental.enableAgents`** (boolean):
- **Description:** Enable local and remote subagents. Warning: Experimental
feature, uses YOLO mode for subagents
- **Default:** `false`
- **Requires restart:** Yes
- **`experimental.extensionManagement`** (boolean):
- **Description:** Enable extension management features.
- **Default:** `true`
@@ -777,13 +809,13 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `false`
- **Requires restart:** Yes
- **`experimental.isModelAvailabilityServiceEnabled`** (boolean):
- **Description:** Enable model routing using new availability service.
- **`experimental.jitContext`** (boolean):
- **Description:** Enable Just-In-Time (JIT) context loading.
- **Default:** `false`
- **Requires restart:** Yes
- **`experimental.jitContext`** (boolean):
- **Description:** Enable Just-In-Time (JIT) context loading.
- **`experimental.skills`** (boolean):
- **Description:** Enable Agent Skills (experimental).
- **Default:** `false`
- **Requires restart:** Yes
@@ -811,15 +843,111 @@ their corresponding top-level category object in your `settings.json` file.
- **`experimental.codebaseInvestigatorSettings.model`** (string):
- **Description:** The model to use for the Codebase Investigator agent.
- **Default:** `"gemini-2.5-pro"`
- **Default:** `"auto"`
- **Requires restart:** Yes
- **`experimental.useOSC52Paste`** (boolean):
- **Description:** Use OSC 52 sequence for pasting instead of clipboardy
(useful for remote sessions).
- **Default:** `false`
- **`experimental.introspectionAgentSettings.enabled`** (boolean):
- **Description:** Enable the Introspection Agent.
- **Default:** `false`
- **Requires restart:** Yes
#### `skills`
- **`skills.disabled`** (array):
- **Description:** List of disabled skills.
- **Default:** `[]`
- **Requires restart:** Yes
#### `hooks`
- **`hooks`** (object):
- **Description:** Hook configurations for intercepting and customizing agent
behavior.
- **Default:** `{}`
- **`hooks.enabled`** (boolean):
- **Description:** Canonical toggle for the hooks system. When disabled, no
hooks will be executed.
- **Default:** `false`
- **`hooks.disabled`** (array):
- **Description:** List of hook names (commands) that should be disabled.
Hooks in this list will not execute even if configured.
- **Default:** `[]`
- **`hooks.notifications`** (boolean):
- **Description:** Show visual indicators when hooks are executing.
- **Default:** `true`
- **`hooks.BeforeTool`** (array):
- **Description:** Hooks that execute before tool execution. Can intercept,
validate, or modify tool calls.
- **Default:** `[]`
- **`hooks.AfterTool`** (array):
- **Description:** Hooks that execute after tool execution. Can process
results, log outputs, or trigger follow-up actions.
- **Default:** `[]`
- **`hooks.BeforeAgent`** (array):
- **Description:** Hooks that execute before agent loop starts. Can set up
context or initialize resources.
- **Default:** `[]`
- **`hooks.AfterAgent`** (array):
- **Description:** Hooks that execute after agent loop completes. Can perform
cleanup or summarize results.
- **Default:** `[]`
- **`hooks.Notification`** (array):
- **Description:** Hooks that execute on notification events (errors,
warnings, info). Can log or alert on specific conditions.
- **Default:** `[]`
- **`hooks.SessionStart`** (array):
- **Description:** Hooks that execute when a session starts. Can initialize
session-specific resources or state.
- **Default:** `[]`
- **`hooks.SessionEnd`** (array):
- **Description:** Hooks that execute when a session ends. Can perform cleanup
or persist session data.
- **Default:** `[]`
- **`hooks.PreCompress`** (array):
- **Description:** Hooks that execute before chat history compression. Can
back up or analyze conversation before compression.
- **Default:** `[]`
- **`hooks.BeforeModel`** (array):
- **Description:** Hooks that execute before LLM requests. Can modify prompts,
inject context, or control model parameters.
- **Default:** `[]`
- **`hooks.AfterModel`** (array):
- **Description:** Hooks that execute after LLM responses. Can process
outputs, extract information, or log interactions.
- **Default:** `[]`
- **`hooks.BeforeToolSelection`** (array):
- **Description:** Hooks that execute before tool selection. Can filter or
prioritize available tools dynamically.
- **Default:** `[]`
#### `admin`
- **`admin.secureModeEnabled`** (boolean):
- **Description:** If true, disallows yolo mode from being used.
- **Default:** `false`
- **`admin.extensions.enabled`** (boolean):
- **Description:** If false, disallows extensions from being installed or
used.
- **Default:** `true`
- **`admin.mcp.enabled`** (boolean):
- **Description:** If false, disallows MCP servers from being used.
- **Default:** `true`
<!-- SETTINGS-AUTOGEN:END -->
#### `mcpServers`
@@ -1052,6 +1180,18 @@ the `advanced.excludedEnvVars` setting in your `settings.json` file.
- **`GEMINI_SANDBOX`**:
- Alternative to the `sandbox` setting in `settings.json`.
- Accepts `true`, `false`, `docker`, `podman`, or a custom command string.
- **`GEMINI_SYSTEM_MD`**:
- Replaces the builtin system prompt with content from a Markdown file.
- `true`/`1`: Use project default path `./.gemini/system.md`.
- Any other string: Treat as a path (relative/absolute supported, `~`
expands).
- `false`/`0` or unset: Use the builtin prompt. See
[System Prompt Override](../cli/system-prompt.md).
- **`GEMINI_WRITE_SYSTEM_MD`**:
- Writes the current builtin system prompt to a file for review.
- `true`/`1`: Write to `./.gemini/system.md`. Otherwise treat the value as a
path.
- Run the CLI once with this set to generate the file.
- **`SEATBELT_PROFILE`** (macOS specific):
- Switches the Seatbelt (`sandbox-exec`) profile on macOS.
- `permissive-open`: (Default) Restricts writes to the project folder (and a
@@ -1077,6 +1217,52 @@ the `advanced.excludedEnvVars` setting in your `settings.json` file.
- Specifies the endpoint for the code assist server.
- This is useful for development and testing.
### Environment variable redaction
To prevent accidental leakage of sensitive information, Gemini CLI automatically
redacts potential secrets from environment variables when executing tools (such
as shell commands). This "best effort" redaction applies to variables inherited
from the system or loaded from `.env` files.
**Default Redaction Rules:**
- **By Name:** Variables are redacted if their names contain sensitive terms
like `TOKEN`, `SECRET`, `PASSWORD`, `KEY`, `AUTH`, `CREDENTIAL`, `PRIVATE`, or
`CERT`.
- **By Value:** Variables are redacted if their values match known secret
patterns, such as:
- Private keys (RSA, OpenSSH, PGP, etc.)
- Certificates
- URLs containing credentials
- API keys and tokens (GitHub, Google, AWS, Stripe, Slack, etc.)
- **Specific Blocklist:** Certain variables like `CLIENT_ID`, `DB_URI`,
`DATABASE_URL`, and `CONNECTION_STRING` are always redacted by default.
**Allowlist (Never Redacted):**
- Common system variables (e.g., `PATH`, `HOME`, `USER`, `SHELL`, `TERM`,
`LANG`).
- Variables starting with `GEMINI_CLI_`.
- GitHub Action specific variables.
**Configuration:**
You can customize this behavior in your `settings.json` file:
- **`security.allowedEnvironmentVariables`**: A list of variable names to
_never_ redact, even if they match sensitive patterns.
- **`security.blockedEnvironmentVariables`**: A list of variable names to
_always_ redact, even if they don't match sensitive patterns.
```json
{
"security": {
"allowedEnvironmentVariables": ["MY_PUBLIC_KEY", "NOT_A_SECRET_TOKEN"],
"blockedEnvironmentVariables": ["INTERNAL_IP_ADDRESS"]
}
}
```
## Command-line arguments
Arguments passed directly when running the CLI can override other configurations
+1 -1
View File
@@ -24,7 +24,7 @@ Rename the photos in my "photos" directory based on their contents.
Result: Gemini will ask for permission to rename your files.
Select **Yes, allow once.** and your files will be renamed:
Select **Allow once** and your files will be renamed:
```bash
photos/yellow_flowers.png
+62 -56
View File
@@ -1,64 +1,37 @@
# Gemini 3 Pro on Gemini CLI (join the waitlist)
# Gemini 3 Pro and Gemini 3 Flash on Gemini CLI
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.
Gemini 3 Pro and Gemini 3 Flash are now available on Gemini CLI! Currently, most
paid customers of Gemini CLI will have access to both Gemini 3 Pro and Gemini 3
Flash, including the following subscribers:
**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.
- Google AI Pro and Google AI Ultra (excluding business customers).
- Gemini Code Assist Standard and Enterprise (requires
[administrative enablement](#administrator-instructions)).
- Paid Gemini API and Vertex API key holders.
## Do I need to join the waitlist?
For free tier users:
The following users will be **automatically granted access** to Gemini 3 Pro on
Gemini CLI:
- If you signed up for the waitlist, please check your email for details. Weve
onboarded everyone who signed up to the previously available waitlist.
- If you were not on our waitlist, were rolling out additional access gradually
to ensure the experience remains fast and reliable. Stay tuned for more
details.
- 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).
## How to get started with Gemini 3 on Gemini CLI
For **Gemini Code Assist Enterprise users**, access is coming soon.
Get started by upgrading Gemini CLI to the latest version (0.21.1):
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.
```bash
npm install -g @google/gemini-cli@latest
```
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).
After youve confirmed your version is 0.21.1 or later:
## How to join the waitlist
1. Use the `/settings` command in Gemini CLI.
2. Toggle **Preview Features** to `true`.
3. Run `/model` and select **Auto (Gemini 3)**.
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).
For more information, see [Gemini CLI model selection](../cli/model.md).
### Usage limits and fallback
@@ -76,12 +49,12 @@ 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.
> **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 and routing types
### Model selection and 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.
@@ -100,6 +73,39 @@ manage your usage limits:
To learn more about selecting a model and routing, refer to
[Gemini CLI Model Selection](../cli/model.md).
## How to enable Gemini 3 with Gemini CLI on Gemini Code Assist
If you're using Gemini Code Assist Standard or Gemini Code Assist Enterprise,
enabling Gemini 3 Pro on Gemini CLI requires configuring your release channels.
Using Gemini 3 Pro will require two steps: administrative enablement and user
enablement.
To learn more about these settings, refer to
[Configure Gemini Code Assist release channels](https://developers.google.com/gemini-code-assist/docs/configure-release-channels).
### Administrator instructions
An administrator with **Google Cloud Settings Admin** permissions must follow
these directions:
- Navigate to the Google Cloud Project you're using with Gemini CLI for Code
Assist.
- Go to **Admin for Gemini** > **Settings**.
- Under **Release channels for Gemini Code Assist in local IDEs** select
**Preview**.
- Click **Save changes**.
### User instructions
Wait for two to three minutes after your administrator has enabled **Preview**,
then:
- Open Gemini CLI.
- Use the `/settings` command.
- Set **Preview Features** to `true`.
Restart Gemini CLI and you should have access to Gemini 3.
## Need help?
If you need help, we recommend searching for an existing
+856
View File
@@ -0,0 +1,856 @@
# Hooks on Gemini CLI: Best practices
This guide covers security considerations, performance optimization, debugging
techniques, and privacy considerations for developing and deploying hooks in
Gemini CLI.
## Performance
### Keep hooks fast
Hooks run synchronously—slow hooks delay the agent loop. Optimize for speed by
using parallel operations:
```javascript
// Sequential operations are slower
const data1 = await fetch(url1).then((r) => r.json());
const data2 = await fetch(url2).then((r) => r.json());
const data3 = await fetch(url3).then((r) => r.json());
// Prefer parallel operations for better performance
// Start requests concurrently
const p1 = fetch(url1).then((r) => r.json());
const p2 = fetch(url2).then((r) => r.json());
const p3 = fetch(url3).then((r) => r.json());
// Wait for all results
const [data1, data2, data3] = await Promise.all([p1, p2, p3]);
```
### Cache expensive operations
Store results between invocations to avoid repeated computation:
```javascript
const fs = require('fs');
const path = require('path');
const CACHE_FILE = '.gemini/hook-cache.json';
function readCache() {
try {
return JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8'));
} catch {
return {};
}
}
function writeCache(data) {
fs.writeFileSync(CACHE_FILE, JSON.stringify(data, null, 2));
}
async function main() {
const cache = readCache();
const cacheKey = `tool-list-${(Date.now() / 3600000) | 0}`; // Hourly cache
if (cache[cacheKey]) {
console.log(JSON.stringify(cache[cacheKey]));
return;
}
// Expensive operation
const result = await computeExpensiveResult();
cache[cacheKey] = result;
writeCache(cache);
console.log(JSON.stringify(result));
}
```
### Use appropriate events
Choose hook events that match your use case to avoid unnecessary execution.
`AfterAgent` fires once per agent loop completion, while `AfterModel` fires
after every LLM call (potentially multiple times per loop):
```json
// If checking final completion, use AfterAgent instead of AfterModel
{
"hooks": {
"AfterAgent": [
{
"matcher": "*",
"hooks": [
{
"name": "final-checker",
"command": "./check-completion.sh"
}
]
}
]
}
}
```
### Filter with matchers
Use specific matchers to avoid unnecessary hook execution. Instead of matching
all tools with `*`, specify only the tools you need:
```json
{
"matcher": "write_file|replace",
"hooks": [
{
"name": "validate-writes",
"command": "./validate.sh"
}
]
}
```
### Optimize JSON parsing
For large inputs, use streaming JSON parsers to avoid loading everything into
memory:
```javascript
// Standard approach: parse entire input
const input = JSON.parse(await readStdin());
const content = input.tool_input.content;
// For very large inputs: stream and extract only needed fields
const { createReadStream } = require('fs');
const JSONStream = require('JSONStream');
const stream = createReadStream(0).pipe(JSONStream.parse('tool_input.content'));
let content = '';
stream.on('data', (chunk) => {
content += chunk;
});
```
## Debugging
### Log to files
Write debug information to dedicated log files:
```bash
#!/usr/bin/env bash
LOG_FILE=".gemini/hooks/debug.log"
# Log with timestamp
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"
}
input=$(cat)
log "Received input: ${input:0:100}..."
# Hook logic here
log "Hook completed successfully"
```
### Use stderr for errors
Error messages on stderr are surfaced appropriately based on exit codes:
```javascript
try {
const result = dangerousOperation();
console.log(JSON.stringify({ result }));
} catch (error) {
console.error(`Hook error: ${error.message}`);
process.exit(2); // Blocking error
}
```
### Test hooks independently
Run hook scripts manually with sample JSON input:
```bash
# Create test input
cat > test-input.json << 'EOF'
{
"session_id": "test-123",
"cwd": "/tmp/test",
"hook_event_name": "BeforeTool",
"tool_name": "write_file",
"tool_input": {
"file_path": "test.txt",
"content": "Test content"
}
}
EOF
# Test the hook
cat test-input.json | .gemini/hooks/my-hook.sh
# Check exit code
echo "Exit code: $?"
```
### Check exit codes
Ensure your script returns the correct exit code:
```bash
#!/usr/bin/env bash
set -e # Exit on error
# Hook logic
process_input() {
# ...
}
if process_input; then
echo "Success message"
exit 0
else
echo "Error message" >&2
exit 2
fi
```
### Enable telemetry
Hook execution is logged when `telemetry.logPrompts` is enabled:
```json
{
"telemetry": {
"logPrompts": true
}
}
```
View hook telemetry in logs to debug execution issues.
### Use hook panel
The `/hooks panel` command shows execution status and recent output:
```bash
/hooks panel
```
Check for:
- Hook execution counts
- Recent successes/failures
- Error messages
- Execution timing
## Development
### Start simple
Begin with basic logging hooks before implementing complex logic:
```bash
#!/usr/bin/env bash
# Simple logging hook to understand input structure
input=$(cat)
echo "$input" >> .gemini/hook-inputs.log
echo "Logged input"
```
### Use JSON libraries
Parse JSON with proper libraries instead of text processing:
**Bad:**
```bash
# Fragile text parsing
tool_name=$(echo "$input" | grep -oP '"tool_name":\s*"\K[^"]+')
```
**Good:**
```bash
# Robust JSON parsing
tool_name=$(echo "$input" | jq -r '.tool_name')
```
### Make scripts executable
Always make hook scripts executable:
```bash
chmod +x .gemini/hooks/*.sh
chmod +x .gemini/hooks/*.js
```
### Version control
Commit hooks to share with your team:
```bash
git add .gemini/hooks/
git add .gemini/settings.json
git commit -m "Add project hooks for security and testing"
```
**`.gitignore` considerations:**
```gitignore
# Ignore hook cache and logs
.gemini/hook-cache.json
.gemini/hook-debug.log
.gemini/memory/session-*.jsonl
# Keep hook scripts
!.gemini/hooks/*.sh
!.gemini/hooks/*.js
```
### Document behavior
Add descriptions to help others understand your hooks:
```json
{
"hooks": {
"BeforeTool": [
{
"matcher": "write_file|replace",
"hooks": [
{
"name": "secret-scanner",
"type": "command",
"command": "$GEMINI_PROJECT_DIR/.gemini/hooks/block-secrets.sh",
"description": "Scans code changes for API keys, passwords, and other secrets before writing"
}
]
}
]
}
}
```
Add comments in hook scripts:
```javascript
#!/usr/bin/env node
/**
* RAG Tool Filter Hook
*
* This hook reduces the tool space from 100+ tools to ~15 relevant ones
* by extracting keywords from the user's request and filtering tools
* based on semantic similarity.
*
* Performance: ~500ms average, cached tool embeddings
* Dependencies: @google/generative-ai
*/
```
## Troubleshooting
### Hook not executing
**Check hook name in `/hooks panel`:**
```bash
/hooks panel
```
Verify the hook appears in the list and is enabled.
**Verify matcher pattern:**
```bash
# Test regex pattern
echo "write_file|replace" | grep -E "write_.*|replace"
```
**Check disabled list:**
```json
{
"hooks": {
"disabled": ["my-hook-name"]
}
}
```
**Ensure script is executable:**
```bash
ls -la .gemini/hooks/my-hook.sh
chmod +x .gemini/hooks/my-hook.sh
```
**Verify script path:**
```bash
# Check path expansion
echo "$GEMINI_PROJECT_DIR/.gemini/hooks/my-hook.sh"
# Verify file exists
test -f "$GEMINI_PROJECT_DIR/.gemini/hooks/my-hook.sh" && echo "File exists"
```
### Hook timing out
**Check configured timeout:**
```json
{
"name": "slow-hook",
"timeout": 60000
}
```
**Optimize slow operations:**
```javascript
// Before: Sequential operations (slow)
for (const item of items) {
await processItem(item);
}
// After: Parallel operations (fast)
await Promise.all(items.map((item) => processItem(item)));
```
**Use caching:**
```javascript
const cache = new Map();
async function getCachedData(key) {
if (cache.has(key)) {
return cache.get(key);
}
const data = await fetchData(key);
cache.set(key, data);
return data;
}
```
**Consider splitting into multiple faster hooks:**
```json
{
"hooks": {
"BeforeTool": [
{
"matcher": "write_file",
"hooks": [
{
"name": "quick-check",
"command": "./quick-validation.sh",
"timeout": 1000
}
]
},
{
"matcher": "write_file",
"hooks": [
{
"name": "deep-check",
"command": "./deep-analysis.sh",
"timeout": 30000
}
]
}
]
}
}
```
### Invalid JSON output
**Validate JSON before outputting:**
```bash
#!/usr/bin/env bash
output='{"decision": "allow"}'
# Validate JSON
if echo "$output" | jq empty 2>/dev/null; then
echo "$output"
else
echo "Invalid JSON generated" >&2
exit 1
fi
```
**Ensure proper quoting and escaping:**
```javascript
// Bad: Unescaped string interpolation
const message = `User said: ${userInput}`;
console.log(JSON.stringify({ message }));
// Good: Automatic escaping
console.log(JSON.stringify({ message: `User said: ${userInput}` }));
```
**Check for binary data or control characters:**
```javascript
function sanitizeForJSON(str) {
return str.replace(/[\x00-\x1F\x7F-\x9F]/g, ''); // Remove control chars
}
const cleanContent = sanitizeForJSON(content);
console.log(JSON.stringify({ content: cleanContent }));
```
### Exit code issues
**Verify script returns correct codes:**
```bash
#!/usr/bin/env bash
set -e # Exit on error
# Processing logic
if validate_input; then
echo "Success"
exit 0
else
echo "Validation failed" >&2
exit 2
fi
```
**Check for unintended errors:**
```bash
#!/usr/bin/env bash
# Don't use 'set -e' if you want to handle errors explicitly
# set -e
if ! command_that_might_fail; then
# Handle error
echo "Command failed but continuing" >&2
fi
# Always exit explicitly
exit 0
```
**Use trap for cleanup:**
```bash
#!/usr/bin/env bash
cleanup() {
# Cleanup logic
rm -f /tmp/hook-temp-*
}
trap cleanup EXIT
# Hook logic here
```
### Environment variables not available
**Check if variable is set:**
```bash
#!/usr/bin/env bash
if [ -z "$GEMINI_PROJECT_DIR" ]; then
echo "GEMINI_PROJECT_DIR not set" >&2
exit 1
fi
if [ -z "$CUSTOM_VAR" ]; then
echo "Warning: CUSTOM_VAR not set, using default" >&2
CUSTOM_VAR="default-value"
fi
```
**Debug available variables:**
```bash
#!/usr/bin/env bash
# List all environment variables
env > .gemini/hook-env.log
# Check specific variables
echo "GEMINI_PROJECT_DIR: $GEMINI_PROJECT_DIR" >> .gemini/hook-env.log
echo "GEMINI_SESSION_ID: $GEMINI_SESSION_ID" >> .gemini/hook-env.log
echo "GEMINI_API_KEY: ${GEMINI_API_KEY:+<set>}" >> .gemini/hook-env.log
```
**Use .env files:**
```bash
#!/usr/bin/env bash
# Load .env file if it exists
if [ -f "$GEMINI_PROJECT_DIR/.env" ]; then
source "$GEMINI_PROJECT_DIR/.env"
fi
```
## Using Hooks Securely
### Threat Model
Understanding where hooks come from and what they can do is critical for secure
usage.
| Hook Source | Description |
| :---------------------------- | :------------------------------------------------------------------------------------------------------------------------- |
| **System** | Configured by system administrators (e.g., `/etc/gemini-cli/settings.json`, `/Library/...`). Assumed to be the **safest**. |
| **User** (`~/.gemini/...`) | Configured by you. You are responsible for ensuring they are safe. |
| **Extensions** | You explicitly approve and install these. Security depends on the extension source (integrity). |
| **Project** (`./.gemini/...`) | **Untrusted by default.** Safest in trusted internal repos; higher risk in third-party/public repos. |
#### Project Hook Security
When you open a project with hooks defined in `.gemini/settings.json`:
1. **Detection**: Gemini CLI detects the hooks.
2. **Identification**: A unique identity is generated for each hook based on its
`name` and `command`.
3. **Warning**: If this specific hook identity has not been seen before, a
**warning** is displayed.
4. **Execution**: The hook is executed (unless specific security settings block
it).
5. **Trust**: The hook is marked as "trusted" for this project.
> [!IMPORTANT] **Modification Detection**: If the `command` string of a project
> hook is changed (e.g., by a `git pull`), its identity changes. Gemini CLI will
> treat it as a **new, untrusted hook** and warn you again. This prevents
> malicious actors from silently swapping a verified command for a malicious
> one.
### Risks
| Risk | Description |
| :--------------------------- | :----------------------------------------------------------------------------------------------------------------------------------- |
| **Arbitrary Code Execution** | Hooks run as your user. They can do anything you can do (delete files, install software). |
| **Data Exfiltration** | A hook could read your input (prompts), output (code), or environment variables (`GEMINI_API_KEY`) and send them to a remote server. |
| **Prompt Injection** | Malicious content in a file or web page could trick an LLM into running a tool that triggers a hook in an unexpected way. |
### Mitigation Strategies
#### Verify the source
**Verify the source** of any project hooks or extensions before enabling them.
- For open-source projects, a quick review of the hook scripts is recommended.
- For extensions, ensure you trust the author or publisher (e.g., verified
publishers, well-known community members).
- Be cautious with obfuscated scripts or compiled binaries from unknown sources.
#### Sanitize Environment
Hooks inherit the environment of the Gemini CLI process, which may include
sensitive API keys. Gemini CLI attempts to sanitize sensitive variables, but you
should be cautious.
- **Avoid printing environment variables** to stdout/stderr unless necessary.
- **Use `.env` files** to securely manage sensitive variables, ensuring they are
excluded from version control.
**System Administrators:** You can enforce environment variable redaction by
default in the system configuration (e.g., `/etc/gemini-cli/settings.json`):
```json
{
"security": {
"environmentVariableRedaction": {
"enabled": true,
"blocked": ["MY_SECRET_KEY"],
"allowed": ["SAFE_VAR"]
}
}
}
```
## Authoring Secure Hooks
When writing your own hooks, follow these practices to ensure they are robust
and secure.
### Validate all inputs
Never trust data from hooks without validation. Hook inputs often come from the
LLM or user prompts, which can be manipulated.
```bash
#!/usr/bin/env bash
input=$(cat)
# Validate JSON structure
if ! echo "$input" | jq empty 2>/dev/null; then
echo "Invalid JSON input" >&2
exit 1
fi
# Validate tool_name explicitly
tool_name=$(echo "$input" | jq -r '.tool_name // empty')
if [[ "$tool_name" != "write_file" && "$tool_name" != "read_file" ]]; then
echo "Unexpected tool: $tool_name" >&2
exit 1
fi
```
### Use timeouts
Prevent denial-of-service (hanging agents) by enforcing timeouts. Gemini CLI
defaults to 60 seconds, but you should set stricter limits for fast hooks.
```json
{
"hooks": {
"BeforeTool": [
{
"matcher": "*",
"hooks": [
{
"name": "fast-validator",
"command": "./hooks/validate.sh",
"timeout": 5000 // 5 seconds
}
]
}
]
}
}
```
### Limit permissions
Run hooks with minimal required permissions:
```bash
#!/usr/bin/env bash
# Don't run as root
if [ "$EUID" -eq 0 ]; then
echo "Hook should not run as root" >&2
exit 1
fi
# Check file permissions before writing
if [ -w "$file_path" ]; then
# Safe to write
else
echo "Insufficient permissions" >&2
exit 1
fi
```
### Example: Secret Scanner
Use `BeforeTool` hooks to prevent committing sensitive data. This is a powerful
pattern for enhancing security in your workflow.
```javascript
const SECRET_PATTERNS = [
/api[_-]?key\s*[:=]\s*['"]?[a-zA-Z0-9_-]{20,}['"]?/i,
/password\s*[:=]\s*['"]?[^\s'"]{8,}['"]?/i,
/secret\s*[:=]\s*['"]?[a-zA-Z0-9_-]{20,}['"]?/i,
/AKIA[0-9A-Z]{16}/, // AWS access key
/ghp_[a-zA-Z0-9]{36}/, // GitHub personal access token
/sk-[a-zA-Z0-9]{48}/, // OpenAI API key
];
function containsSecret(content) {
return SECRET_PATTERNS.some((pattern) => pattern.test(content));
}
```
## Privacy considerations
Hook inputs and outputs may contain sensitive information. Gemini CLI respects
the `telemetry.logPrompts` setting for hook data logging.
### What data is collected
Hook telemetry may include:
- **Hook inputs:** User prompts, tool arguments, file contents
- **Hook outputs:** Hook responses, decision reasons, added context
- **Standard streams:** stdout and stderr from hook processes
- **Execution metadata:** Hook name, event type, duration, success/failure
### Privacy settings
**Enabled (default):**
Full hook I/O is logged to telemetry. Use this when:
- Developing and debugging hooks
- Telemetry is redirected to a trusted enterprise system
- You understand and accept the privacy implications
**Disabled:**
Only metadata is logged (event name, duration, success/failure). Hook inputs and
outputs are excluded. Use this when:
- Sending telemetry to third-party systems
- Working with sensitive data
- Privacy regulations require minimizing data collection
### Configuration
**Disable PII logging in settings:**
```json
{
"telemetry": {
"logPrompts": false
}
}
```
**Disable via environment variable:**
```bash
export GEMINI_TELEMETRY_LOG_PROMPTS=false
```
### Sensitive data in hooks
If your hooks process sensitive data:
1. **Minimize logging:** Don't write sensitive data to log files
2. **Sanitize outputs:** Remove sensitive data before outputting
3. **Use secure storage:** Encrypt sensitive data at rest
4. **Limit access:** Restrict hook script permissions
**Example sanitization:**
```javascript
function sanitizeOutput(data) {
const sanitized = { ...data };
// Remove sensitive fields
delete sanitized.apiKey;
delete sanitized.password;
// Redact sensitive strings
if (sanitized.content) {
sanitized.content = sanitized.content.replace(
/api[_-]?key\s*[:=]\s*['"]?[a-zA-Z0-9_-]{20,}['"]?/gi,
'[REDACTED]',
);
}
return sanitized;
}
console.log(JSON.stringify(sanitizeOutput(hookOutput)));
```
## Learn more
- [Hooks Reference](index.md) - Complete API reference
- [Writing Hooks](writing-hooks.md) - Tutorial and examples
- [Configuration](../cli/configuration.md) - Gemini CLI settings
- [Hooks Design Document](../hooks-design.md) - Technical architecture
+687
View File
@@ -0,0 +1,687 @@
# Gemini CLI hooks
Hooks are scripts or programs that Gemini CLI executes at specific points in the
agentic loop, allowing you to intercept and customize behavior without modifying
the CLI's source code.
See [writing hooks guide](writing-hooks.md) for a tutorial on creating your
first hook and a comprehensive example.
See [hooks reference](reference.md) for the technical specification of the I/O
schemas.
See [best practices](best-practices.md) for guidelines on security, performance,
and debugging.
## What are hooks?
With hooks, you can:
- **Add context:** Inject relevant information before the model processes a
request
- **Validate actions:** Review and block potentially dangerous operations
- **Enforce policies:** Implement security and compliance requirements
- **Log interactions:** Track tool usage and model responses
- **Optimize behavior:** Dynamically adjust tool selection or model parameters
Hooks run synchronously as part of the agent loop—when a hook event fires,
Gemini CLI waits for all matching hooks to complete before continuing.
## Security and Risks
> [!WARNING] **Hooks execute arbitrary code with your user privileges.**
By configuring hooks, you are explicitly allowing Gemini CLI to run shell
commands on your machine. Malicious or poorly configured hooks can:
- **Exfiltrate data**: Read sensitive files (`.env`, ssh keys) and send them to
remote servers.
- **Modify system**: Delete files, install malware, or change system settings.
- **Consume resources**: Run infinite loops or crash your system.
**Project-level hooks** (in `.gemini/settings.json`) and **Extension hooks** are
particularly risky when opening third-party projects or extensions from
untrusted authors. Gemini CLI will **warn you** the first time it detects a new
project hook (identified by its name and command), but it is **your
responsibility** to review these hooks (and any installed extensions) before
trusting them.
See [Security Considerations](best-practices.md#using-hooks-securely) for a
detailed threat model and mitigation strategies.
## Core concepts
### Hook events
Hooks are triggered by specific events in Gemini CLI's lifecycle. The following
table lists all available hook events:
| Event | When It Fires | Common Use Cases |
| --------------------- | --------------------------------------------- | ------------------------------------------ |
| `SessionStart` | When a session begins | Initialize resources, load context |
| `SessionEnd` | When a session ends | Clean up, save state |
| `BeforeAgent` | After user submits prompt, before planning | Add context, validate prompts |
| `AfterAgent` | When agent loop ends | Review output, force continuation |
| `BeforeModel` | Before sending request to LLM | Modify prompts, add instructions |
| `AfterModel` | After receiving LLM response | Filter responses, log interactions |
| `BeforeToolSelection` | Before LLM selects tools (after BeforeModel) | Filter available tools, optimize selection |
| `BeforeTool` | Before a tool executes | Validate arguments, block dangerous ops |
| `AfterTool` | After a tool executes | Process results, run tests |
| `PreCompress` | Before context compression | Save state, notify user |
| `Notification` | When a notification occurs (e.g., permission) | Auto-approve, log decisions |
### Hook types
Gemini CLI currently supports **command hooks** that run shell commands or
scripts:
```json
{
"type": "command",
"command": "$GEMINI_PROJECT_DIR/.gemini/hooks/my-hook.sh",
"timeout": 30000
}
```
**Note:** Plugin hooks (npm packages) are planned for a future release.
### Matchers
For tool-related events (`BeforeTool`, `AfterTool`), you can filter which tools
trigger the hook:
```json
{
"hooks": {
"BeforeTool": [
{
"matcher": "write_file|replace",
"hooks": [
/* hooks for write operations */
]
}
]
}
}
```
**Matcher patterns:**
- **Exact match:** `"read_file"` matches only `read_file`
- **Regex:** `"write_.*|replace"` matches `write_file`, `replace`
- **Wildcard:** `"*"` or `""` matches all tools
**Session event matchers:**
- **SessionStart:** `startup`, `resume`, `clear`
- **SessionEnd:** `exit`, `clear`, `logout`, `prompt_input_exit`
- **PreCompress:** `manual`, `auto`
- **Notification:** `ToolPermission`
## Hook input/output contract
### Command hook communication
Hooks communicate via:
- **Input:** JSON on stdin
- **Output:** Exit code + stdout/stderr
### Exit codes
- **0:** Success - stdout shown to user (or injected as context for some events)
- **2:** Blocking error - stderr shown to agent/user, operation may be blocked
- **Other:** Non-blocking warning - logged but execution continues
### Common input fields
Every hook receives these base fields:
```json
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.jsonl",
"cwd": "/path/to/project",
"hook_event_name": "BeforeTool",
"timestamp": "2025-12-01T10:30:00Z"
// ... event-specific fields
}
```
### Event-specific fields
#### BeforeTool
**Input:**
```json
{
"tool_name": "write_file",
"tool_input": {
"file_path": "/path/to/file.ts",
"content": "..."
}
}
```
**Output (JSON on stdout):**
```json
{
"decision": "allow|deny|ask|block",
"reason": "Explanation shown to agent",
"systemMessage": "Message shown to user"
}
```
Or simple exit codes:
- Exit 0 = allow (stdout shown to user)
- Exit 2 = deny (stderr shown to agent)
#### AfterTool
**Input:**
```json
{
"tool_name": "read_file",
"tool_input": { "file_path": "..." },
"tool_response": "file contents..."
}
```
**Output:**
```json
{
"decision": "allow|deny",
"hookSpecificOutput": {
"hookEventName": "AfterTool",
"additionalContext": "Extra context for agent"
}
}
```
#### BeforeAgent
**Input:**
```json
{
"prompt": "Fix the authentication bug"
}
```
**Output:**
```json
{
"decision": "allow|deny",
"hookSpecificOutput": {
"hookEventName": "BeforeAgent",
"additionalContext": "Recent project decisions: ..."
}
}
```
#### BeforeModel
**Input:**
```json
{
"llm_request": {
"model": "gemini-2.0-flash-exp",
"messages": [{ "role": "user", "content": "Hello" }],
"config": { "temperature": 0.7 },
"toolConfig": {
"functionCallingConfig": {
"mode": "AUTO",
"allowedFunctionNames": ["read_file", "write_file"]
}
}
}
}
```
**Output:**
```json
{
"decision": "allow",
"hookSpecificOutput": {
"hookEventName": "BeforeModel",
"llm_request": {
"messages": [
{ "role": "system", "content": "Additional instructions..." },
{ "role": "user", "content": "Hello" }
]
}
}
}
```
#### AfterModel
**Input:**
```json
{
"llm_request": {
"model": "gemini-2.0-flash-exp",
"messages": [
/* ... */
],
"config": {
/* ... */
},
"toolConfig": {
/* ... */
}
},
"llm_response": {
"text": "string",
"candidates": [
{
"content": {
"role": "model",
"parts": ["array of content parts"]
},
"finishReason": "STOP"
}
]
}
}
```
**Output:**
```json
{
"hookSpecificOutput": {
"hookEventName": "AfterModel",
"llm_response": {
"candidate": {
/* modified response */
}
}
}
}
```
#### BeforeToolSelection
**Input:**
```json
{
"llm_request": {
"model": "gemini-2.0-flash-exp",
"messages": [
/* ... */
],
"toolConfig": {
"functionCallingConfig": {
"mode": "AUTO",
"allowedFunctionNames": [
/* 100+ tools */
]
}
}
}
}
```
**Output:**
```json
{
"hookSpecificOutput": {
"hookEventName": "BeforeToolSelection",
"toolConfig": {
"functionCallingConfig": {
"mode": "ANY",
"allowedFunctionNames": ["read_file", "write_file", "replace"]
}
}
}
}
```
Or simple output (comma-separated tool names sets mode to ANY):
```bash
echo "read_file,write_file,replace"
```
#### SessionStart
**Input:**
```json
{
"source": "startup|resume|clear"
}
```
**Output:**
```json
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "Loaded 5 project memories"
}
}
```
#### SessionEnd
**Input:**
```json
{
"reason": "exit|clear|logout|prompt_input_exit|other"
}
```
No structured output expected (but stdout/stderr logged).
#### PreCompress
**Input:**
```json
{
"trigger": "manual|auto"
}
```
**Output:**
```json
{
"systemMessage": "Compression starting..."
}
```
#### Notification
**Input:**
```json
{
"notification_type": "ToolPermission",
"message": "string",
"details": {
/* notification details */
}
}
```
**Output:**
```json
{
"systemMessage": "Notification logged"
}
```
## Configuration
Hook definitions are configured in `settings.json` files using the `hooks`
object. Configuration can be specified at multiple levels with defined
precedence rules.
### Configuration layers
Hook configurations are applied in the following order of execution (lower
numbers run first):
1. **Project settings:** `.gemini/settings.json` in your project directory
(highest priority)
2. **User settings:** `~/.gemini/settings.json`
3. **System settings:** `/etc/gemini-cli/settings.json`
4. **Extensions:** Internal hooks defined by installed extensions (lowest
priority)
#### Deduplication and shadowing
If multiple hooks with the identical **name** and **command** are discovered
across different configuration layers, Gemini CLI deduplicates them. The hook
from the higher-priority layer (e.g., Project) will be kept, and others will be
ignored.
Within each level, hooks run in the order they are declared in the
configuration.
### Configuration schema
```json
{
"hooks": {
"EventName": [
{
"matcher": "pattern",
"hooks": [
{
"name": "hook-identifier",
"type": "command",
"command": "./path/to/script.sh",
"description": "What this hook does",
"timeout": 30000
}
]
}
]
}
}
```
**Configuration properties:**
- **`name`** (string, recommended): Unique identifier for the hook used in
`/hooks enable/disable` commands. If omitted, the `command` path is used as
the identifier.
- **`type`** (string, required): Hook type - currently only `"command"` is
supported
- **`command`** (string, required): Path to the script or command to execute
- **`description`** (string, optional): Human-readable description shown in
`/hooks panel`
- **`timeout`** (number, optional): Timeout in milliseconds (default: 60000)
- **`matcher`** (string, optional): Pattern to filter when hook runs (event
matchers only)
### Environment variables
Hooks have access to:
- `GEMINI_PROJECT_DIR`: Project root directory
- `GEMINI_SESSION_ID`: Current session ID
- `GEMINI_API_KEY`: Gemini API key (if configured)
- All other environment variables from the parent process
## Managing hooks
### View registered hooks
Use the `/hooks panel` command to view all registered hooks:
```bash
/hooks panel
```
This command displays:
- All active hooks organized by event
- Hook source (user, project, system)
- Hook type (command or plugin)
- Execution status and recent output
### Enable and disable hooks
You can temporarily enable or disable individual hooks using commands:
```bash
/hooks enable hook-name
/hooks disable hook-name
```
These commands allow you to control hook execution without editing configuration
files. The hook name should match the `name` field in your hook configuration.
Changes made via these commands are persisted to your global User settings
(`~/.gemini/settings.json`).
### Disabled hooks configuration
To permanently disable hooks, add them to the `hooks.disabled` array in your
`settings.json`:
```json
{
"hooks": {
"disabled": ["secret-scanner", "auto-test"]
}
}
```
**Note:** The `hooks.disabled` array uses a UNION merge strategy. Disabled hooks
from all configuration levels (user, project, system) are combined and
deduplicated, meaning a hook disabled at any level remains disabled.
## Migration from Claude Code
If you have hooks configured for Claude Code, you can migrate them:
```bash
gemini hooks migrate --from-claude
```
This command:
- Reads `.claude/settings.json`
- Converts event names (`PreToolUse``BeforeTool`, etc.)
- Translates tool names (`Bash``run_shell_command`, `replace``replace`)
- Updates matcher patterns
- Writes to `.gemini/settings.json`
### Event name mapping
| Claude Code | Gemini CLI |
| ------------------ | -------------- |
| `PreToolUse` | `BeforeTool` |
| `PostToolUse` | `AfterTool` |
| `UserPromptSubmit` | `BeforeAgent` |
| `Stop` | `AfterAgent` |
| `Notification` | `Notification` |
| `SessionStart` | `SessionStart` |
| `SessionEnd` | `SessionEnd` |
| `PreCompact` | `PreCompress` |
### Tool name mapping
| Claude Code | Gemini CLI |
| ----------- | --------------------- |
| `Bash` | `run_shell_command` |
| `Edit` | `replace` |
| `Read` | `read_file` |
| `Write` | `write_file` |
| `Glob` | `glob` |
| `Grep` | `search_file_content` |
| `LS` | `list_directory` |
## Tool and Event Matchers Reference
### Available tool names for matchers
The following built-in tools can be used in `BeforeTool` and `AfterTool` hook
matchers:
#### File operations
- `read_file` - Read a single file
- `read_many_files` - Read multiple files at once
- `write_file` - Create or overwrite a file
- `replace` - Edit file content with find/replace
#### File system
- `list_directory` - List directory contents
- `glob` - Find files matching a pattern
- `search_file_content` - Search within file contents
#### Execution
- `run_shell_command` - Execute shell commands
#### Web and external
- `google_web_search` - Google Search with grounding
- `web_fetch` - Fetch web page content
#### Agent features
- `write_todos` - Manage TODO items
- `save_memory` - Save information to memory
- `delegate_to_agent` - Delegate tasks to sub-agents
#### Example matchers
```json
{
"matcher": "write_file|replace" // File editing tools
}
```
```json
{
"matcher": "read_.*" // All read operations
}
```
```json
{
"matcher": "run_shell_command" // Only shell commands
}
```
```json
{
"matcher": "*" // All tools
}
```
### Event-specific matchers
#### SessionStart event matchers
- `startup` - Fresh session start
- `resume` - Resuming a previous session
- `clear` - Session cleared
#### SessionEnd event matchers
- `exit` - Normal exit
- `clear` - Session cleared
- `logout` - User logged out
- `prompt_input_exit` - Exit from prompt input
- `other` - Other reasons
#### PreCompress event matchers
- `manual` - Manually triggered compression
- `auto` - Automatically triggered compression
#### Notification event matchers
- `ToolPermission` - Tool permission notifications
## Learn more
- [Writing Hooks](writing-hooks.md) - Tutorial and comprehensive example
- [Best Practices](best-practices.md) - Security, performance, and debugging
- [Custom Commands](../cli/custom-commands.md) - Create reusable prompt
shortcuts
- [Configuration](../cli/configuration.md) - Gemini CLI configuration options
- [Hooks Design Document](../hooks-design.md) - Technical architecture details
+168
View File
@@ -0,0 +1,168 @@
# Hooks Reference
This document provides the technical specification for Gemini CLI hooks,
including the JSON schemas for input and output, exit code behaviors, and the
stable model API.
## Communication Protocol
Hooks communicate with Gemini CLI via standard streams and exit codes:
- **Input**: Gemini CLI sends a JSON object to the hook's `stdin`.
- **Output**: The hook sends a JSON object (or plain text) to `stdout`.
- **Exit Codes**: Used to signal success or blocking errors.
### Exit Code Behavior
| Exit Code | Meaning | Behavior |
| :-------- | :----------------- | :---------------------------------------------------------------------------------------------- |
| `0` | **Success** | `stdout` is parsed as JSON. If parsing fails, it's treated as a `systemMessage`. |
| `2` | **Blocking Error** | Interrupts the current operation. `stderr` is shown to the agent (for tool events) or the user. |
| Other | **Warning** | Execution continues. `stderr` is logged as a non-blocking warning. |
---
## Input Schema (`stdin`)
Every hook receives a base JSON object. Extra fields are added depending on the
specific event.
### Base Fields (All Events)
| Field | Type | Description |
| :---------------- | :------- | :---------------------------------------------------- |
| `session_id` | `string` | Unique identifier for the current CLI session. |
| `transcript_path` | `string` | Path to the session's JSON transcript (if available). |
| `cwd` | `string` | The current working directory. |
| `hook_event_name` | `string` | The name of the firing event (e.g., `BeforeTool`). |
| `timestamp` | `string` | ISO 8601 timestamp of the event. |
### Event-Specific Fields
#### Tool Events (`BeforeTool`, `AfterTool`)
- `tool_name`: (`string`) The internal name of the tool (e.g., `write_file`,
`run_shell_command`).
- `tool_input`: (`object`) The arguments passed to the tool.
- `tool_response`: (`object`, **AfterTool only**) The raw output from the tool
execution.
#### Agent Events (`BeforeAgent`, `AfterAgent`)
- `prompt`: (`string`) The user's submitted prompt.
- `prompt_response`: (`string`, **AfterAgent only**) The final response text
from the model.
- `stop_hook_active`: (`boolean`, **AfterAgent only**) Indicates if a stop hook
is already handling a continuation.
#### Model Events (`BeforeModel`, `AfterModel`, `BeforeToolSelection`)
- `llm_request`: (`LLMRequest`) A stable representation of the outgoing request.
See [Stable Model API](#stable-model-api).
- `llm_response`: (`LLMResponse`, **AfterModel only**) A stable representation
of the incoming response.
#### Session & Notification Events
- `source`: (`startup` | `resume` | `clear`, **SessionStart only**) The trigger
source.
- `reason`: (`exit` | `clear` | `logout` | `prompt_input_exit` | `other`,
**SessionEnd only**) The reason for session end.
- `trigger`: (`manual` | `auto`, **PreCompress only**) What triggered the
compression event.
- `notification_type`: (`ToolPermission`, **Notification only**) The type of
notification being fired.
- `message`: (`string`, **Notification only**) The notification message.
- `details`: (`object`, **Notification only**) Payload-specific details for the
notification.
---
## Output Schema (`stdout`)
If the hook exits with `0`, the CLI attempts to parse `stdout` as JSON.
### Common Output Fields
| Field | Type | Description |
| :------------------- | :-------- | :----------------------------------------------------------------------- |
| `decision` | `string` | One of: `allow`, `deny`, `block`, `ask`, `approve`. |
| `reason` | `string` | Explanation shown to the **agent** when a decision is `deny` or `block`. |
| `systemMessage` | `string` | Message displayed to the **user** in the CLI terminal. |
| `continue` | `boolean` | If `false`, immediately terminates the agent loop for this turn. |
| `stopReason` | `string` | Message shown to the user when `continue` is `false`. |
| `suppressOutput` | `boolean` | If `true`, the hook execution is hidden from the CLI transcript. |
| `hookSpecificOutput` | `object` | Container for event-specific data (see below). |
### `hookSpecificOutput` Reference
| Field | Supported Events | Description |
| :------------------ | :----------------------------------------- | :-------------------------------------------------------------------------------- |
| `additionalContext` | `SessionStart`, `BeforeAgent`, `AfterTool` | Appends text directly to the agent's context. |
| `llm_request` | `BeforeModel` | A `Partial<LLMRequest>` to override parameters of the outgoing call. |
| `llm_response` | `BeforeModel` | A **full** `LLMResponse` to bypass the model and provide a synthetic result. |
| `llm_response` | `AfterModel` | A `Partial<LLMResponse>` to modify the model's response before the agent sees it. |
| `toolConfig` | `BeforeToolSelection` | Object containing `mode` (`AUTO`/`ANY`/`NONE`) and `allowedFunctionNames`. |
---
## Stable Model API
Gemini CLI uses a decoupled format for model interactions to ensure hooks remain
stable even if the underlying Gemini SDK changes.
### `LLMRequest` Object
Used in `BeforeModel` and `BeforeToolSelection`.
> 💡 **Note**: In v1, model hooks are primarily text-focused. Non-text parts
> (like images or function calls) provided in the `content` array will be
> simplified to their string representation by the translator.
```typescript
{
"model": string,
"messages": Array<{
"role": "user" | "model" | "system",
"content": string | Array<{ "type": string, [key: string]: any }>
}>,
"config"?: {
"temperature"?: number,
"maxOutputTokens"?: number,
"topP"?: number,
"topK"?: number
},
"toolConfig"?: {
"mode"?: "AUTO" | "ANY" | "NONE",
"allowedFunctionNames"?: string[]
}
}
```
### `LLMResponse` Object
Used in `AfterModel` and as a synthetic response in `BeforeModel`.
```typescript
{
"text"?: string,
"candidates": Array<{
"content": {
"role": "model",
"parts": string[]
},
"finishReason"?: "STOP" | "MAX_TOKENS" | "SAFETY" | "RECITATION" | "OTHER",
"index"?: number,
"safetyRatings"?: Array<{
"category": string,
"probability": string,
"blocked"?: boolean
}>
}>,
"usageMetadata"?: {
"promptTokenCount"?: number,
"candidatesTokenCount"?: number,
"totalTokenCount"?: number
}
}
```
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -128,8 +128,8 @@ editor.
You can also **modify the suggested changes** directly in the diff view before
accepting them.
If you select Yes, allow always in the CLI, changes will no longer show up in
the IDE as they will be auto-accepted.
If you select Allow for this session in the CLI, changes will no longer show
up in the IDE as they will be auto-accepted.
## Using with sandboxing
+12
View File
@@ -73,6 +73,9 @@ 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.
- **[System Prompt Override](./cli/system-prompt.md):** Replace built-in system
instructions using `GEMINI_SYSTEM_MD`.
- **[Policy Engine](./core/policy-engine.md):** Use the Policy Engine for
fine-grained control over tool execution.
@@ -102,6 +105,15 @@ This documentation is organized into the following sections:
- **[Extension releasing](./extensions/extension-releasing.md):** How to release
Gemini CLI extensions.
### Hooks
- **[Hooks](./hooks/index.md):** Intercept and customize Gemini CLI behavior at
key lifecycle points.
- **[Writing Hooks](./hooks/writing-hooks.md):** Learn how to create your first
hook with a comprehensive example.
- **[Best Practices](./hooks/best-practices.md):** Security, performance, and
debugging guidelines for hooks.
### IDE integration
- **[Introduction to IDE integration](./ide-integration/index.md):** Connect the
+3 -3
View File
@@ -199,9 +199,9 @@ file, or case.
## Continuous integration
To ensure the integration tests are always run, a GitHub Actions workflow is
defined in `.github/workflows/e2e.yml`. This workflow automatically runs the
integrations tests for pull requests against the `main` branch, or when a pull
request is added to a merge queue.
defined in `.github/workflows/chained_e2e.yml`. This workflow automatically runs
the integrations tests for pull requests against the `main` branch, or when a
pull request is added to a merge queue.
The workflow runs the tests in different sandboxing environments to ensure
Gemini CLI is tested across each:
+3
View File
@@ -14,6 +14,9 @@ feature), while the PR is the "how" (the implementation). This separation helps
us track work, prioritize features, and maintain clear historical context. Our
automation is built around this principle.
> **Note:** Issues tagged as "🔒Maintainers only" are reserved for project
> maintainers. We will not accept pull requests related to these issues.
---
## Detailed automation workflows
+1 -1
View File
@@ -33,7 +33,7 @@ nightly) or the release branch (for preview/stable).
### 2. End-to-end (E2E) tests
All workflows in `.github/workflows/e2e.yml` must pass.
All workflows in `.github/workflows/chained_e2e.yml` must pass.
- **Platforms:** **Linux, macOS and Windows**.
- **Sandboxing:** Tests must pass with both `sandbox:none` and `sandbox:docker`
+51 -5
View File
@@ -24,7 +24,7 @@
"slug": "docs/get-started"
},
{
"label": "Gemini 3 Pro on Gemini CLI",
"label": "Gemini 3 on Gemini CLI",
"slug": "docs/get-started/gemini-3"
},
{
@@ -88,6 +88,10 @@
"label": "Session Management",
"slug": "docs/cli/session-management"
},
{
"label": "Agent Skills",
"slug": "docs/cli/skills"
},
{
"label": "Settings",
"slug": "docs/cli/settings"
@@ -115,6 +119,10 @@
{
"label": "Uninstall",
"slug": "docs/cli/uninstall"
},
{
"label": "System prompt override",
"slug": "docs/cli/system-prompt"
}
]
},
@@ -193,6 +201,27 @@
}
]
},
{
"label": "Hooks",
"items": [
{
"label": "Introduction",
"slug": "docs/hooks"
},
{
"label": "Writing hooks",
"slug": "docs/hooks/writing-hooks"
},
{
"label": "Hooks reference",
"slug": "docs/hooks/reference"
},
{
"label": "Best practices",
"slug": "docs/hooks/best-practices"
}
]
},
{
"label": "IDE integration",
"items": [
@@ -217,10 +246,6 @@
"label": "Releases",
"slug": "docs/releases"
},
{
"label": "Changelog",
"slug": "docs/changelogs"
},
{
"label": "Integration tests",
"slug": "docs/integration-tests"
@@ -231,6 +256,27 @@
}
]
},
{
"label": "Releases",
"items": [
{
"label": "Release notes",
"slug": "docs/changelogs/"
},
{
"label": "Latest release",
"slug": "docs/changelogs/latest"
},
{
"label": "Preview release",
"slug": "docs/changelogs/preview"
},
{
"label": "Changelog",
"slug": "docs/changelogs/releases"
}
]
},
{
"label": "Support",
"items": [
+7 -6
View File
@@ -36,8 +36,9 @@ glob patterns.
## 2. `read_file` (ReadFile)
`read_file` reads and returns the content of a specified file. This tool handles
text, images (PNG, JPG, GIF, WEBP, SVG, BMP), and PDF files. For text files, it
can read specific line ranges. Other binary file types are generally skipped.
text, images (PNG, JPG, GIF, WEBP, SVG, BMP), audio files (MP3, WAV, AIFF, AAC,
OGG, FLAC), and PDF files. For text files, it can read specific line ranges.
Other binary file types are generally skipped.
- **Tool name:** `read_file`
- **Display name:** ReadFile
@@ -53,16 +54,16 @@ can read specific line ranges. Other binary file types are generally skipped.
- For text files: Returns the content. If `offset` and `limit` are used,
returns only that slice of lines. Indicates if content was truncated due to
line limits or line length limits.
- For image and PDF files: Returns the file content as a base64-encoded data
structure suitable for model consumption.
- For image, audio, and PDF files: Returns the file content as a
base64-encoded data structure suitable for model consumption.
- For other binary files: Attempts to identify and skip them, returning a
message indicating it's a generic binary file.
- **Output:** (`llmContent`):
- For text files: The file content, potentially prefixed with a truncation
message (e.g.,
`[File content truncated: showing lines 1-100 of 500 total lines...]\nActual file content...`).
- For image/PDF files: An object containing `inlineData` with `mimeType` and
base64 `data` (e.g.,
- For image/audio/PDF files: An object containing `inlineData` with `mimeType`
and base64 `data` (e.g.,
`{ inlineData: { mimeType: 'image/png', data: 'base64encodedstring' } }`).
- For other binary files: A message like
`Cannot display content of binary file: /path/to/data.bin`.
+29 -2
View File
@@ -16,8 +16,8 @@ An MCP server enables the Gemini CLI to:
through standardized schema definitions.
- **Execute tools:** Call specific tools with defined arguments and receive
structured responses.
- **Access resources:** Read data from specific resources (though the Gemini CLI
primarily focuses on tool execution).
- **Access resources:** Read data from specific resources that the server
exposes (files, API payloads, reports, etc.).
With an MCP server, you can extend the Gemini CLI's capabilities to perform
actions beyond its built-in features, such as interacting with databases, APIs,
@@ -40,6 +40,7 @@ The discovery process is orchestrated by `discoverMcpTools()`, which:
4. **Sanitizes and validates** tool schemas for compatibility with the Gemini
API
5. **Registers tools** in the global tool registry with conflict resolution
6. **Fetches and registers resources** if the server exposes any
### Execution layer (`mcp-tool.ts`)
@@ -59,6 +60,32 @@ The Gemini CLI supports three MCP transport types:
- **SSE Transport:** Connects to Server-Sent Events endpoints
- **Streamable HTTP Transport:** Uses HTTP streaming for communication
## Working with MCP resources
Some MCP servers expose contextual “resources” in addition to the tools and
prompts. Gemini CLI discovers these automatically and gives you the possibility
to reference them in the chat.
### Discovery and listing
- When discovery runs, the CLI fetches each servers `resources/list` results.
- The `/mcp` command displays a Resources section alongside Tools and Prompts
for every connected server.
This returns a concise, plain-text list of URIs plus metadata.
### Referencing resources in a conversation
You can use the same `@` syntax already known for referencing local files:
```
@server://resource/path
```
Resource URIs appear in the completion menu together with filesystem paths. When
you submit the message, the CLI calls `resources/read` and injects the content
in the conversation.
## How to set up your MCP server
The Gemini CLI uses the `mcpServers` configuration in your `settings.json` file
+1 -1
View File
@@ -26,7 +26,7 @@ plan.
- **Progress tracking:** The agent updates this list as it works, marking tasks
as `completed` when done.
- **Single socus:** Only one task will be marked `in_progress` at a time,
- **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
+4 -2
View File
@@ -124,8 +124,7 @@ This is especially useful for scripting and automation.
## Debugging tips
- **CLI debugging:**
- Use the `--verbose` flag (if available) with CLI commands for more detailed
output.
- Use the `--debug` flag for more detailed output.
- Check the CLI logs, often found in a user-specific configuration or cache
directory.
@@ -154,3 +153,6 @@ guide_, consider searching the Gemini CLI
[Issue tracker on GitHub](https://github.com/google-gemini/gemini-cli/issues).
If you can't find an issue similar to yours, consider creating a new GitHub
Issue with a detailed description. Pull requests are also welcome!
> **Note:** Issues tagged as "🔒Maintainers only" are reserved for project
> maintainers. We will not accept pull requests related to these issues.
+21 -10
View File
@@ -12,7 +12,7 @@ import prettierConfig from 'eslint-config-prettier';
import importPlugin from 'eslint-plugin-import';
import vitest from '@vitest/eslint-plugin';
import globals from 'globals';
import licenseHeader from 'eslint-plugin-license-header';
import headers from 'eslint-plugin-headers';
import path from 'node:path';
import url from 'node:url';
@@ -164,7 +164,11 @@ export default tseslint.config(
'prefer-arrow-callback': 'error',
'prefer-const': ['error', { destructuring: 'all' }],
radix: 'error',
'no-console': 'error',
'default-case': 'error',
'@typescript-eslint/await-thenable': ['error'],
'@typescript-eslint/no-floating-promises': ['error'],
'@typescript-eslint/no-unnecessary-type-assertion': ['error'],
},
},
{
@@ -206,19 +210,26 @@ export default tseslint.config(
{
files: ['./**/*.{tsx,ts,js}'],
plugins: {
'license-header': licenseHeader,
headers,
import: importPlugin,
},
rules: {
'license-header/header': [
'headers/header-format': [
'error',
[
'/**',
' * @license',
' * Copyright 2025 Google LLC',
' * SPDX-License-Identifier: Apache-2.0',
' */',
],
{
source: 'string',
content: [
'@license',
'Copyright (year) Google LLC',
'SPDX-License-Identifier: Apache-2.0',
].join('\n'),
patterns: {
year: {
pattern: '202[5-6]',
defaultValue: '2026',
},
},
},
],
'import/enforce-node-protocol-usage': ['error', 'always'],
},
-6
View File
@@ -1,6 +0,0 @@
prompt = """
Please summarize the findings for the pattern `{{args}}`.
Search Results:
!{grep -r {{args}} .}
"""
-4
View File
@@ -1,4 +0,0 @@
{
"name": "custom-commands",
"version": "1.0.0"
}
@@ -32,7 +32,7 @@ describe('Interactive Mode', () => {
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');
await run.type('\r');
// Wait for the specific end marker.
await run.expectText('THE_END.', 30000);
+9 -2
View File
@@ -4,13 +4,20 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect } from 'vitest';
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import * as os from 'node:os';
import { TestRig } from './test-helper.js';
describe('Ctrl+C exit', () => {
let rig: TestRig;
beforeEach(() => {
rig = new TestRig();
});
afterEach(async () => await rig.cleanup());
it('should exit gracefully on second Ctrl+C', async () => {
const rig = new TestRig();
await rig.setup('should exit gracefully on second Ctrl+C', {
settings: { tools: { useRipgrep: false } },
});
+9 -3
View File
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, expect, it } from 'vitest';
import { describe, expect, it, beforeEach, afterEach } from 'vitest';
import { TestRig } from './test-helper.js';
import { writeFileSync } from 'node:fs';
import { join } from 'node:path';
@@ -20,8 +20,15 @@ const extensionUpdate = `{
}`;
describe('extension install', () => {
let rig: TestRig;
beforeEach(() => {
rig = new TestRig();
});
afterEach(async () => await rig.cleanup());
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);
@@ -47,7 +54,6 @@ describe('extension install', () => {
'uninstall',
'test-extension-install',
]);
await rig.cleanup();
}
});
});
+32 -12
View File
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { expect, it, describe } from 'vitest';
import { expect, it, describe, beforeEach, afterEach } from 'vitest';
import { TestRig } from './test-helper.js';
import { TestMcpServer } from './test-mcp-server.js';
import { writeFileSync } from 'node:fs';
@@ -18,10 +18,23 @@ import stripAnsi from 'strip-ansi';
const itIf = (condition: boolean) => (condition ? it : it.skip);
describe('extension reloading', () => {
const sandboxEnv = env['GEMINI_SANDBOX'];
let rig: TestRig;
beforeEach(() => {
rig = new TestRig();
});
afterEach(async () => await rig.cleanup());
const sandboxEnv = env['GEMINI_SANDBOX'];
// Fails in linux non-sandbox e2e tests
// TODO(#14527): Re-enable this once fixed
// Fails in sandbox mode, can't check for local extension updates.
itIf((!sandboxEnv || sandboxEnv === 'false') && platform() !== 'win32')(
itIf(
(!sandboxEnv || sandboxEnv === 'false') &&
platform() !== 'win32' &&
platform() !== 'linux',
)(
'installs a local extension, updates it, checks it was reloaded properly',
async () => {
const serverA = new TestMcpServer();
@@ -38,7 +51,6 @@ describe('extension reloading', () => {
},
};
const rig = new TestRig();
rig.setup('extension reload test', {
settings: {
experimental: { extensionReloading: true },
@@ -70,7 +82,7 @@ describe('extension reloading', () => {
writeFileSync(testServerPath, safeJsonStringify(extension, 2));
// Start the CLI.
const run = await rig.runInteractive('--debug');
const run = await rig.runInteractive({ args: '--debug' });
await run.expectText('You have 1 extension with an update available');
// See the outdated extension
await run.sendText('/extensions list');
@@ -83,7 +95,10 @@ describe('extension reloading', () => {
// Poll for the updated list
await rig.pollCommand(
() => run.sendKeys('\u0015/mcp list\r'),
async () => {
await run.sendText('/mcp list');
await run.type('\r');
},
() => {
const output = stripAnsi(run.output);
return (
@@ -98,9 +113,9 @@ describe('extension reloading', () => {
// Update the extension, expect the list to update, and mcp servers as well.
await run.sendKeys('\u0015/extensions update test-extension');
await run.expectText('/extensions update test-extension');
await run.sendKeys('\r');
await run.type('\r');
await new Promise((resolve) => setTimeout(resolve, 500));
await run.sendKeys('\r');
await run.type('\r');
await run.expectText(
` * test-server (remote): http://localhost:${portB}/mcp`,
);
@@ -111,7 +126,10 @@ describe('extension reloading', () => {
// Poll for the updated extension version
await rig.pollCommand(
() => run.sendKeys('\u0015/extensions list\r'),
async () => {
await run.sendText('/extensions list');
await run.type('\r');
},
() =>
stripAnsi(run.output).includes(
'test-extension (v0.0.2) - active (updated)',
@@ -121,7 +139,10 @@ describe('extension reloading', () => {
// Poll for the updated mcp tool
await rig.pollCommand(
() => run.sendKeys('\u0015/mcp list\r'),
async () => {
await run.sendText('/mcp list');
await run.type('\r');
},
() => {
const output = stripAnsi(run.output);
return (
@@ -134,13 +155,12 @@ describe('extension reloading', () => {
);
await run.sendText('/quit');
await run.sendKeys('\r');
await run.type('\r');
// Clean things up.
await serverA.stop();
await serverB.stop();
await rig.runCommand(['extensions', 'uninstall', 'test-extension']);
await rig.cleanup();
},
);
});
@@ -37,7 +37,7 @@ describe('Interactive file system', () => {
// Step 1: Read the file
const readPrompt = `Read the version from ${fileName}`;
await run.type(readPrompt);
await run.sendKeys('\r');
await run.type('\r');
const readCall = await rig.waitForToolCall('read_file', 30000);
expect(readCall, 'Expected to find a read_file tool call').toBe(true);
@@ -45,7 +45,7 @@ describe('Interactive file system', () => {
// Step 2: Write the file
const writePrompt = `now change the version to 1.0.1 in the file`;
await run.type(writePrompt);
await run.sendKeys('\r');
await run.type('\r');
// Check tool calls made with right args
await rig.expectToolCallSuccess(
+38 -23
View File
@@ -4,20 +4,29 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect } from 'vitest';
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { existsSync } from 'node:fs';
import * as path from 'node:path';
import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
describe('file-system', () => {
let rig: TestRig;
beforeEach(() => {
rig = new TestRig();
});
afterEach(async () => await rig.cleanup());
it('should be able to read a file', async () => {
const rig = new TestRig();
await rig.setup('should be able to read a file');
await rig.setup('should be able to read a file', {
settings: { tools: { core: ['read_file'] } },
});
rig.createFile('test.txt', 'hello world');
const result = await rig.run(
`read the file test.txt and show me its contents`,
);
const result = await rig.run({
args: `read the file test.txt and show me its contents`,
});
const foundToolCall = await rig.waitForToolCall('read_file');
@@ -39,11 +48,14 @@ describe('file-system', () => {
});
it('should be able to write a file', async () => {
const rig = new TestRig();
await rig.setup('should be able to write a file');
await rig.setup('should be able to write a file', {
settings: { tools: { core: ['write_file', 'replace', 'read_file'] } },
});
rig.createFile('test.txt', '');
const result = await rig.run(`edit test.txt to have a hello world message`);
const result = await rig.run({
args: `edit test.txt to have a hello world message`,
});
// Accept multiple valid tools for editing files
const foundToolCall = await rig.waitForAnyToolCall([
@@ -94,13 +106,14 @@ describe('file-system', () => {
});
it('should correctly handle file paths with spaces', async () => {
const rig = new TestRig();
await rig.setup('should correctly handle file paths with spaces');
await rig.setup('should correctly handle file paths with spaces', {
settings: { tools: { core: ['write_file', 'read_file'] } },
});
const fileName = 'my test file.txt';
const result = await rig.run(
`write "hello" to "${fileName}" and then stop. Do not perform any other actions.`,
);
const result = await rig.run({
args: `write "hello" to "${fileName}" and then stop. Do not perform any other actions.`,
});
const foundToolCall = await rig.waitForToolCall('write_file');
if (!foundToolCall) {
@@ -116,13 +129,14 @@ describe('file-system', () => {
});
it('should perform a read-then-write sequence', async () => {
const rig = new TestRig();
await rig.setup('should perform a read-then-write sequence');
await rig.setup('should perform a read-then-write sequence', {
settings: { tools: { core: ['read_file', 'replace', 'write_file'] } },
});
const fileName = 'version.txt';
rig.createFile(fileName, '1.0.0');
const prompt = `Read the version from ${fileName} and write the next version 1.0.1 back to the file.`;
const result = await rig.run(prompt);
const result = await rig.run({ args: prompt });
await rig.waitForTelemetryReady();
const toolLogs = rig.readToolLogs();
@@ -151,16 +165,15 @@ describe('file-system', () => {
});
it.skip('should replace multiple instances of a string', async () => {
const rig = new TestRig();
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(
`rewrite the file ${fileName} to replace all instances of "test line" with "new line"`,
);
const result = await rig.run({
args: `rewrite the file ${fileName} to replace all instances of "test line" with "new line"`,
});
const validTools = ['write_file', 'edit'];
const foundToolCall = await rig.waitForAnyToolCall(validTools);
@@ -203,13 +216,15 @@ describe('file-system', () => {
});
it('should fail safely when trying to edit a non-existent file', async () => {
const rig = new TestRig();
await rig.setup(
'should fail safely when trying to edit a non-existent file',
{ settings: { tools: { core: ['read_file', 'replace'] } } },
);
const fileName = 'non_existent.txt';
const result = await rig.run(`In ${fileName}, replace "a" with "b"`);
const result = await rig.run({
args: `In ${fileName}, replace "a" with "b"`,
});
await rig.waitForTelemetryReady();
const toolLogs = rig.readToolLogs();
+20 -17
View File
@@ -4,36 +4,39 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect } from 'vitest';
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { TestRig } from './test-helper.js';
import { join } from 'node:path';
describe('Flicker Detector', () => {
let rig: TestRig;
beforeEach(() => {
rig = new TestRig();
});
afterEach(async () => await rig.cleanup());
it('should not detect a flicker under the max height budget', async () => {
const rig = new TestRig();
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();
} finally {
await rig.cleanup();
}
// We expect NO flicker event to be found.
const flickerMetric = rig.readMetric('ui.flicker.count');
expect(flickerMetric).toBeNull();
});
});
+14 -5
View File
@@ -5,17 +5,26 @@
*/
import { WEB_SEARCH_TOOL_NAME } from '../packages/core/src/tools/tool-names.js';
import { describe, it, expect } from 'vitest';
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
describe(WEB_SEARCH_TOOL_NAME, () => {
describe('web search tool', () => {
let rig: TestRig;
beforeEach(() => {
rig = new TestRig();
});
afterEach(async () => await rig.cleanup());
it('should be able to search the web', async () => {
const rig = new TestRig();
await rig.setup('should be able to search the web');
await rig.setup('should be able to search the web', {
settings: { tools: { core: [WEB_SEARCH_TOOL_NAME] } },
});
let result;
try {
result = await rig.run(`what is the weather in London`);
result = await rig.run({ args: `what is the weather in London` });
} catch (error) {
// Network errors can occur in CI environments
if (
@@ -0,0 +1,2 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"functionCall":{"name":"list_dir","args":{"path":"."}}}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":10,"candidatesTokenCount":10,"totalTokenCount":20}}]}
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Final Answer"}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":10,"candidatesTokenCount":10,"totalTokenCount":20}}]}
@@ -0,0 +1 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"**Responding**\n\nI will respond to the user's request.\n\n","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120,"promptTokensDetails":[{"modality":"TEXT","tokenCount":100}],"thoughtsTokenCount":20}},{"candidates":[{"content":{"parts":[{"text":"Response to: "}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"candidatesTokenCount":5,"totalTokenCount":125,"promptTokensDetails":[{"modality":"TEXT","tokenCount":100}],"thoughtsTokenCount":20}},{"candidates":[{"content":{"parts":[{"text":"Hello World"}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":100,"candidatesTokenCount":7,"totalTokenCount":127,"promptTokensDetails":[{"modality":"TEXT","tokenCount":100}],"thoughtsTokenCount":20}}]}
+214
View File
@@ -0,0 +1,214 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { TestRig } from './test-helper.js';
import { join } from 'node:path';
import { writeFileSync } from 'node:fs';
describe('Hooks Agent Flow', () => {
let rig: TestRig;
beforeEach(() => {
rig = new TestRig();
});
afterEach(async () => {
if (rig) {
await rig.cleanup();
}
});
describe('BeforeAgent Hooks', () => {
it('should inject additional context via BeforeAgent hook', async () => {
await rig.setup('should inject additional context via BeforeAgent hook', {
fakeResponsesPath: join(
import.meta.dirname,
'hooks-agent-flow.responses',
),
});
const hookScript = `
try {
const output = {
decision: "allow",
hookSpecificOutput: {
hookEventName: "BeforeAgent",
additionalContext: "SYSTEM INSTRUCTION: This is injected context."
}
};
process.stdout.write(JSON.stringify(output));
} catch (e) {
console.error('Failed to write stdout:', e);
process.exit(1);
}
console.error('DEBUG: BeforeAgent hook executed');
`;
const scriptPath = join(rig.testDir!, 'before_agent_context.cjs');
writeFileSync(scriptPath, hookScript);
await rig.setup('should inject additional context via BeforeAgent hook', {
settings: {
hooks: {
enabled: true,
BeforeAgent: [
{
hooks: [
{
type: 'command',
command: `node "${scriptPath}"`,
timeout: 5000,
},
],
},
],
},
},
});
await rig.run({ args: 'Hello test' });
// Verify hook execution and telemetry
const hookTelemetryFound = await rig.waitForTelemetryEvent('hook_call');
expect(hookTelemetryFound).toBeTruthy();
const hookLogs = rig.readHookLogs();
const beforeAgentLog = hookLogs.find(
(log) => log.hookCall.hook_event_name === 'BeforeAgent',
);
expect(beforeAgentLog).toBeDefined();
expect(beforeAgentLog?.hookCall.stdout).toContain('injected context');
expect(beforeAgentLog?.hookCall.stdout).toContain('"decision":"allow"');
expect(beforeAgentLog?.hookCall.stdout).toContain(
'SYSTEM INSTRUCTION: This is injected context.',
);
});
});
describe('AfterAgent Hooks', () => {
it('should receive prompt and response in AfterAgent hook', async () => {
await rig.setup('should receive prompt and response in AfterAgent hook', {
fakeResponsesPath: join(
import.meta.dirname,
'hooks-agent-flow.responses',
),
});
const hookScript = `
const fs = require('fs');
try {
const input = fs.readFileSync(0, 'utf-8');
console.error('DEBUG: AfterAgent hook input received');
process.stdout.write("Received Input: " + input);
} catch (err) {
console.error('Hook Failed:', err);
process.exit(1);
}
`;
const scriptPath = join(rig.testDir!, 'after_agent_verify.cjs');
writeFileSync(scriptPath, hookScript);
await rig.setup('should receive prompt and response in AfterAgent hook', {
settings: {
hooks: {
enabled: true,
AfterAgent: [
{
hooks: [
{
type: 'command',
command: `node "${scriptPath}"`,
timeout: 5000,
},
],
},
],
},
},
});
await rig.run({ args: 'Hello validation' });
const hookTelemetryFound = await rig.waitForTelemetryEvent('hook_call');
expect(hookTelemetryFound).toBeTruthy();
const hookLogs = rig.readHookLogs();
const afterAgentLog = hookLogs.find(
(log) => log.hookCall.hook_event_name === 'AfterAgent',
);
expect(afterAgentLog).toBeDefined();
// Verify the hook stdout contains the input we echoed which proves the
// hook received the prompt and response
expect(afterAgentLog?.hookCall.stdout).toContain('Received Input');
expect(afterAgentLog?.hookCall.stdout).toContain('Hello validation');
// The fake response contains "Hello World"
expect(afterAgentLog?.hookCall.stdout).toContain('Hello World');
});
});
describe('Multi-step Loops', () => {
it('should fire BeforeAgent and AfterAgent exactly once per turn despite tool calls', async () => {
await rig.setup(
'should fire BeforeAgent and AfterAgent exactly once per turn despite tool calls',
{
fakeResponsesPath: join(
import.meta.dirname,
'hooks-agent-flow-multistep.responses',
),
settings: {
hooks: {
enabled: true,
BeforeAgent: [
{
hooks: [
{
type: 'command',
command: `node -e "console.log('BeforeAgent Fired')"`,
timeout: 5000,
},
],
},
],
AfterAgent: [
{
hooks: [
{
type: 'command',
command: `node -e "console.log('AfterAgent Fired')"`,
timeout: 5000,
},
],
},
],
},
},
},
);
await rig.run({ args: 'Do a multi-step task' });
const hookLogs = rig.readHookLogs();
const beforeAgentLogs = hookLogs.filter(
(log) => log.hookCall.hook_event_name === 'BeforeAgent',
);
const afterAgentLogs = hookLogs.filter(
(log) => log.hookCall.hook_event_name === 'AfterAgent',
);
expect(beforeAgentLogs).toHaveLength(1);
expect(afterAgentLogs).toHaveLength(1);
const afterAgentLog = afterAgentLogs[0];
expect(afterAgentLog).toBeDefined();
expect(afterAgentLog?.hookCall.stdout).toContain('AfterAgent Fired');
});
});
});
@@ -0,0 +1,2 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Hi there!"}],"role":"model"},"finishReason":"STOP","index":0}]}]}
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Clarification: I am a bot."}],"role":"model"},"finishReason":"STOP","index":0}]}]}
@@ -0,0 +1 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"**Initializing File Creation**\n\nI'm starting to think about how to make a new file called `test.txt`. My plan is to use a `write_file` tool. I'll need to specify the location and what the file should contain. For now, it will be empty.\n\n\n","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":13216,"totalTokenCount":13269,"promptTokensDetails":[{"modality":"TEXT","tokenCount":13216}],"thoughtsTokenCount":53}},{"candidates":[{"content":{"parts":[{"functionCall":{"name":"write_file","args":{"file_path":"test.txt","content":""}},"thoughtSignature":"CiQBcsjafJ20Qbx0YvING6aZ0wYoGWJh3eqornOG4E4AfBLiVsQKXwFyyNp8UlwYs/pv9IRQQGhDlrmlOJF2hfQijryyUYLI+qjDYTpZ6KKIfZF4+vS0soL2BJ3eTXA6gaadFEfNQem3WQVeQoKLFoW4Hv4mbasXqQc0K3p15DuSAtZZENTbCnsBcsjafGK+BJyF/Npnd7gyU0TL5PXePT0nuDFjhJDxlSRUJHDP315TewD3PUYsXd10oWsfhy4B5AngyUiBPUoajdsxg8WxaxnOZYqcp8EIuwtGZrCTev6IihT5nE5jj7u0P9vtnCmkAc6p+4O7Q7Jku1uVGqeJChgzI4YKSAFyyNp8EXSdbttV4xzX+NLKkc276L8Y63tnKU6/Y7fc9/58tU29DSdrgwfe9qmvwtTsO0piFXSLazqHJt8h2bgR7A7GnKDiIA=="}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":13216,"candidatesTokenCount":21,"totalTokenCount":13290,"promptTokensDetails":[{"modality":"TEXT","tokenCount":13216}],"thoughtsTokenCount":53}},{"candidates":[{"content":{"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":13216,"candidatesTokenCount":21,"totalTokenCount":13290,"promptTokensDetails":[{"modality":"TEXT","tokenCount":13216}],"thoughtsTokenCount":53}}]}
@@ -0,0 +1 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"**Devising a Greeting Phrase**\n\nI've been occupied by the constraint of constructing a five-word salutation. My goal is to make it natural and concise. I'm exploring various combinations to meet the specified word count precisely.\n\n\n","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12587,"totalTokenCount":12612,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12587}],"thoughtsTokenCount":25}},{"candidates":[{"content":{"parts":[{"text":"Hello! How can I help you?","thoughtSignature":"CiQBcsjafHso9FUsdYOCTv1xOLlW4MnjbeYnUUBocz0KNgHSzOcKZAFyyNp8XuI6j2afRczgPL8v1dxfVwAJ+5XDKhWKIYf1/8TKGVHh7xXnPfdYBdQ07Ohe7OZXr92xL/IC7B1U2SHDuAOozC0CCW7aiDysu6Hbo6jzYfW5epKht4QjdxYgcKHySrkKMQFyyNp8jXWlHmox53O/CJPXXz2FAmw+ubHKBpYgRezBpA+byyEY2RbVYlZlEMSNkhs="}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":12587,"candidatesTokenCount":7,"totalTokenCount":12619,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12587}],"thoughtsTokenCount":25}}]}
@@ -0,0 +1,4 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"**Creating the First Test File**\n\nI'll use the `write_file` tool to create `first-run.txt` with the content \"test1\".\n\n\n","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12779,"totalTokenCount":12824,"cachedContentTokenCount":12204,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12779}],"cacheTokensDetails":[{"modality":"TEXT","tokenCount":12204}],"thoughtsTokenCount":45}},{"candidates":[{"content":{"parts":[{"functionCall":{"name":"write_file","args":{"content":"test1","file_path":"first-run.txt"}},"thoughtSignature":"CiQBcsjafG+JDSqtKOK+ZvSjZQZmS91c1Gz0YyTiirI2u5+rhEIKZAFyyNp8DNXb+xHILTC+FVlEifqEHdrmfFNBLKojci1UIBhcZpQ4UXCMkxUXYKO34IjTlyLgSsjVbbXWEFXatb/z/RtTDcf51uc3YOEwlDScGempkJxfFgcPfIiD7bhuHBqdQfUKfAFyyNp8wZ71h+QjdfVw12PwDXWgGZ0Xed1GuyJXuqAwpWnwxDIvsDaPwDFYyLR1XDiIZZk4AvFCGt6HGMSLRuPh4K3i9CVnDc5hcjyvMIde0idAFMrgs2Mq5SARfCPrWkqyq2f0Q0WonUl2n7yr/sDQ78rx2E6qXyUJ8XMKfAFyyNp8DdTYLttyI0jknqAeZDxdFmHtpJUI8UKP5YHzpQc8Qn80OJcwhZSRH4HRKCqoC7Sukq/A5vJ5T468WqgjOoLlPLq02bYRTf/q6LC1ogEhdLHrcFv2jDeCdXJJ8NHv3O4DZAUAk1W5Gd0428zMFOxH3AgkWwEGuow="}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":12779,"candidatesTokenCount":24,"totalTokenCount":12848,"cachedContentTokenCount":12204,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12779}],"cacheTokensDetails":[{"modality":"TEXT","tokenCount":12204}],"thoughtsTokenCount":45}}]}
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"File created successfully. Active hook executed"}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":12951,"candidatesTokenCount":7,"totalTokenCount":12958,"cachedContentTokenCount":12202,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12951}],"cacheTokensDetails":[{"modality":"TEXT","tokenCount":12202}]}}]}
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"**Creating the Second Test File**\n\nI'll use the `write_file` tool to create `second-run.txt` with the content \"test2\".\n\n\n","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12779,"totalTokenCount":12826,"cachedContentTokenCount":12204,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12779}],"cacheTokensDetails":[{"modality":"TEXT","tokenCount":12204}],"thoughtsTokenCount":47}},{"candidates":[{"content":{"parts":[{"functionCall":{"name":"write_file","args":{"content":"test2","file_path":"second-run.txt"}},"thoughtSignature":"CiQBcsjafG+JDSqtKOK+ZvSjZQZmS91c1Gz0YyTiirI2u5+rhEIKZAFyyNp8DNXb+xHILTC+FVlEifqEHdrmfFNBLKojci1UIBhcZpQ4UXCMkxUXYKO34IjTlyLgSsjVbbXWEFXatb/z/RtTDcf51uc3YOEwlDScGempkJxfFgcPfIiD7bhuHBqdQfUKfAFyyNp8wZ71h+QjdfVw12PwDXWgGZ0Xed1GuyJXuqAwpWnwxDIvsDaPwDFYyLR1XDiIZZk4AvFCGt6HGMSLRuPh4K3i9CVnDc5hcjyvMIde0idAFMrgs2Mq5SARfCPrWkqyq2f0Q0WonUl2n7yr/sDQ78rx2E6qXyUJ8XMKfAFyyNp8DdTYLttyI0jknqAeZDxdFmHtpJUI8UKP5YHzpQc8Qn80OJcwhZSRH4HRKCqoC7Sukq/A5vJ5T468WqgjOoLlPLq02bYRTf/q6LC1ogEhdLHrcFv2jDeCdXJJ8NHv3O4DZAUAk1W5Gd0428zMFOxH3AgkWwEGuow="}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":12779,"candidatesTokenCount":24,"totalTokenCount":12850,"cachedContentTokenCount":12204,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12779}],"cacheTokensDetails":[{"modality":"TEXT","tokenCount":12204}],"thoughtsTokenCount":47}}]}
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"File created successfully. Active hook executed"}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":12951,"candidatesTokenCount":7,"totalTokenCount":12958,"cachedContentTokenCount":12202,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12951}],"cacheTokensDetails":[{"modality":"TEXT","tokenCount":12202}]}}]}
@@ -0,0 +1,2 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"**Creating the Test File**\n\nI'll use the `write_file` tool to create `disabled-test.txt` with the content \"test\".\n\n\n","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12779,"totalTokenCount":12820,"cachedContentTokenCount":12204,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12779}],"cacheTokensDetails":[{"modality":"TEXT","tokenCount":12204}],"thoughtsTokenCount":41}},{"candidates":[{"content":{"parts":[{"functionCall":{"name":"write_file","args":{"content":"test","file_path":"disabled-test.txt"}},"thoughtSignature":"CiQBcsjafG+JDSqtKOK+ZvSjZQZmS91c1Gz0YyTiirI2u5+rhEIKZAFyyNp8DNXb+xHILTC+FVlEifqEHdrmfFNBLKojci1UIBhcZpQ4UXCMkxUXYKO34IjTlyLgSsjVbbXWEFXatb/z/RtTDcf51uc3YOEwlDScGempkJxfFgcPfIiD7bhuHBqdQfUKfAFyyNp8wZ71h+QjdfVw12PwDXWgGZ0Xed1GuyJXuqAwpWnwxDIvsDaPwDFYyLR1XDiIZZk4AvFCGt6HGMSLRuPh4K3i9CVnDc5hcjyvMIde0idAFMrgs2Mq5SARfCPrWkqyq2f0Q0WonUl2n7yr/sDQ78rx2E6qXyUJ8XMKfAFyyNp8DdTYLttyI0jknqAeZDxdFmHtpJUI8UKP5YHzpQc8Qn80OJcwhZSRH4HRKCqoC7Sukq/A5vJ5T468WqgjOoLlPLq02bYRTf/q6LC1ogEhdLHrcFv2jDeCdXJJ8NHv3O4DZAUAk1W5Gd0428zMFOxH3AgkWwEGuow="}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":12779,"candidatesTokenCount":24,"totalTokenCount":12844,"cachedContentTokenCount":12204,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12779}],"cacheTokensDetails":[{"modality":"TEXT","tokenCount":12204}],"thoughtsTokenCount":41}}]}
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"File created successfully. Enabled hook executed."}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":12951,"candidatesTokenCount":8,"totalTokenCount":12959,"cachedContentTokenCount":12202,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12951}],"cacheTokensDetails":[{"modality":"TEXT","tokenCount":12202}]}}]}
@@ -0,0 +1,2 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"functionCall":{"name":"write_file","args":{"content":"original content","file_path":"original.txt"}}}],"role":"model"},"finishReason":"STOP","index":0}]}]}
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"I have created the file."}],"role":"model"},"finishReason":"STOP","index":0}]}]}
@@ -0,0 +1,4 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"**Formulating a Plan**\n\nOkay, I've outlined the initial steps: I'll use the `write_file` tool to make a file named `multi-event-test.txt` containing the text \"testing multiple events\". After that, I'll need to remember to reply with the phrase as requested. It seems straightforward so far.\n\n\n","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12622,"totalTokenCount":12692,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12622}],"thoughtsTokenCount":70}},{"candidates":[{"content":{"parts":[{"text":"**Confirming the Procedure**\n\nI've solidified the steps. First, I'll create `multi-event-test.txt` using the `write_file` tool with the required content. Following that, my response will be \"BeforeAgent: User request processed.\" This ensures I fulfill both parts of the request.\n\n\n","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12622,"totalTokenCount":12713,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12622}],"thoughtsTokenCount":91}},{"candidates":[{"content":{"parts":[{"functionCall":{"name":"write_file","args":{"file_path":"multi-event-test.txt","content":"testing multiple events"}},"thoughtSignature":"CiQBcsjafIqcYtNLIeBwJi3k5k8jho3QiWM+51Kw5vTQ7/V4qVQKZgFyyNp8mIIB0+Mvwhvo2fACDpTWpRYeOFPGrjZrc+N05S0WGEHzE4Dv9peHKdvZkjGNW+HyYHXoRpd5c/ScdhPxQoVZmZ9K7sRjVxv/nWVDoKnHlSsn94nJ8acjLnj1oqt9cHni0ApyAXLI2nwj5WuLHr+UFIxnqRKCUJboLo6bQMkqR1TsqXbjsgHp3zNQYT+xzbse4PKPLJV48FN6cL9MrrZ81E7k7AVo1cKyrC7ky7tdRH6gYHewIqgQWBIUgMKhLkePH/fYZ6fS7SMrf4Q6DFGHh6pIAAdRCooBAXLI2nxpudEZr+5jZAaAcCMIdij5oZq3s0xsQv/7iWVh8IossRuR0J4eMMSN8fV6+fjbSQ6YtJQfrxsm3a6gVIkJNno2b2PRZestS/0Z7DvPDGE6r1sGchvbcz8EW7Z/pvJvPBRFWlMTJ1eqY9vuyuNYMKeWlyt+5V9y2GUbcLWvcNDZSC43vQEKCo0BAXLI2nxP4INgBaSHInyFrG1/SEP0SUimKvP69FkcIBxx60x3iKqdtb2flLIhoOr/QuesASlflRfzNo3J5LOudrjZzNlRfVRqOZIyOVxZlviXtO7+w/oPCV61Sby6xPTGtFsWlt6GxEGF7iYLfvi4KWN9q/W9tlqEqUrpl/WMwS/4pYBi1xPcvXZNlJ6g"}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":12622,"candidatesTokenCount":28,"totalTokenCount":12741,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12622}],"thoughtsTokenCount":91}}]}
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"\n"}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12836,"totalTokenCount":12836,"cachedContentTokenCount":12204,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12836}],"cacheTokensDetails":[{"modality":"TEXT","tokenCount":12204}]}},{"candidates":[{"content":{"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":12836,"totalTokenCount":12836,"cachedContentTokenCount":12204,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12836}],"cacheTokensDetails":[{"modality":"TEXT","tokenCount":12204}]}}]}
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"\n"}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12836,"totalTokenCount":12836,"cachedContentTokenCount":12204,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12836}],"cacheTokensDetails":[{"modality":"TEXT","tokenCount":12204}]}},{"candidates":[{"content":{"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":12836,"totalTokenCount":12836,"cachedContentTokenCount":12204,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12836}],"cacheTokensDetails":[{"modality":"TEXT","tokenCount":12204}]}}]}
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"**Echoing User Commands**\n\nI'm now tasked with echoing a specific phrase following a particular signal, but it's becoming complex. The user wants me to repeat \"BeforeAgent: User request processed\" when prompted. It appears I need to retain context from the previous turn, the user's initial request to create a file, to correctly respond now.\n\n\n","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12759,"totalTokenCount":12827,"cachedContentTokenCount":12199,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12759}],"cacheTokensDetails":[{"modality":"TEXT","tokenCount":12199}],"thoughtsTokenCount":68}},{"candidates":[{"content":{"parts":[{"text":"**Responding Precisely to Prompt**\n\nI've determined I need to repeat the phrase \"BeforeAgent: User request processed,\" even though the overall context and turn history are complex. The user has given several prompts, but has now provided a more direct command, which I believe is to follow up on the previous request. I am taking care to match the specific instructions the user provided.\n\n\n","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12759,"totalTokenCount":12982,"cachedContentTokenCount":12199,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12759}],"cacheTokensDetails":[{"modality":"TEXT","tokenCount":12199}],"thoughtsTokenCount":223}},{"candidates":[{"content":{"parts":[{"text":"BeforeAgent: User request processed","thoughtSignature":"CiQBcsjafAntJrb1JBgpnZaCNeYhOJXtbH6dKTeM1llglCdoOvUKYwFyyNp8PUj5sihYyITQJhdz4MqEeftyuUc4G+iTprve11gPN04eK9Y1Wi/wyln4RjRgroIrV5kByKzdGhECoyCeInpiILGhY0peIM7dZOKFdIOL7xAR9pmn4wMreqyH7l5WSAqJAQFyyNp8Cugemkt4YZWkIwEJYmUukLFx4d5EwP/9k/e4OH/svpM+uyuN3n1KVN3bFgRV5yuF0HnDLl+P7WVSSxMmWvXO2f7A1HALg+gCvZw9IV7Btgg1qp81dDoNcVkzSbTBtT4UrlJ5R6sclvHZOLUtKGwBEQ6zRonBugAgj9RV4BT1AJNOgdSsCokBAXLI2nyDGU1Iq30QVbqhgEwFa5sB6uPC+35BV8ZKGwK+YglO9rqXMrkXM+GcQi2hVIsOFXBYGTS6E2/mQfFbIKDytrb1JgP3q5xVd/bE23M2Nnf+q5TLbRpLAPmyfg0AGwhN0L7d5W6b/3ydqEPeA1/Vw/cnBzz5ND1LOTOX6BFqEs33/WHj7HIKpAEBcsjafEsn8//cZMWUQcSAucBQauojv/f7h11nbeMrZK84nEotR30BgMIWYiiWM6sGDy/4MzHwr+z2YdAz4PSgRvEf7DPxHps2nvZfAdtskgtdPl2JD81WpokSnJvCqU+cOuz+Nh3+fIiZ6vEsVpi/5cwEiGT0g3Z3I2ubyzv58oH8YnVQlKT3MsKRGb5//aXZJY57jNrexgDPzYAQsBgSuGBmqwqaAQFyyNp8sSIYw3It6GpZqC+oxJCC26pt4RxhG8rDZ3zuoADYlOpoUdSzbNuDB+iVHeen5OoCEAaH0GrFV4iZxgu40wu4ZD/VMfHi/Vm7vku23EUV/94U8mT+VEwPfd2gqv+3xPZ9MEHjOOox1Xq1984w2cA6u0Qn7wWHXeOGFVGSOHtdJtQ7ToNT8VEecblAVq8lm42sSccXQEEKmAEBcsjafONCvBhW2s8Bset20YFdbeSHelnILFDxXlCoYla5nP5UjGk4vpXu2+7RCFtKXfoyYEVEkmiGBRsmwJ82Q1nMkGkXMhuTdNhu4aCwI5m+STGxx26vkp9bcqGwMDHBotZL63PSrJacRoW8zfpDXD1PABLeTIfh5jgipQdgltyjlbc+3qfIfjBYNRSkE8ByErSz5rT7SwqSAQFyyNp8W2kut1PSJISxM7YJtbRdFqPBTikGDM6F/3l6ba6LpeRBfHdtueLChqFpwLH41VdIPQ7lRZflOq3KaZz+TQ11eDnYQbiaIdGOPgHJ/HH/0iQv2hnoOY5vg3gubFWFuZh9Bfun2VCYUI39tIxGC46TZWfgCdiP/O9CFOlpDfidPiz5ZS/4LhG9FA4Q85OuCpEBAXLI2nzpoEUA6jCZopeNTRA2uZ1r0DMm5cWVVXtFO4CoRS+19BbADNBRyNrR5qcf7bUflJBvMRVxx3mtmgK9aE5VmKYxK2Dqg15l9RUxjtqspC3VVmszVd6lOkf1BBQ/VtWDulqRetKE2u62Is9NNGuK9HsLzIBLRRc8QoML41WffuXQ+uxwyXpjx2USC44MGAqIAQFyyNp8gN3lOyHyk674W3Pyv+Egw1ZDUQK4xpvAfgnK+y53gclMGJ2IjOSvg4j0f1WO1OGqY2TBUFS7w21PXasvCkfxpqeStEb+U7Vm0r63LzXdGdug5/b1Ap6Phn4/vAYmfaKISKG4+QpjI+ehgEJzsIee2rgqOaePTP18fq8T7EDbF/B/iscKNQFyyNp8DWt2a8OetaCc5E/KsntbbOcNc7yikPZBdUezphrqIH4ztpicsHvEicYF002qWHoY"}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":12759,"candidatesTokenCount":4,"totalTokenCount":12986,"cachedContentTokenCount":12199,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12759}],"cacheTokensDetails":[{"modality":"TEXT","tokenCount":12199}],"thoughtsTokenCount":223}}]}
@@ -0,0 +1 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"**Executing the Command**\n\nI've got the command, \"echo test,\" ready to go. My focus is entirely on calling the `run_shell_command` tool now. The user's input is processed, and the next step is straightforward: using the tool to execute the supplied command.\n\n\n","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12751,"totalTokenCount":12801,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12751}],"thoughtsTokenCount":50}},{"candidates":[{"content":{"parts":[{"functionCall":{"name":"run_shell_command","args":{"command":"echo test","description":"Running the command 'echo test'"}},"thoughtSignature":"CiQBcsjafL1lDlnUGmt38n1/gjwecXzy9S3qEW5sYMEno5Mr7LEKZgFyyNp8jMABmMAatt49FTdh7UiM62SI1GnjcyG+kV7xzcD73uMKHST/0D0vKP7x1equv5d6YiXnOslhVnnHotYPtVl0/kI/0unBZRdMzkBNrJXKUoSWXJXxNpV6JhJav3Uh9h1sPQqOAQFyyNp8PFeESLk0J5cPFP0EA7a13iA/rXTiKoHnjSCzDV9ALcXM78xv10/V028ZtDeQslYfT82q4++W8AlJwTQRTIrdscu2y+nCS8jnQizYN1V1yR42eMzuBU3txXcqEV8bmP6GGOe58vrqyS2zdnJKCgMntMB/niwlJlr5frhDestSOJk62tVDWKFzOiAKOAFyyNp81FtGXQTX+OSio/2PbzpCCuaQFqpEgCZpkaXXyvmXYDAI1qCq1tA+m/e5ozWdm8zTGuyb"}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":12751,"candidatesTokenCount":28,"totalTokenCount":12829,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12751}],"thoughtsTokenCount":50}}]}
@@ -0,0 +1 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"**Seeking Task Clarity**\n\nI'm currently focused on identifying the precise task. My initial assessment indicates the user is seeking assistance, but the specific requirements remain undefined. I will directly solicit a detailed task description from the user to clarify this.\n\n\n","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12604,"totalTokenCount":12633,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12604}],"thoughtsTokenCount":29}},{"candidates":[{"content":{"parts":[{"text":"Hello! I'm ready to help. Please describe the task you'd like me to assist you with.","thoughtSignature":"CiQBcsjafM2CL00L595T19DK8M8zP5p9/tbFPPwdM2S6669z2FgKYQFyyNp8Ya0YVCtft9Asr/45XOCfNdPWbwZt8SvIeX3IxYzOFcOK14+DnoDIuTIrmRQBeUvdxD59QmEWx+/OaSxj9564L0IU703C1JX20buEtYhkRM4LhK0G4LG/z6IJauEKSQFyyNp8n784BnEcDTQGfZ8/s3pl/TNaNzjQx0o8wYCYZH1qsRbVa3YJAvRGrVXL6y9ka10w0lhEsrQ8vOiw6ilZKirA5DjLz4U="}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":12604,"candidatesTokenCount":22,"totalTokenCount":12655,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12604}],"thoughtsTokenCount":29}}]}
@@ -0,0 +1 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"**Greeting the User**\n\nI've registered the user's greeting. I'm primed to respond with a friendly welcome and signal my availability to assist. My focus now is drafting a suitable response.\n\n\n","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12761,"totalTokenCount":12787,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12761}],"thoughtsTokenCount":26}},{"candidates":[{"content":{"parts":[{"text":"Hello! I'm ready to help. What can I do for you?","thoughtSignature":"CikBcsjafBz/0rqJuIv9woxRvivjZyAqBjpoJhOTSPfcbMWCawTfcyKImQpxAXLI2nxyuBo6dqZmTxkH7XxPxjq7mNoacRa48wc/eT5caK/4tu0Y9fJ1ScpJZb+tCNzrqTNwVXa98ppjB2O/X4eejJN+hUr3LCalDFRdRLO17PFUI5qgYSbSgIGzhbnQASgzOArvvqzDPPgqXWVIDj8KMQFyyNp8ayfqBNRkBykRSTDtzOKVGkjLW1dXWamLB4ojeEVHSOgne4vlYaKs44pitsg="}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":12761,"candidatesTokenCount":15,"totalTokenCount":12802,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12761}],"thoughtsTokenCount":26}}]}
@@ -0,0 +1 @@
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"**Initiating a Dialogue**\n\nI've successfully received and understood the user's initial request. My next move will be to output a simple \"Hello\" as a greeting, fulfilling the basic instruction I was given. This constitutes the first step in the interaction, and I'm ready to move forward based on the user's subsequent input.\n\n\n","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":12588,"totalTokenCount":12607,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12588}],"thoughtsTokenCount":19}},{"candidates":[{"content":{"parts":[{"text":"Hello","thoughtSignature":"CikBcsjafB9jXawgyqQ5mpEJ4ihpLD/B2i8GR75sod00ZF3TCbrLHS9YjgpeAXLI2nx1fmJO2VIiwBpF+vLBPhYE/B2992PVW6XM20cEYx4g0leDNs6BIhzEipm6RYOxzgz8KxH9+ZkCnd8bVZr59lbDCgqSCSB6IKA+csXHKsF9g3UMRAtoSBwiBw=="}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":12588,"totalTokenCount":12607,"promptTokensDetails":[{"modality":"TEXT","tokenCount":12588}],"thoughtsTokenCount":19}}]}
File diff suppressed because it is too large Load Diff
+30 -12
View File
@@ -22,11 +22,9 @@ describe('JSON output', () => {
});
it('should return a valid JSON with response and stats', async () => {
const result = await rig.run(
'What is the capital of France?',
'--output-format',
'json',
);
const result = await rig.run({
args: ['What is the capital of France?', '--output-format', 'json'],
});
const parsed = JSON.parse(result);
expect(parsed).toHaveProperty('response');
@@ -37,6 +35,17 @@ describe('JSON output', () => {
expect(typeof parsed.stats).toBe('object');
});
it('should return a valid JSON with a session ID', async () => {
const result = await rig.run({
args: ['Hello', '--output-format', 'json'],
});
const parsed = JSON.parse(result);
expect(parsed).toHaveProperty('session_id');
expect(typeof parsed.session_id).toBe('string');
expect(parsed.session_id).not.toBe('');
});
it('should return a JSON error for sd auth mismatch before running', async () => {
process.env['GOOGLE_GENAI_USE_GCA'] = 'true';
await rig.setup('json-output-auth-mismatch', {
@@ -49,7 +58,7 @@ describe('JSON output', () => {
let thrown: Error | undefined;
try {
await rig.run('Hello', '--output-format', 'json');
await rig.run({ args: ['Hello', '--output-format', 'json'] });
expect.fail('Expected process to exit with error');
} catch (e) {
thrown = e as Error;
@@ -87,6 +96,9 @@ describe('JSON output', () => {
"enforced authentication type is 'gemini-api-key'",
);
expect(payload.error.message).toContain("current type is 'oauth-personal'");
expect(payload).toHaveProperty('session_id');
expect(typeof payload.session_id).toBe('string');
expect(payload.session_id).not.toBe('');
});
it('should not exit on tool errors and allow model to self-correct in JSON mode', async () => {
@@ -96,12 +108,14 @@ describe('JSON output', () => {
'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".',
'--output-format',
'json',
);
const result = await rig.run({
args: [
`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".',
'--output-format',
'json',
],
});
const parsed = JSON.parse(result);
@@ -129,5 +143,9 @@ describe('JSON output', () => {
// Should NOT have an error field at the top level
expect(parsed.error).toBeUndefined();
expect(parsed).toHaveProperty('session_id');
expect(typeof parsed.session_id).toBe('string');
expect(parsed.session_id).not.toBe('');
});
});
+13 -4
View File
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it } from 'vitest';
import { describe, it, beforeEach, afterEach } from 'vitest';
import {
TestRig,
poll,
@@ -15,9 +15,18 @@ import { existsSync } from 'node:fs';
import { join } from 'node:path';
describe('list_directory', () => {
let rig: TestRig;
beforeEach(() => {
rig = new TestRig();
});
afterEach(async () => await rig.cleanup());
it('should be able to list a directory', async () => {
const rig = new TestRig();
rig.setup('should be able to list a directory');
await rig.setup('should be able to list a directory', {
settings: { tools: { core: ['list_directory'] } },
});
rig.createFile('file1.txt', 'file 1 content');
rig.mkdir('subdir');
rig.sync();
@@ -36,7 +45,7 @@ describe('list_directory', () => {
const prompt = `Can you list the files in the current directory.`;
const result = await rig.run(prompt);
const result = await rig.run({ args: prompt });
try {
await rig.expectToolCallSuccess(['list_directory']);
@@ -23,7 +23,7 @@
import { writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { beforeAll, describe, it } from 'vitest';
import { describe, it, afterEach, beforeEach } from 'vitest';
import { TestRig } from './test-helper.js';
// Create a minimal MCP server that doesn't require external dependencies
@@ -166,9 +166,15 @@ rpc.send({
`;
describe('mcp server with cyclic tool schema is detected', () => {
const rig = new TestRig();
let rig: TestRig;
beforeAll(async () => {
beforeEach(() => {
rig = new TestRig();
});
afterEach(async () => await rig.cleanup());
it('mcp tool list should include tool with cyclic tool schema', async () => {
// Setup test directory with MCP server configuration
await rig.setup('cyclic-schema-mcp-server', {
settings: {
@@ -190,13 +196,11 @@ describe('mcp server with cyclic tool schema is detected', () => {
const { chmodSync } = await import('node:fs');
chmodSync(testServerPath, 0o755);
}
});
it('mcp tool list should include tool with cyclic tool schema', async () => {
const run = await rig.runInteractive();
await run.type('/mcp list');
await run.sendKeys('\r');
await run.type('\r');
await run.expectText('tool_with_cyclic_schema');
});
+17 -14
View File
@@ -4,24 +4,29 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect } from 'vitest';
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { TestRig } from './test-helper.js';
describe('mixed input crash prevention', () => {
let rig: TestRig;
beforeEach(() => {
rig = new TestRig();
});
afterEach(async () => await rig.cleanup());
it('should not crash when using mixed prompt inputs', async () => {
const rig = new TestRig();
rig.setup('should not crash when using mixed prompt inputs');
// Test: echo "say '1'." | gemini --prompt-interactive="say '2'." say '3'.
const stdinContent = "say '1'.";
try {
await rig.run(
{ stdin: stdinContent },
'--prompt-interactive',
"say '2'.",
"say '3'.",
);
await rig.run({
args: ['--prompt-interactive', "say '2'.", "say '3'."],
stdin: stdinContent,
});
throw new Error('Expected the command to fail, but it succeeded');
} catch (error: unknown) {
expect(error).toBeInstanceOf(Error);
@@ -40,15 +45,13 @@ describe('mixed input crash prevention', () => {
});
it('should provide clear error message for mixed input', async () => {
const rig = new TestRig();
rig.setup('should provide clear error message for mixed input');
try {
await rig.run(
{ stdin: 'test input' },
'--prompt-interactive',
'test prompt',
);
await rig.run({
args: ['--prompt-interactive', 'test prompt'],
stdin: 'test input',
});
throw new Error('Expected the command to fail, but it succeeded');
} catch (error: unknown) {
expect(error).toBeInstanceOf(Error);
+13 -5
View File
@@ -4,19 +4,28 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect } from 'vitest';
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
describe('read_many_files', () => {
let rig: TestRig;
beforeEach(() => {
rig = new TestRig();
});
afterEach(async () => await rig.cleanup());
it.skip('should be able to read multiple files', async () => {
const rig = new TestRig();
await rig.setup('should be able to read multiple files');
await rig.setup('should be able to read multiple files', {
settings: { tools: { core: ['read_many_files', 'read_file'] } },
});
rig.createFile('file1.txt', 'file 1 content');
rig.createFile('file2.txt', 'file 2 content');
const prompt = `Use the read_many_files tool to read the contents of file1.txt and file2.txt and then print the contents of each file.`;
const result = await rig.run(prompt);
const result = await rig.run({ args: prompt });
// Check for either read_many_files or multiple read_file calls
const allTools = rig.readToolLogs();
@@ -43,6 +52,5 @@ describe('read_many_files', () => {
// Validate model output - will throw if no output
validateModelOutput(result, null, 'Read many files test');
await rig.cleanup();
});
});
+28 -16
View File
@@ -4,13 +4,21 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect } from 'vitest';
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { TestRig } from './test-helper.js';
describe('replace', () => {
let rig: TestRig;
beforeEach(() => {
rig = new TestRig();
});
afterEach(async () => await rig.cleanup());
it('should be able to replace content in a file', async () => {
const rig = new TestRig();
await rig.setup('should be able to replace content in a file');
await rig.setup('should be able to replace content in a file', {
settings: { tools: { core: ['replace', 'read_file'] } },
});
const fileName = 'file_to_replace.txt';
const originalContent = 'foo content';
@@ -18,7 +26,9 @@ describe('replace', () => {
rig.createFile(fileName, originalContent);
await rig.run(`Replace 'foo' with 'bar' in the file 'file_to_replace.txt'`);
await rig.run({
args: `Replace 'foo' with 'bar' in the file 'file_to_replace.txt'`,
});
const foundToolCall = await rig.waitForToolCall('replace');
expect(foundToolCall, 'Expected to find a replace tool call').toBeTruthy();
@@ -27,9 +37,9 @@ describe('replace', () => {
});
it.skip('should handle $ literally when replacing text ending with $', async () => {
const rig = new TestRig();
await rig.setup(
'should handle $ literally when replacing text ending with $',
{ settings: { tools: { core: ['replace', 'read_file'] } } },
);
const fileName = 'regex.yml';
@@ -38,9 +48,9 @@ describe('replace', () => {
rig.createFile(fileName, originalContent);
await rig.run(
"Open regex.yml and append ' # updated' after the line containing ^[sv]d[a-z]$ without breaking the $ character.",
);
await rig.run({
args: "Open regex.yml and append ' # updated' after the line containing ^[sv]d[a-z]$ without breaking the $ character.",
});
const foundToolCall = await rig.waitForToolCall('replace');
expect(foundToolCall, 'Expected to find a replace tool call').toBeTruthy();
@@ -49,8 +59,9 @@ describe('replace', () => {
});
it.skip('should insert a multi-line block of text', async () => {
const rig = new TestRig();
await rig.setup('should insert a multi-line block of text');
await rig.setup('should insert a multi-line block of text', {
settings: { tools: { core: ['replace', 'read_file'] } },
});
const fileName = 'insert_block.txt';
const originalContent = 'Line A\n<INSERT_TEXT_HERE>\nLine C';
const newBlock = 'First line\nSecond line\nThird line';
@@ -59,7 +70,7 @@ describe('replace', () => {
rig.createFile(fileName, originalContent);
const prompt = `In ${fileName}, replace "<INSERT_TEXT_HERE>" with:\n${newBlock}. Use unix style line endings.`;
await rig.run(prompt);
await rig.run({ args: prompt });
const foundToolCall = await rig.waitForToolCall('replace');
expect(foundToolCall, 'Expected to find a replace tool call').toBeTruthy();
@@ -68,8 +79,9 @@ describe('replace', () => {
});
it.skip('should delete a block of text', async () => {
const rig = new TestRig();
await rig.setup('should delete a block of text');
await rig.setup('should delete a block of text', {
settings: { tools: { core: ['replace', 'read_file'] } },
});
const fileName = 'delete_block.txt';
const blockToDelete =
'## DELETE THIS ##\nThis is a block of text to delete.\n## END DELETE ##';
@@ -77,9 +89,9 @@ describe('replace', () => {
const expectedContent = 'Hello\nWorld';
rig.createFile(fileName, originalContent);
await rig.run(
`In ${fileName}, delete the entire block from "## DELETE THIS ##" to "## END DELETE ##" including the markers and the newline that follows it.`,
);
await rig.run({
args: `In ${fileName}, delete the entire block from "## DELETE THIS ##" to "## END DELETE ##" including the markers and the newline that follows it.`,
});
const foundToolCall = await rig.waitForToolCall('replace');
expect(foundToolCall, 'Expected to find a replace tool call').toBeTruthy();
+4
View File
@@ -27,6 +27,10 @@ class MockConfig {
getDebugMode() {
return true;
}
getFileFilteringRespectGeminiIgnore() {
return true;
}
}
describe('ripgrep-real-direct', () => {
+77 -78
View File
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect } from 'vitest';
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
import { getShellConfiguration } from '../packages/core/src/utils/shell-utils.js';
@@ -84,13 +84,21 @@ function getChainedEchoCommand(): { allowPattern: string; command: string } {
}
describe('run_shell_command', () => {
let rig: TestRig;
beforeEach(() => {
rig = new TestRig();
});
afterEach(async () => await rig.cleanup());
it('should be able to run a shell command', async () => {
const rig = new TestRig();
await rig.setup('should be able to run a shell command');
await rig.setup('should be able to run a shell command', {
settings: { tools: { core: ['run_shell_command'] } },
});
const prompt = `Please run the command "echo hello-world" and show me the output`;
const result = await rig.run(prompt);
const result = await rig.run({ args: prompt });
const foundToolCall = await rig.waitForToolCall('run_shell_command');
@@ -117,8 +125,9 @@ describe('run_shell_command', () => {
});
it('should be able to run a shell command via stdin', async () => {
const rig = new TestRig();
await rig.setup('should be able to run a shell command via stdin');
await rig.setup('should be able to run a shell command via stdin', {
settings: { tools: { core: ['run_shell_command'] } },
});
const prompt = `Please run the command "echo test-stdin" and show me what it outputs`;
@@ -145,7 +154,6 @@ describe('run_shell_command', () => {
});
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');
const testFile = rig.createFile('test.txt', 'Lorem\nIpsum\nDolor\n');
@@ -153,13 +161,11 @@ describe('run_shell_command', () => {
const prompt = `use ${command} to tell me how many lines there are in ${testFile}`;
// Provide the prompt via stdin to simulate non-interactive mode
const result = await rig.run(
{
stdin: prompt,
yolo: false,
},
`--allowed-tools=run_shell_command(${tool})`,
);
const result = await rig.run({
args: [`--allowed-tools=run_shell_command(${tool})`],
stdin: prompt,
yolo: false,
});
const foundToolCall = await rig.waitForToolCall('run_shell_command', 15000);
@@ -192,20 +198,17 @@ describe('run_shell_command', () => {
});
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');
const testFile = rig.createFile('test.txt', 'Lorem\nIpsum\nDolor\n');
const { command } = getLineCountCommand();
const prompt = `use ${command} to tell me how many lines there are in ${testFile}`;
const result = await rig.run(
{
stdin: prompt,
yolo: false,
},
'--allowed-tools=run_shell_command',
);
const result = await rig.run({
args: '--allowed-tools=run_shell_command',
stdin: prompt,
yolo: false,
});
const foundToolCall = await rig.waitForToolCall('run_shell_command', 15000);
@@ -229,15 +232,16 @@ describe('run_shell_command', () => {
});
it('should succeed with --yolo mode', async () => {
const rig = new TestRig();
await rig.setup('should succeed with --yolo mode');
await rig.setup('should succeed with --yolo mode', {
settings: { tools: { core: ['run_shell_command'] } },
});
const testFile = rig.createFile('test.txt', 'Lorem\nIpsum\nDolor\n');
const { command } = getLineCountCommand();
const prompt = `use ${command} to tell me how many lines there are in ${testFile}`;
const result = await rig.run({
prompt: prompt,
args: prompt,
yolo: true,
});
@@ -263,20 +267,17 @@ describe('run_shell_command', () => {
});
it.skip('should work with ShellTool alias', async () => {
const rig = new TestRig();
await rig.setup('should work with ShellTool alias');
const testFile = rig.createFile('test.txt', 'Lorem\nIpsum\nDolor\n');
const { tool, command } = getLineCountCommand();
const prompt = `use ${command} to tell me how many lines there are in ${testFile}`;
const result = await rig.run(
{
stdin: prompt,
yolo: false,
},
`--allowed-tools=ShellTool(${tool})`,
);
const result = await rig.run({
args: `--allowed-tools=ShellTool(${tool})`,
stdin: prompt,
yolo: false,
});
const foundToolCall = await rig.waitForToolCall('run_shell_command', 15000);
@@ -311,7 +312,6 @@ describe('run_shell_command', () => {
// TODO(#11062): Un-skip this once we can make it reliable by using hard coded
// model responses.
it.skip('should combine multiple --allowed-tools flags', async () => {
const rig = new TestRig();
await rig.setup('should combine multiple --allowed-tools flags');
const { tool, command } = getLineCountCommand();
@@ -319,14 +319,14 @@ describe('run_shell_command', () => {
`use both ${command} and ls to count the number of lines in files in this ` +
`directory. Do not pipe these commands into each other, run them separately.`;
const result = await rig.run(
{
stdin: prompt,
yolo: false,
},
`--allowed-tools=run_shell_command(${tool})`,
'--allowed-tools=run_shell_command(ls)',
);
const result = await rig.run({
args: [
`--allowed-tools=run_shell_command(${tool})`,
'--allowed-tools=run_shell_command(ls)',
],
stdin: prompt,
yolo: false,
});
for (const expected in ['ls', tool]) {
const foundToolCall = await rig.waitForToolCall(
@@ -361,8 +361,9 @@ describe('run_shell_command', () => {
});
it('should reject commands not on the allowlist', async () => {
const rig = new TestRig();
await rig.setup('should reject commands not on the allowlist');
await rig.setup('should reject commands not on the allowlist', {
settings: { tools: { core: ['run_shell_command'] } },
});
const testFile = rig.createFile('test.txt', 'Disallowed command check\n');
const allowedCommand = getAllowedListCommand();
@@ -373,13 +374,11 @@ describe('run_shell_command', () => {
`If the command fails because it is not permitted, respond with the single word FAIL. ` +
`If it succeeds, respond with SUCCESS.`;
const result = await rig.run(
{
stdin: prompt,
yolo: false,
},
`--allowed-tools=run_shell_command(${allowedCommand})`,
);
const result = await rig.run({
args: `--allowed-tools=run_shell_command(${allowedCommand})`,
stdin: prompt,
yolo: false,
});
if (!result.toLowerCase().includes('fail')) {
printDebugInfo(rig, result, {
@@ -429,7 +428,6 @@ describe('run_shell_command', () => {
// 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',
);
@@ -437,13 +435,11 @@ describe('run_shell_command', () => {
const chained = getChainedEchoCommand();
const shellInjection = `!{${chained.command}}`;
await rig.run(
{
stdin: `${shellInjection}\n`,
yolo: false,
},
`--allowed-tools=ShellTool(${chained.allowPattern})`,
);
await rig.run({
args: `--allowed-tools=ShellTool(${chained.allowPattern})`,
stdin: `${shellInjection}\n`,
yolo: false,
});
// CLI should refuse to execute the chained command without scheduling run_shell_command.
const toolLogs = rig
@@ -458,22 +454,24 @@ describe('run_shell_command', () => {
});
it('should allow all with "ShellTool" and other specific tools', async () => {
const rig = new TestRig();
await rig.setup(
'should allow all with "ShellTool" and other specific tools',
{
settings: { tools: { core: ['run_shell_command'] } },
},
);
const { tool } = getLineCountCommand();
const prompt = `Please run the command "echo test-allow-all" and show me the output`;
const result = await rig.run(
{
stdin: prompt,
yolo: false,
},
`--allowed-tools=run_shell_command(${tool})`,
'--allowed-tools=run_shell_command',
);
const result = await rig.run({
args: [
`--allowed-tools=run_shell_command(${tool})`,
'--allowed-tools=run_shell_command',
],
stdin: prompt,
yolo: false,
});
const foundToolCall = await rig.waitForToolCall('run_shell_command', 15000);
@@ -505,8 +503,9 @@ describe('run_shell_command', () => {
});
it('should propagate environment variables to the child process', async () => {
const rig = new TestRig();
await rig.setup('should propagate environment variables');
await rig.setup('should propagate environment variables', {
settings: { tools: { core: ['run_shell_command'] } },
});
const varName = 'GEMINI_CLI_TEST_VAR';
const varValue = `test-value-${Math.random().toString(36).substring(7)}`;
@@ -514,7 +513,7 @@ describe('run_shell_command', () => {
try {
const prompt = `Use echo to learn the value of the environment variable named ${varName} and tell me what it is.`;
const result = await rig.run(prompt);
const result = await rig.run({ args: prompt });
const foundToolCall = await rig.waitForToolCall('run_shell_command');
@@ -537,13 +536,12 @@ describe('run_shell_command', () => {
});
it.skip('should run a platform-specific file listing command', async () => {
const rig = new TestRig();
await rig.setup('should run platform-specific file listing');
const fileName = `test-file-${Math.random().toString(36).substring(7)}.txt`;
rig.createFile(fileName, 'test content');
const prompt = `Run a shell command to list the files in the current directory and tell me what they are.`;
const result = await rig.run(prompt);
const result = await rig.run({ args: prompt });
const foundToolCall = await rig.waitForToolCall('run_shell_command');
@@ -565,12 +563,13 @@ describe('run_shell_command', () => {
});
it('rejects invalid shell expressions', async () => {
const rig = new TestRig();
await rig.setup('rejects invalid shell expressions');
await rig.setup('rejects invalid shell expressions', {
settings: { tools: { core: ['run_shell_command'] } },
});
const invalidCommand = getInvalidCommand();
const result = await rig.run(
`I am testing the error handling of the run_shell_command tool. Please attempt to run the following command, which I know has invalid syntax: \`${invalidCommand}\`. If the command fails as expected, please return the word FAIL, otherwise return the word SUCCESS.`,
);
const result = await rig.run({
args: `I am testing the error handling of the run_shell_command tool. Please attempt to run the following command, which I know has invalid syntax: \`${invalidCommand}\`. If the command fails as expected, please return the word FAIL, otherwise return the word SUCCESS.`,
});
expect(result).toContain('FAIL');
const escapedInvalidCommand = JSON.stringify(invalidCommand).slice(1, -1);
+13 -4
View File
@@ -4,18 +4,27 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect } from 'vitest';
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
describe('save_memory', () => {
let rig: TestRig;
beforeEach(() => {
rig = new TestRig();
});
afterEach(async () => await rig.cleanup());
it('should be able to save to memory', async () => {
const rig = new TestRig();
await rig.setup('should be able to save to memory');
await rig.setup('should be able to save to memory', {
settings: { tools: { core: ['save_memory'] } },
});
const prompt = `remember that my favorite color is blue.
what is my favorite color? tell me that and surround it with $ symbol`;
const result = await rig.run(prompt);
const result = await rig.run({ args: prompt });
const foundToolCall = await rig.waitForToolCall('save_memory');
+13 -8
View File
@@ -10,7 +10,7 @@
* external dependencies, making it compatible with Docker sandbox mode.
*/
import { describe, it, beforeAll, expect } from 'vitest';
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { TestRig, poll, validateModelOutput } from './test-helper.js';
import { join } from 'node:path';
import { writeFileSync } from 'node:fs';
@@ -165,9 +165,15 @@ rpc.send({
`;
describe('simple-mcp-server', () => {
const rig = new TestRig();
let rig: TestRig;
beforeAll(async () => {
beforeEach(() => {
rig = new TestRig();
});
afterEach(async () => await rig.cleanup());
it('should add two numbers', async () => {
// Setup test directory with MCP server configuration
await rig.setup('simple-mcp-server', {
settings: {
@@ -177,6 +183,7 @@ describe('simple-mcp-server', () => {
args: ['mcp-server.cjs'],
},
},
tools: { core: [] },
},
});
@@ -208,14 +215,12 @@ describe('simple-mcp-server', () => {
if (!isReady) {
throw new Error('MCP server script was not ready in time.');
}
});
it('should add two numbers', async () => {
// Test directory is already set up in before hook
// Just run the command - MCP server config is in settings.json
const output = await rig.run(
'Use the `add` tool to calculate 5+10 and output only the resulting number.',
);
const output = await rig.run({
args: 'Use the `add` tool to calculate 5+10 and output only the resulting number.',
});
const foundToolCall = await rig.waitForToolCall('add');
+10 -4
View File
@@ -4,19 +4,26 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect } from 'vitest';
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
describe.skip('stdin context', () => {
let rig: TestRig;
beforeEach(() => {
rig = new TestRig();
});
afterEach(async () => await rig.cleanup());
it('should be able to use stdin as context for a prompt', async () => {
const rig = new TestRig();
await rig.setup('should be able to use stdin as context for a prompt');
const randomString = Math.random().toString(36).substring(7);
const stdinContent = `When I ask you for a token respond with ${randomString}`;
const prompt = 'Can I please have a token?';
const result = await rig.run({ prompt, stdin: stdinContent });
const result = await rig.run({ args: prompt, stdin: stdinContent });
await rig.waitForTelemetryEvent('api_request');
const lastRequest = rig.readLastApiRequest();
@@ -75,7 +82,6 @@ describe.skip('stdin context', () => {
even though gemini is intended to run interactively.
*/
const rig = new TestRig();
await rig.setup('should exit quickly if stdin stream does not end');
try {
+10 -3
View File
@@ -4,16 +4,23 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect } from 'vitest';
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { TestRig } from './test-helper.js';
describe('telemetry', () => {
let rig: TestRig;
beforeEach(() => {
rig = new TestRig();
});
afterEach(async () => await rig.cleanup());
it('should emit a metric and a log event', async () => {
const rig = new TestRig();
rig.setup('should emit a metric and a log event');
// Run a simple command that should trigger telemetry
await rig.run('just saying hi');
await rig.run({ args: 'just saying hi' });
// Verify that a user_prompt event was logged
const hasUserPromptEvent = await rig.waitForTelemetryEvent('user_prompt');
+66 -51
View File
@@ -18,6 +18,7 @@ import * as os from 'node:os';
import { GEMINI_DIR } from '../packages/core/src/utils/paths.js';
const __dirname = dirname(fileURLToPath(import.meta.url));
const BUNDLE_PATH = join(__dirname, '..', 'bundle/gemini.js');
// Get timeout based on environment
function getDefaultTimeout() {
@@ -195,17 +196,12 @@ export class InteractiveRun {
if (!timeout) {
timeout = getDefaultTimeout();
}
const found = await poll(
await poll(
() => stripAnsi(this.output).toLowerCase().includes(text.toLowerCase()),
timeout,
200,
);
expect(
found,
`Did not find expected text: "${text}". Output was:\n${stripAnsi(
this.output,
)}`,
).toBe(true);
expect(stripAnsi(this.output).toLowerCase()).toContain(text.toLowerCase());
}
// This types slowly to make sure command is correct, but only work for short
@@ -213,6 +209,12 @@ export class InteractiveRun {
async type(text: string) {
let typedSoFar = '';
for (const char of text) {
if (char === '\r') {
// wait >30ms before `enter` to avoid fast return conversion
// from bufferFastReturn() in KeypressContent.tsx
await new Promise((resolve) => setTimeout(resolve, 50));
}
this.ptyProcess.write(char);
typedSoFar += char;
@@ -271,19 +273,14 @@ export class InteractiveRun {
}
export class TestRig {
bundlePath: string;
testDir: string | null;
testDir: string | null = 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');
this.testDir = null;
}
private _interactiveRuns: InteractiveRun[] = [];
setup(
testName: string,
@@ -294,7 +291,9 @@ export class TestRig {
) {
this.testName = testName;
const sanitizedName = sanitizeTestName(testName);
this.testDir = join(env['INTEGRATION_TEST_FILE_DIR']!, sanitizedName);
const testFileDir =
env['INTEGRATION_TEST_FILE_DIR'] || join(os.tmpdir(), 'gemini-cli-tests');
this.testDir = join(testFileDir, sanitizedName);
mkdirSync(this.testDir, { recursive: true });
if (options.fakeResponsesPath) {
this.fakeResponsesPath = join(this.testDir, 'fake-responses.json');
@@ -332,7 +331,9 @@ export class TestRig {
ui: {
useAlternateBuffer: true,
},
model: DEFAULT_GEMINI_MODEL,
model: {
name: DEFAULT_GEMINI_MODEL,
},
sandbox:
env['GEMINI_SANDBOX'] !== 'false' ? env['GEMINI_SANDBOX'] : false,
// Don't show the IDE connection dialog when running from VsCode
@@ -374,7 +375,7 @@ export class TestRig {
const command = isNpmReleaseTest ? 'gemini' : 'node';
const initialArgs = isNpmReleaseTest
? extraInitialArgs
: [this.bundlePath, ...extraInitialArgs];
: [BUNDLE_PATH, ...extraInitialArgs];
if (this.fakeResponsesPath) {
if (process.env['REGENERATE_MODEL_GOLDENS'] === 'true') {
initialArgs.push('--record-responses', this.fakeResponsesPath);
@@ -385,19 +386,13 @@ export class TestRig {
return { command, initialArgs };
}
run(
promptOrOptions:
| string
| {
prompt?: string;
stdin?: string;
stdinDoesNotEnd?: boolean;
yolo?: boolean;
},
...args: string[]
): Promise<string> {
const yolo =
typeof promptOrOptions === 'string' || promptOrOptions.yolo !== false;
run(options: {
args?: string | string[];
stdin?: string;
stdinDoesNotEnd?: boolean;
yolo?: boolean;
}): Promise<string> {
const yolo = options.yolo !== false;
const { command, initialArgs } = this._getCommandAndArgs(
yolo ? ['--yolo'] : [],
);
@@ -411,21 +406,17 @@ export class TestRig {
encoding: 'utf-8',
};
if (typeof promptOrOptions === 'string') {
commandArgs.push(promptOrOptions);
} else if (
typeof promptOrOptions === 'object' &&
promptOrOptions !== null
) {
if (promptOrOptions.prompt) {
commandArgs.push(promptOrOptions.prompt);
}
if (promptOrOptions.stdin) {
execOptions.input = promptOrOptions.stdin;
if (options.args) {
if (Array.isArray(options.args)) {
commandArgs.push(...options.args);
} else {
commandArgs.push(options.args);
}
}
commandArgs.push(...args);
if (options.stdin) {
execOptions.input = options.stdin;
}
const child = spawn(command, commandArgs, {
cwd: this.testDir!,
@@ -441,10 +432,7 @@ export class TestRig {
child.stdin!.write(execOptions.input);
}
if (
typeof promptOrOptions === 'object' &&
!promptOrOptions.stdinDoesNotEnd
) {
if (!options.stdinDoesNotEnd) {
child.stdin!.end();
}
@@ -591,6 +579,18 @@ export class TestRig {
}
async cleanup() {
// Kill any interactive runs that are still active
for (const run of this._interactiveRuns) {
try {
await run.kill();
} catch (error) {
if (env['VERBOSE'] === 'true') {
console.warn('Failed to kill interactive run during cleanup:', error);
}
}
}
this._interactiveRuns = [];
if (
process.env['REGENERATE_MODEL_GOLDENS'] === 'true' &&
this.fakeResponsesPath
@@ -1024,11 +1024,25 @@ export class TestRig {
return null;
}
async runInteractive(...args: string[]): Promise<InteractiveRun> {
const { command, initialArgs } = this._getCommandAndArgs(['--yolo']);
const commandArgs = [...initialArgs, ...args];
async runInteractive(options?: {
args?: string | string[];
yolo?: boolean;
}): Promise<InteractiveRun> {
const yolo = options?.yolo !== false;
const { command, initialArgs } = this._getCommandAndArgs(
yolo ? ['--yolo'] : [],
);
const commandArgs = [...initialArgs];
const options: pty.IPtyForkOptions = {
if (options?.args) {
if (Array.isArray(options.args)) {
commandArgs.push(...options.args);
} else {
commandArgs.push(options.args);
}
}
const ptyOptions: pty.IPtyForkOptions = {
name: 'xterm-color',
cols: 80,
rows: 80,
@@ -1039,9 +1053,10 @@ export class TestRig {
};
const executable = command === 'node' ? process.execPath : command;
const ptyProcess = pty.spawn(executable, commandArgs, options);
const ptyProcess = pty.spawn(executable, commandArgs, ptyOptions);
const run = new InteractiveRun(ptyProcess);
this._interactiveRuns.push(run);
// Wait for the app to be ready
await run.expectText(' Type your message or @path/to/file', 30000);
return run;
+12 -14
View File
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { writeFileSync, readFileSync } from 'node:fs';
import { join, resolve } from 'node:path';
import { TestRig } from './test-helper.js';
@@ -47,28 +47,26 @@ const utf32BE = (s: string) => {
return Buffer.concat([bom, payload]);
};
let rig: TestRig;
let dir: string;
describe('BOM end-to-end integraion', () => {
beforeAll(async () => {
let rig: TestRig;
beforeEach(async () => {
rig = new TestRig();
await rig.setup('bom-integration');
dir = rig.testDir!;
await rig.setup('bom-integration', {
settings: { tools: { core: ['read_file'] } },
});
});
afterAll(async () => {
await rig.cleanup();
});
afterEach(async () => await rig.cleanup());
async function runAndAssert(
filename: string,
content: Buffer,
expectedText: string | null,
) {
writeFileSync(join(dir, filename), content);
writeFileSync(join(rig.testDir!, filename), content);
const prompt = `read the file ${filename} and output its exact contents`;
const output = await rig.run(prompt);
const output = await rig.run({ args: prompt });
await rig.waitForToolCall('read_file');
const lower = output.toLowerCase();
if (expectedText === null) {
@@ -126,9 +124,9 @@ describe('BOM end-to-end integraion', () => {
);
const imageContent = readFileSync(imagePath);
const filename = 'gemini-screenshot.png';
writeFileSync(join(dir, filename), imageContent);
writeFileSync(join(rig.testDir!, filename), imageContent);
const prompt = `What is shown in the image ${filename}?`;
const output = await rig.run(prompt);
const output = await rig.run({ args: prompt });
await rig.waitForToolCall('read_file');
const lower = output.toLowerCase();
// The response is non-deterministic, so we just check for some
+13 -4
View File
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, vi } from 'vitest';
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import {
TestRig,
createToolCallErrorMessage,
@@ -13,12 +13,21 @@ import {
} from './test-helper.js';
describe('write_file', () => {
let rig: TestRig;
beforeEach(() => {
rig = new TestRig();
});
afterEach(async () => await rig.cleanup());
it('should be able to write a file', async () => {
const rig = new TestRig();
await rig.setup('should be able to write a file');
await rig.setup('should be able to write a file', {
settings: { tools: { core: ['write_file', 'read_file'] } },
});
const prompt = `show me an example of using the write tool. put a dad joke in dad.txt`;
const result = await rig.run(prompt);
const result = await rig.run({ args: prompt });
const foundToolCall = await rig.waitForToolCall('write_file');
+977 -85
View File
File diff suppressed because it is too large Load Diff
+8 -6
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli",
"version": "0.20.0-nightly.20251201.2fe609cb6",
"version": "0.24.2",
"engines": {
"node": ">=20.0.0"
},
@@ -14,7 +14,7 @@
"url": "git+https://github.com/google-gemini/gemini-cli.git"
},
"config": {
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.20.0-nightly.20251201.2fe609cb6"
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.24.2"
},
"scripts": {
"start": "cross-env NODE_ENV=development node scripts/start.js",
@@ -52,7 +52,7 @@
"lint:all": "node scripts/lint.js",
"format": "prettier --experimental-cli --write .",
"typecheck": "npm run typecheck --workspaces --if-present",
"preflight": "npm run clean && npm ci && npm run format && npm run lint:ci && npm run build && npm run typecheck && npm run test:ci",
"preflight": "npm run clean && npm ci && npm run format && npm run build && npm run lint:ci && npm run typecheck && npm run test:ci",
"prepare": "husky && npm run bundle",
"prepare:package": "node scripts/prepare-package.js",
"release:version": "node scripts/version.js",
@@ -62,7 +62,7 @@
"pre-commit": "node scripts/pre-commit.js"
},
"overrides": {
"ink": "npm:@jrichman/ink@6.4.6",
"ink": "npm:@jrichman/ink@6.4.7",
"wrap-ansi": "9.0.2",
"cliui": {
"wrap-ansi": "7.0.0"
@@ -89,12 +89,13 @@
"@vitest/coverage-v8": "^3.1.1",
"@vitest/eslint-plugin": "^1.3.4",
"cross-env": "^7.0.3",
"depcheck": "^1.4.7",
"esbuild": "^0.25.0",
"esbuild-plugin-wasm": "^1.1.0",
"eslint": "^9.24.0",
"eslint-config-prettier": "^10.1.2",
"eslint-plugin-headers": "^1.3.3",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-license-header": "^0.8.0",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^5.2.0",
"glob": "^12.0.0",
@@ -113,13 +114,14 @@
"react-devtools-core": "^6.1.2",
"semver": "^7.7.2",
"strip-ansi": "^7.1.2",
"ts-prune": "^0.10.3",
"tsx": "^4.20.3",
"typescript-eslint": "^8.30.1",
"vitest": "^3.2.4",
"yargs": "^17.7.2"
},
"dependencies": {
"ink": "npm:@jrichman/ink@6.4.6",
"ink": "npm:@jrichman/ink@6.4.7",
"latest-version": "^9.0.0",
"simple-git": "^3.28.0"
},
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli-a2a-server",
"version": "0.20.0-nightly.20251201.2fe609cb6",
"version": "0.24.2",
"description": "Gemini CLI A2A Server",
"repository": {
"type": "git",
@@ -25,13 +25,13 @@
"dist"
],
"dependencies": {
"@a2a-js/sdk": "^0.3.2",
"@a2a-js/sdk": "^0.3.7",
"@google-cloud/storage": "^7.16.0",
"@google/gemini-cli-core": "file:../core",
"express": "^5.1.0",
"fs-extra": "^11.3.0",
"tar": "^7.5.2",
"uuid": "^11.1.0",
"uuid": "^13.0.0",
"winston": "^3.17.0"
},
"devDependencies": {
+13 -11
View File
@@ -12,11 +12,7 @@ import type {
RequestContext,
ExecutionEventBus,
} from '@a2a-js/sdk/server';
import type {
ToolCallRequestInfo,
ServerGeminiToolCallRequestEvent,
Config,
} from '@google/gemini-cli-core';
import type { ToolCallRequestInfo, Config } from '@google/gemini-cli-core';
import {
GeminiEventType,
SimpleExtensionLoader,
@@ -127,6 +123,7 @@ export class CoderAgentExecutor implements AgentExecutor {
contextId,
config,
eventBus,
agentSettings.autoExecute,
);
runtimeTask.taskState = persistedState._taskState;
await runtimeTask.geminiClient.initialize();
@@ -145,7 +142,13 @@ export class CoderAgentExecutor implements AgentExecutor {
): Promise<TaskWrapper> {
const agentSettings = agentSettingsInput || ({} as AgentSettings);
const config = await this.getConfig(agentSettings, taskId);
const runtimeTask = await Task.create(taskId, contextId, config, eventBus);
const runtimeTask = await Task.create(
taskId,
contextId,
config,
eventBus,
agentSettings.autoExecute,
);
await runtimeTask.geminiClient.initialize();
const wrapper = new TaskWrapper(runtimeTask, agentSettings);
@@ -280,8 +283,8 @@ export class CoderAgentExecutor implements AgentExecutor {
requestContext: RequestContext,
eventBus: ExecutionEventBus,
): Promise<void> {
const userMessage = requestContext.userMessage as Message;
const sdkTask = requestContext.task as SDKTask | undefined;
const userMessage = requestContext.userMessage;
const sdkTask = requestContext.task;
const taskId = sdkTask?.id || userMessage.taskId || uuidv4();
const contextId: string =
@@ -397,6 +400,7 @@ export class CoderAgentExecutor implements AgentExecutor {
`[CoderAgentExecutor] Error creating task ${taskId}:`,
error,
);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
pushTaskStateFailed(error, eventBus, taskId, contextId);
return;
}
@@ -477,9 +481,7 @@ export class CoderAgentExecutor implements AgentExecutor {
throw new Error('Execution aborted');
}
if (event.type === GeminiEventType.ToolCallRequest) {
toolCallRequests.push(
(event as ServerGeminiToolCallRequestEvent).value,
);
toolCallRequests.push(event.value);
continue;
}
await currentTask.acceptAgentMessage(event);
+300 -9
View File
@@ -18,12 +18,27 @@ import {
GeminiEventType,
type Config,
type ToolCallRequestInfo,
type GitService,
type CompletedToolCall,
ApprovalMode,
ToolConfirmationOutcome,
} from '@google/gemini-cli-core';
import { createMockConfig } from '../utils/testing_utils.js';
import type { ExecutionEventBus } from '@a2a-js/sdk/server';
import type { ExecutionEventBus, RequestContext } from '@a2a-js/sdk/server';
import { CoderAgentEvent } from '../types.js';
import type { ToolCall } from '@google/gemini-cli-core';
const mockProcessRestorableToolCalls = vi.hoisted(() => vi.fn());
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
const original =
await importOriginal<typeof import('@google/gemini-cli-core')>();
return {
...original,
processRestorableToolCalls: mockProcessRestorableToolCalls,
};
});
describe('Task', () => {
it('scheduleToolCalls should not modify the input requests array', async () => {
const mockConfig = createMockConfig();
@@ -71,6 +86,141 @@ describe('Task', () => {
expect(requests).toEqual(originalRequests);
});
describe('scheduleToolCalls', () => {
const mockConfig = createMockConfig();
const mockEventBus: ExecutionEventBus = {
publish: vi.fn(),
on: vi.fn(),
off: vi.fn(),
once: vi.fn(),
removeAllListeners: vi.fn(),
finished: vi.fn(),
};
beforeEach(() => {
vi.clearAllMocks();
});
it('should not create a checkpoint if no restorable tools are called', async () => {
// @ts-expect-error - Calling private constructor for test purposes.
const task = new Task(
'task-id',
'context-id',
mockConfig as Config,
mockEventBus,
);
const requests: ToolCallRequestInfo[] = [
{
callId: '1',
name: 'run_shell_command',
args: { command: 'ls' },
isClientInitiated: false,
prompt_id: 'prompt-id-1',
},
];
const abortController = new AbortController();
await task.scheduleToolCalls(requests, abortController.signal);
expect(mockProcessRestorableToolCalls).not.toHaveBeenCalled();
});
it('should create a checkpoint if a restorable tool is called', async () => {
const mockConfig = createMockConfig({
getCheckpointingEnabled: () => true,
getGitService: () => Promise.resolve({} as GitService),
});
mockProcessRestorableToolCalls.mockResolvedValue({
checkpointsToWrite: new Map([['test.json', 'test content']]),
toolCallToCheckpointMap: new Map(),
errors: [],
});
// @ts-expect-error - Calling private constructor for test purposes.
const task = new Task(
'task-id',
'context-id',
mockConfig as Config,
mockEventBus,
);
const requests: ToolCallRequestInfo[] = [
{
callId: '1',
name: 'replace',
args: {
file_path: 'test.txt',
old_string: 'old',
new_string: 'new',
},
isClientInitiated: false,
prompt_id: 'prompt-id-1',
},
];
const abortController = new AbortController();
await task.scheduleToolCalls(requests, abortController.signal);
expect(mockProcessRestorableToolCalls).toHaveBeenCalledOnce();
});
it('should process all restorable tools for checkpointing in a single batch', async () => {
const mockConfig = createMockConfig({
getCheckpointingEnabled: () => true,
getGitService: () => Promise.resolve({} as GitService),
});
mockProcessRestorableToolCalls.mockResolvedValue({
checkpointsToWrite: new Map([
['test1.json', 'test content 1'],
['test2.json', 'test content 2'],
]),
toolCallToCheckpointMap: new Map([
['1', 'test1'],
['2', 'test2'],
]),
errors: [],
});
// @ts-expect-error - Calling private constructor for test purposes.
const task = new Task(
'task-id',
'context-id',
mockConfig as Config,
mockEventBus,
);
const requests: ToolCallRequestInfo[] = [
{
callId: '1',
name: 'replace',
args: {
file_path: 'test.txt',
old_string: 'old',
new_string: 'new',
},
isClientInitiated: false,
prompt_id: 'prompt-id-1',
},
{
callId: '2',
name: 'write_file',
args: { file_path: 'test2.txt', content: 'new content' },
isClientInitiated: false,
prompt_id: 'prompt-id-2',
},
{
callId: '3',
name: 'not_restorable',
args: {},
isClientInitiated: false,
prompt_id: 'prompt-id-3',
},
];
const abortController = new AbortController();
await task.scheduleToolCalls(requests, abortController.signal);
expect(mockProcessRestorableToolCalls).toHaveBeenCalledExactlyOnceWith(
[
expect.objectContaining({ callId: '1' }),
expect.objectContaining({ callId: '2' }),
],
expect.anything(),
expect.anything(),
);
});
});
describe('acceptAgentMessage', () => {
it('should set currentTraceId when event has traceId', async () => {
const mockConfig = createMockConfig();
@@ -205,10 +355,12 @@ describe('Task', () => {
let task: Task;
type SpyInstance = ReturnType<typeof vi.spyOn>;
let setTaskStateAndPublishUpdateSpy: SpyInstance;
let mockConfig: Config;
let mockEventBus: ExecutionEventBus;
beforeEach(() => {
const mockConfig = createMockConfig();
const mockEventBus: ExecutionEventBus = {
mockConfig = createMockConfig() as Config;
mockEventBus = {
publish: vi.fn(),
on: vi.fn(),
off: vi.fn(),
@@ -218,12 +370,7 @@ describe('Task', () => {
};
// @ts-expect-error - Calling private constructor
task = new Task(
'task-id',
'context-id',
mockConfig as Config,
mockEventBus,
);
task = new Task('task-id', 'context-id', mockConfig, mockEventBus);
// Spy on the method we want to check calls for
setTaskStateAndPublishUpdateSpy = vi.spyOn(
@@ -317,5 +464,149 @@ describe('Task', () => {
);
expect(finalCall).toBeUndefined();
});
describe('auto-approval', () => {
it('should auto-approve tool calls when autoExecute is true', () => {
task.autoExecute = true;
const onConfirmSpy = vi.fn();
const toolCalls = [
{
request: { callId: '1' },
status: 'awaiting_approval',
confirmationDetails: { onConfirm: onConfirmSpy },
},
] as unknown as ToolCall[];
// @ts-expect-error - Calling private method
task._schedulerToolCallsUpdate(toolCalls);
expect(onConfirmSpy).toHaveBeenCalledWith(
ToolConfirmationOutcome.ProceedOnce,
);
});
it('should auto-approve tool calls when approval mode is YOLO', () => {
(mockConfig.getApprovalMode as Mock).mockReturnValue(ApprovalMode.YOLO);
task.autoExecute = false;
const onConfirmSpy = vi.fn();
const toolCalls = [
{
request: { callId: '1' },
status: 'awaiting_approval',
confirmationDetails: { onConfirm: onConfirmSpy },
},
] as unknown as ToolCall[];
// @ts-expect-error - Calling private method
task._schedulerToolCallsUpdate(toolCalls);
expect(onConfirmSpy).toHaveBeenCalledWith(
ToolConfirmationOutcome.ProceedOnce,
);
});
it('should NOT auto-approve when autoExecute is false and mode is not YOLO', () => {
task.autoExecute = false;
(mockConfig.getApprovalMode as Mock).mockReturnValue(
ApprovalMode.DEFAULT,
);
const onConfirmSpy = vi.fn();
const toolCalls = [
{
request: { callId: '1' },
status: 'awaiting_approval',
confirmationDetails: { onConfirm: onConfirmSpy },
},
] as unknown as ToolCall[];
// @ts-expect-error - Calling private method
task._schedulerToolCallsUpdate(toolCalls);
expect(onConfirmSpy).not.toHaveBeenCalled();
});
});
});
describe('currentPromptId and promptCount', () => {
it('should correctly initialize and update promptId and promptCount', async () => {
const mockConfig = createMockConfig();
mockConfig.getGeminiClient = vi.fn().mockReturnValue({
sendMessageStream: vi.fn().mockReturnValue((async function* () {})()),
});
mockConfig.getSessionId = () => 'test-session-id';
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
const task = new Task(
'task-id',
'context-id',
mockConfig as Config,
mockEventBus,
);
// Initial state
expect(task.currentPromptId).toBeUndefined();
expect(task.promptCount).toBe(0);
// First user message should set prompt_id
const userMessage1 = {
userMessage: {
parts: [{ kind: 'text', text: 'hello' }],
},
} as RequestContext;
const abortController1 = new AbortController();
for await (const _ of task.acceptUserMessage(
userMessage1,
abortController1.signal,
)) {
// no-op
}
const expectedPromptId1 = 'test-session-id########0';
expect(task.promptCount).toBe(1);
expect(task.currentPromptId).toBe(expectedPromptId1);
// A new user message should generate a new prompt_id
const userMessage2 = {
userMessage: {
parts: [{ kind: 'text', text: 'world' }],
},
} as RequestContext;
const abortController2 = new AbortController();
for await (const _ of task.acceptUserMessage(
userMessage2,
abortController2.signal,
)) {
// no-op
}
const expectedPromptId2 = 'test-session-id########1';
expect(task.promptCount).toBe(2);
expect(task.currentPromptId).toBe(expectedPromptId2);
// Subsequent tool call processing should use the same prompt_id
const completedTool = {
request: { callId: 'tool-1' },
response: { responseParts: [{ text: 'tool output' }] },
} as CompletedToolCall;
const abortController3 = new AbortController();
for await (const _ of task.sendCompletedToolsToLlm(
[completedTool],
abortController3.signal,
)) {
// no-op
}
expect(task.promptCount).toBe(2);
expect(task.currentPromptId).toBe(expectedPromptId2);
});
});
});
+78 -14
View File
@@ -15,6 +15,7 @@ import {
isNodeError,
parseAndFormatApiError,
safeLiteralReplace,
DEFAULT_GUI_EDITOR,
type AnyDeclarativeTool,
type ToolCall,
type ToolConfirmationPayload,
@@ -26,6 +27,8 @@ import {
type Config,
type UserTierId,
type AnsiOutput,
EDIT_TOOL_NAMES,
processRestorableToolCalls,
} from '@google/gemini-cli-core';
import type { RequestContext } from '@a2a-js/sdk/server';
import { type ExecutionEventBus } from '@a2a-js/sdk/server';
@@ -39,7 +42,8 @@ import type {
} from '@a2a-js/sdk';
import { v4 as uuidv4 } from 'uuid';
import { logger } from '../utils/logger.js';
import * as fs from 'node:fs';
import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import { CoderAgentEvent } from '../types.js';
import type {
CoderAgentMessage,
@@ -67,6 +71,9 @@ export class Task {
completedToolCalls: CompletedToolCall[];
skipFinalTrueAfterInlineEdit = false;
modelInfo?: string;
currentPromptId: string | undefined;
promptCount = 0;
autoExecute: boolean;
// For tool waiting logic
private pendingToolCalls: Map<string, string> = new Map(); //toolCallId --> status
@@ -81,6 +88,7 @@ export class Task {
contextId: string,
config: Config,
eventBus?: ExecutionEventBus,
autoExecute = false,
) {
this.id = id;
this.contextId = contextId;
@@ -92,6 +100,7 @@ export class Task {
this.eventBus = eventBus;
this.completedToolCalls = [];
this._resetToolCompletionPromise();
this.autoExecute = autoExecute;
this.config.setFallbackModelHandler(
// For a2a-server, we want to automatically switch to the fallback model
// for future requests without retrying the current one. The 'stop'
@@ -105,15 +114,16 @@ export class Task {
contextId: string,
config: Config,
eventBus?: ExecutionEventBus,
autoExecute?: boolean,
): Promise<Task> {
return new Task(id, contextId, config, eventBus);
return new Task(id, contextId, config, eventBus, autoExecute);
}
// Note: `getAllMCPServerStatuses` retrieves the status of all MCP servers for the entire
// process. This is not scoped to the individual task but reflects the global connection
// state managed within the @gemini-cli/core module.
async getMetadata(): Promise<TaskMetadata> {
const toolRegistry = await this.config.getToolRegistry();
const toolRegistry = this.config.getToolRegistry();
const mcpServers = this.config.getMcpClientManager()?.getMcpServers() || {};
const serverStatuses = getAllMCPServerStatuses();
const servers = Object.keys(mcpServers).map((serverName) => ({
@@ -390,10 +400,18 @@ export class Task {
}
});
if (this.config.getApprovalMode() === ApprovalMode.YOLO) {
logger.info('[Task] YOLO mode enabled. Auto-approving all tool calls.');
if (
this.autoExecute ||
this.config.getApprovalMode() === ApprovalMode.YOLO
) {
logger.info(
'[Task] ' +
(this.autoExecute ? '' : 'YOLO mode enabled. ') +
'Auto-approving all tool calls.',
);
toolCalls.forEach((tc: ToolCall) => {
if (tc.status === 'awaiting_approval' && tc.confirmationDetails) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
tc.confirmationDetails.onConfirm(ToolConfirmationOutcome.ProceedOnce);
this.pendingToolConfirmationDetails.delete(tc.request.callId);
}
@@ -435,7 +453,7 @@ export class Task {
outputUpdateHandler: this._schedulerOutputUpdate.bind(this),
onAllToolCallsComplete: this._schedulerAllToolCallsComplete.bind(this),
onToolCallsUpdate: this._schedulerToolCallsUpdate.bind(this),
getPreferredEditor: () => 'vscode',
getPreferredEditor: () => DEFAULT_GUI_EDITOR,
config: this.config,
});
return scheduler;
@@ -507,7 +525,7 @@ export class Task {
new_string: string,
): Promise<string> {
try {
const currentContent = fs.readFileSync(file_path, 'utf8');
const currentContent = await fs.readFile(file_path, 'utf8');
return this._applyReplacement(
currentContent,
old_string,
@@ -550,6 +568,44 @@ export class Task {
return;
}
// Set checkpoint file before any file modification tool executes
const restorableToolCalls = requests.filter((request) =>
EDIT_TOOL_NAMES.has(request.name),
);
if (restorableToolCalls.length > 0) {
const gitService = await this.config.getGitService();
if (gitService) {
const { checkpointsToWrite, toolCallToCheckpointMap, errors } =
await processRestorableToolCalls(
restorableToolCalls,
gitService,
this.geminiClient,
);
if (errors.length > 0) {
errors.forEach((error) => logger.error(error));
}
if (checkpointsToWrite.size > 0) {
const checkpointDir =
this.config.storage.getProjectTempCheckpointsDir();
await fs.mkdir(checkpointDir, { recursive: true });
for (const [fileName, content] of checkpointsToWrite) {
const filePath = path.join(checkpointDir, fileName);
await fs.writeFile(filePath, content);
}
}
for (const request of requests) {
const checkpoint = toolCallToCheckpointMap.get(request.callId);
if (checkpoint) {
request.checkpoint = checkpoint;
}
}
}
}
const updatedRequests = await Promise.all(
requests.map(async (request) => {
if (
@@ -691,8 +747,8 @@ export class Task {
return false;
}
const callId = part.data['callId'] as string;
const outcomeString = part.data['outcome'] as string;
const callId = part.data['callId'];
const outcomeString = part.data['outcome'];
let confirmationOutcome: ToolConfirmationOutcome | undefined;
if (outcomeString === 'proceed_once') {
@@ -746,7 +802,14 @@ export class Task {
} as ToolConfirmationPayload)
: undefined;
this.skipFinalTrueAfterInlineEdit = !!payload;
await confirmationDetails.onConfirm(confirmationOutcome, payload);
try {
await confirmationDetails.onConfirm(confirmationOutcome, payload);
} finally {
// Once confirmationDetails.onConfirm finishes (or fails) with a payload,
// reset skipFinalTrueAfterInlineEdit so that external callers receive
// their call has been completed.
this.skipFinalTrueAfterInlineEdit = false;
}
} else {
await confirmationDetails.onConfirm(confirmationOutcome);
}
@@ -818,6 +881,7 @@ export class Task {
} else {
parts = [response];
}
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.geminiClient.addHistory({
role: 'user',
parts,
@@ -856,11 +920,10 @@ export class Task {
};
// Set task state to working as we are about to call LLM
this.setTaskStateAndPublishUpdate('working', stateChange);
// TODO: Determine what it mean to have, then add a prompt ID.
yield* this.geminiClient.sendMessageStream(
llmParts,
aborted,
/*prompt_id*/ '',
completedToolCalls[0]?.request.prompt_id ?? '',
);
}
@@ -890,17 +953,18 @@ export class Task {
}
if (hasContentForLlm) {
this.currentPromptId =
this.config.getSessionId() + '########' + this.promptCount++;
logger.info('[Task] Sending new parts to LLM.');
const stateChange: StateChange = {
kind: CoderAgentEvent.StateChangeEvent,
};
// Set task state to working as we are about to call LLM
this.setTaskStateAndPublishUpdate('working', stateChange);
// TODO: Determine what it mean to have, then add a prompt ID.
yield* this.geminiClient.sendMessageStream(
llmParts,
aborted,
/*prompt_id*/ '',
this.currentPromptId,
);
} else if (anyConfirmationHandled) {
logger.info(
@@ -4,7 +4,10 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { debugLogger } from '@google/gemini-cli-core';
import { ExtensionsCommand } from './extensions.js';
import { InitCommand } from './init.js';
import { RestoreCommand } from './restore.js';
import type { Command } from './types.js';
class CommandRegistry {
@@ -12,11 +15,13 @@ class CommandRegistry {
constructor() {
this.register(new ExtensionsCommand());
this.register(new RestoreCommand());
this.register(new InitCommand());
}
register(command: Command) {
if (this.commands.has(command.name)) {
console.warn(`Command ${command.name} already registered. Skipping.`);
debugLogger.warn(`Command ${command.name} already registered. Skipping.`);
return;
}
@@ -6,7 +6,7 @@
import { describe, it, expect, vi } from 'vitest';
import { ExtensionsCommand, ListExtensionsCommand } from './extensions.js';
import type { Config } from '@google/gemini-cli-core';
import type { CommandContext } from './types.js';
const mockListExtensions = vi.hoisted(() => vi.fn());
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
@@ -42,14 +42,14 @@ describe('ExtensionsCommand', () => {
it('should default to listing extensions', async () => {
const command = new ExtensionsCommand();
const mockConfig = {} as Config;
const mockConfig = { config: {} } as CommandContext;
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);
expect(mockListExtensions).toHaveBeenCalledWith(mockConfig.config);
});
});
@@ -61,19 +61,19 @@ describe('ListExtensionsCommand', () => {
it('should call listExtensions with the provided config', async () => {
const command = new ListExtensionsCommand();
const mockConfig = {} as Config;
const mockConfig = { config: {} } as CommandContext;
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);
expect(mockListExtensions).toHaveBeenCalledWith(mockConfig.config);
});
it('should return a message when no extensions are installed', async () => {
const command = new ListExtensionsCommand();
const mockConfig = {} as Config;
const mockConfig = { config: {} } as CommandContext;
mockListExtensions.mockReturnValue([]);
const result = await command.execute(mockConfig, []);
@@ -82,6 +82,6 @@ describe('ListExtensionsCommand', () => {
name: 'extensions list',
data: 'No extensions installed.',
});
expect(mockListExtensions).toHaveBeenCalledWith(mockConfig);
expect(mockListExtensions).toHaveBeenCalledWith(mockConfig.config);
});
});

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