Compare commits

..

20 Commits

Author SHA1 Message Date
Keith Guerin 5765905f64 fix(cli): fix build error due to incorrect imports in ToolShared.tsx 2026-03-01 01:24:25 -08:00
Keith Guerin 5a36725f22 fix(cli): robust fix for shell scroll-jacking and focus issues 2026-03-01 01:13:27 -08:00
Keith Guerin 9d028db351 fix(cli): resolve scroll-jacking and enable click-to-focus for shell toolboxes 2026-03-01 01:05:28 -08:00
Keith Guerin 30e2820dc2 fix(cli): correct UIActions usage and fix ESLint warning 2026-03-01 00:58:43 -08:00
Keith Guerin ae04632a59 feat(cli): focus-based nested scrolling in shell toolboxes to prevent scroll-jacking 2026-03-01 00:51:12 -08:00
Abhi 703759cfae fix(cli): allow sub-agent confirmation requests in UI while preventing background flicker (#20722) 2026-03-01 02:39:25 +00:00
Sehoon Shon 0063581e47 feat(skills): add github-issue-creator skill (#20709) 2026-02-28 23:22:22 +00:00
Sehoon Shon 6757d4b5c5 fix(cli): resolve autoThemeSwitching when background hasn't changed but theme mismatches (#20706) 2026-02-28 23:22:10 +00:00
Sandy Tao a153ff587b refactor(core): Extract tool parameter names as constants (#20460) 2026-02-28 21:27:54 +00:00
N. Taylor Mullen cd3a8c3f07 fix(cli): reset themeManager between tests to ensure isolation (#20598) 2026-02-28 19:45:31 +00:00
kartik b2214a6676 fix: acp/zed race condition between MCP initialisation and prompt (#20205)
Signed-off-by: Kartik Angiras <angiraskartik@gmail.com>
2026-02-28 17:33:08 +00:00
gemini-cli-robot 6c65a2d813 Changelog for v0.32.0-preview.0 (#20627)
Co-authored-by: gemini-cli-robot <224641728+gemini-cli-robot@users.noreply.github.com>
2026-02-28 16:03:50 +00:00
Jagjeevan Kashid fae0639ba2 fix: use full paths for ACP diff payloads (#19539)
Signed-off-by: Jagjeevan Kashid <jagjeevandev97@gmail.com>
2026-02-28 15:54:44 +00:00
gemini-cli-robot 76f70d65ff Changelog for v0.31.0 (#20634)
Co-authored-by: gemini-cli-robot <224641728+gemini-cli-robot@users.noreply.github.com>
2026-02-28 03:45:07 +00:00
gemini-cli-robot fb6ff847dd chore/release: bump version to 0.33.0-nightly.20260228.1ca5c05d0 (#20644) 2026-02-28 02:13:48 +00:00
Gal Zahavi 1ca5c05d0d fix(github): use robot PAT for automated PRs to pass CLA check (#20641) 2026-02-28 01:13:58 +00:00
Gal Zahavi 0c6c9c6a62 chore(release): bump version to 0.33.0-nightly.20260227.ba149afa0 (#20637) 2026-02-28 00:51:22 +00:00
Sehoon Shon a1367e9cdd fix(core): parse raw ASCII buffer strings in Gaxios errors (#20626) 2026-02-27 23:57:32 +00:00
Tommaso Sciortino c89d4f9c6c docs: add Windows PowerShell equivalents for environments and scripting (#20333) 2026-02-27 23:41:47 +00:00
Aditya Sharma 08ee136132 docs: fix typo in installation documentation (#20153) 2026-02-27 23:04:52 +00:00
89 changed files with 2716 additions and 3572 deletions
@@ -0,0 +1,76 @@
---
name: github-issue-creator
description:
Use this skill when asked to create a GitHub issue. It handles different issue
types (bug, feature, etc.) using repository templates and ensures proper
labeling.
---
# GitHub Issue Creator
This skill guides the creation of high-quality GitHub issues that adhere to the
repository's standards and use the appropriate templates.
## Workflow
Follow these steps to create a GitHub issue:
1. **Identify Issue Type**: Determine if the request is a bug report, feature
request, or other category.
2. **Locate Template**: Search for issue templates in
`.github/ISSUE_TEMPLATE/`.
- `bug_report.yml`
- `feature_request.yml`
- `website_issue.yml`
- If no relevant YAML template is found, look for `.md` templates in the same
directory.
3. **Read Template**: Read the content of the identified template file to
understand the required fields.
4. **Draft Content**: Draft the issue title and body/fields.
- If using a YAML template (form), prepare values for each `id` defined in
the template.
- If using a Markdown template, follow its structure exactly.
- **Default Label**: Always include the `🔒 maintainer only` label unless the
user explicitly requests otherwise.
5. **Create Issue**: Use the `gh` CLI to create the issue.
- **CRITICAL:** To avoid shell escaping and formatting issues with
multi-line Markdown or complex text, ALWAYS write the description/body to
a temporary file first.
**For Markdown Templates or Simple Body:**
```bash
# 1. Write the drafted content to a temporary file
# 2. Create the issue using the --body-file flag
gh issue create --title "Succinct title" --body-file <temp_file_path> --label "🔒 maintainer only"
# 3. Remove the temporary file
rm <temp_file_path>
```
**For YAML Templates (Forms):**
While `gh issue create` supports `--body-file`, YAML forms usually expect
key-value pairs via flags if you want to bypass the interactive prompt.
However, the most reliable non-interactive way to ensure formatting is
preserved for long text fields is to use the `--body` or `--body-file` if the
form has been converted to a standard body, OR to use the `--field` flags
for YAML forms.
*Note: For the `gemini-cli` repository which uses YAML forms, you can often
submit the content as a single body if a specific field-based submission is
not required by the automation.*
6. **Verify**: Confirm the issue was created successfully and provide the link
to the user.
## Principles
- **Clarity**: Titles should be descriptive and follow project conventions.
- **Defensive Formatting**: Always use temporary files with `--body-file` to
prevent newline and special character issues.
- **Maintainer Priority**: Default to internal/maintainer labels to keep the
backlog organized.
- **Completeness**: Provide all requested information (e.g., version info,
reproduction steps).
+1 -1
View File
@@ -145,7 +145,7 @@ jobs:
branch-name: 'release/${{ steps.nightly_version.outputs.RELEASE_TAG }}'
pr-title: 'chore/release: bump version to ${{ steps.nightly_version.outputs.RELEASE_VERSION }}'
pr-body: 'Automated version bump for nightly release.'
github-token: '${{ secrets.GITHUB_TOKEN }}'
github-token: '${{ secrets.GEMINI_CLI_ROBOT_GITHUB_PAT }}'
dry-run: '${{ steps.vars.outputs.is_dry_run }}'
working-directory: './release'
+2 -1
View File
@@ -335,6 +335,7 @@ jobs:
name: 'Create Nightly PR'
needs: ['publish-stable', 'calculate-versions']
runs-on: 'ubuntu-latest'
environment: "${{ github.event.inputs.environment || 'prod' }}"
permissions:
contents: 'write'
pull-requests: 'write'
@@ -397,7 +398,7 @@ jobs:
branch-name: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
pr-title: 'chore(release): bump version to ${{ needs.calculate-versions.outputs.NEXT_NIGHTLY_VERSION }}'
pr-body: 'Automated version bump to prepare for the next nightly release.'
github-token: '${{ secrets.GITHUB_TOKEN }}'
github-token: '${{ secrets.GEMINI_CLI_ROBOT_GITHUB_PAT }}'
dry-run: '${{ github.event.inputs.dry_run }}'
- name: 'Create Issue on Failure'
+21
View File
@@ -18,6 +18,27 @@ on GitHub.
| [Preview](preview.md) | Experimental features ready for early feedback. |
| [Stable](latest.md) | Stable, recommended for general use. |
## Announcements: v0.31.0 - 2026-02-27
- **Gemini 3.1 Pro Preview:** Gemini CLI now supports the new Gemini 3.1 Pro
Preview model
([#19676](https://github.com/google-gemini/gemini-cli/pull/19676) by
@sehoon38).
- **Experimental Browser Agent:** We've introduced a new experimental browser
agent to interact with web pages
([#19284](https://github.com/google-gemini/gemini-cli/pull/19284) by
@gsquared94).
- **Policy Engine Updates:** The policy engine now supports project-level
policies, MCP server wildcards, and tool annotation matching
([#18682](https://github.com/google-gemini/gemini-cli/pull/18682) by
@Abhijit-2592,
[#20024](https://github.com/google-gemini/gemini-cli/pull/20024) by @jerop).
- **Web Fetch Improvements:** We've implemented an experimental direct web fetch
feature and added rate limiting to mitigate DDoS risks
([#19557](https://github.com/google-gemini/gemini-cli/pull/19557) by @mbleigh,
[#19567](https://github.com/google-gemini/gemini-cli/pull/19567) by
@mattKorwel).
## Announcements: v0.30.0 - 2026-02-25
- **SDK & Custom Skills:** Introduced the initial SDK package, enabling dynamic
+389 -310
View File
@@ -1,4 +1,4 @@
# Latest stable release: v0.30.1
# Latest stable release: v0.31.0
Released: February 27, 2026
@@ -11,326 +11,405 @@ npm install -g @google/gemini-cli
## Highlights
- **SDK & Custom Skills**: Introduced the initial SDK package, dynamic system
instructions, `SessionContext` for SDK tool calls, and support for custom
skills.
- **Policy Engine Enhancements**: Added a `--policy` flag for user-defined
policies, strict seatbelt profiles, and transitioned away from
`--allowed-tools`.
- **UI & Themes**: Introduced a generic searchable list for settings and
extensions, added Solarized Dark and Light themes, text wrapping capabilities
to markdown tables, and a clean UI toggle prototype.
- **Vim Support & Ctrl-Z**: Improved Vim support to provide a more complete
experience and added support for Ctrl-Z suspension.
- **Plan Mode & Tools**: Plan Mode now supports project exploration without
planning and skills can be enabled in plan mode. Tool output masking is
enabled by default, and core tool definitions have been centralized.
- **Gemini 3.1 Pro Preview:** Gemini CLI now supports the new Gemini 3.1 Pro
Preview model.
- **Experimental Browser Agent:** We've introduced a new experimental browser
agent to directly interact with web pages and retrieve context.
- **Policy Engine Updates:** The policy engine has been expanded to support
project-level policies, MCP server wildcards, and tool annotation matching,
providing greater control over tool executions.
- **Web Fetch Enhancements:** A new experimental direct web fetch tool has been
implemented, alongside rate-limiting features for enhanced security.
- **Improved Plan Mode:** Plan Mode now includes support for custom storage
directories, automatic model switching, and summarizing work after execution.
## What's Changed
- fix(patch): cherry-pick 58df1c6 to release/v0.30.0-pr-20374 [CONFLICTS] by
@gemini-cli-robot in
[#20567](https://github.com/google-gemini/gemini-cli/pull/20567)
- feat(ux): added text wrapping capabilities to markdown tables by @devr0306 in
[#18240](https://github.com/google-gemini/gemini-cli/pull/18240)
- Revert "fix(mcp): ensure MCP transport is closed to prevent memory leaks" by
@skeshive in [#18771](https://github.com/google-gemini/gemini-cli/pull/18771)
- chore(release): bump version to 0.30.0-nightly.20260210.a2174751d by
@gemini-cli-robot in
[#18772](https://github.com/google-gemini/gemini-cli/pull/18772)
- chore: cleanup unused and add unlisted dependencies in packages/core by
@adamfweidman in
[#18762](https://github.com/google-gemini/gemini-cli/pull/18762)
- chore(core): update activate_skill prompt verbiage to be more direct by
@NTaylorMullen in
[#18605](https://github.com/google-gemini/gemini-cli/pull/18605)
- Add autoconfigure memory usage setting to the dialog by @jacob314 in
[#18510](https://github.com/google-gemini/gemini-cli/pull/18510)
- fix(core): prevent race condition in policy persistence by @braddux in
[#18506](https://github.com/google-gemini/gemini-cli/pull/18506)
- fix(evals): prevent false positive in hierarchical memory test by
@Abhijit-2592 in
[#18777](https://github.com/google-gemini/gemini-cli/pull/18777)
- test(evals): mark all `save_memory` evals as `USUALLY_PASSES` due to
unreliability by @jerop in
[#18786](https://github.com/google-gemini/gemini-cli/pull/18786)
- feat(cli): add setting to hide shortcuts hint UI by @LyalinDotCom in
[#18562](https://github.com/google-gemini/gemini-cli/pull/18562)
- feat(core): formalize 5-phase sequential planning workflow by @jerop in
[#18759](https://github.com/google-gemini/gemini-cli/pull/18759)
- Introduce limits for search results. by @gundermanc in
[#18767](https://github.com/google-gemini/gemini-cli/pull/18767)
- fix(cli): allow closing debug console after auto-open via flicker by
- Use ranged reads and limited searches and fuzzy editing improvements by
@gundermanc in
[#19240](https://github.com/google-gemini/gemini-cli/pull/19240)
- Fix bottom border color by @jacob314 in
[#19266](https://github.com/google-gemini/gemini-cli/pull/19266)
- Release note generator fix by @g-samroberts in
[#19363](https://github.com/google-gemini/gemini-cli/pull/19363)
- test(evals): add behavioral tests for tool output masking by @NTaylorMullen in
[#19172](https://github.com/google-gemini/gemini-cli/pull/19172)
- docs: clarify preflight instructions in GEMINI.md by @NTaylorMullen in
[#19377](https://github.com/google-gemini/gemini-cli/pull/19377)
- feat(cli): add gemini --resume hint on exit by @Mag1ck in
[#16285](https://github.com/google-gemini/gemini-cli/pull/16285)
- fix: optimize height calculations for ask_user dialog by @jackwotherspoon in
[#19017](https://github.com/google-gemini/gemini-cli/pull/19017)
- feat(cli): add Alt+D for forward word deletion by @scidomino in
[#19300](https://github.com/google-gemini/gemini-cli/pull/19300)
- Disable failing eval test by @chrstnb in
[#19455](https://github.com/google-gemini/gemini-cli/pull/19455)
- fix(cli): support legacy onConfirm callback in ToolActionsContext by
@SandyTao520 in
[#18795](https://github.com/google-gemini/gemini-cli/pull/18795)
- feat(masking): enable tool output masking by default by @abhipatel12 in
[#18564](https://github.com/google-gemini/gemini-cli/pull/18564)
- perf(ui): optimize table rendering by memoizing styled characters by @devr0306
in [#18770](https://github.com/google-gemini/gemini-cli/pull/18770)
- feat: multi-line text answers in ask-user tool by @jackwotherspoon in
[#18741](https://github.com/google-gemini/gemini-cli/pull/18741)
- perf(cli): truncate large debug logs and limit message history by @mattKorwel
in [#18663](https://github.com/google-gemini/gemini-cli/pull/18663)
- fix(core): complete MCP discovery when configured servers are skipped by
[#19369](https://github.com/google-gemini/gemini-cli/pull/19369)
- chore(deps): bump tar from 7.5.7 to 7.5.8 by @.github/dependabot.yml[bot] in
[#19367](https://github.com/google-gemini/gemini-cli/pull/19367)
- fix(plan): allow safe fallback when experiment setting for plan is not enabled
but approval mode at startup is plan by @Adib234 in
[#19439](https://github.com/google-gemini/gemini-cli/pull/19439)
- Add explicit color-convert dependency by @chrstnb in
[#19460](https://github.com/google-gemini/gemini-cli/pull/19460)
- feat(devtools): migrate devtools package into monorepo by @SandyTao520 in
[#18936](https://github.com/google-gemini/gemini-cli/pull/18936)
- fix(core): clarify plan mode constraints and exit mechanism by @jerop in
[#19438](https://github.com/google-gemini/gemini-cli/pull/19438)
- feat(cli): add macOS run-event notifications (interactive only) by
@LyalinDotCom in
[#18586](https://github.com/google-gemini/gemini-cli/pull/18586)
- fix(core): cache CLI version to ensure consistency during sessions by
@sehoon38 in [#18793](https://github.com/google-gemini/gemini-cli/pull/18793)
- fix(cli): resolve double rendering in shpool and address vscode lint warnings
by @braddux in
[#18704](https://github.com/google-gemini/gemini-cli/pull/18704)
- feat(plan): document and validate Plan Mode policy overrides by @jerop in
[#18825](https://github.com/google-gemini/gemini-cli/pull/18825)
- Fix pressing any key to exit select mode. by @jacob314 in
[#18421](https://github.com/google-gemini/gemini-cli/pull/18421)
- fix(cli): update F12 behavior to only open drawer if browser fails by
[#19056](https://github.com/google-gemini/gemini-cli/pull/19056)
- Changelog for v0.29.0 by @gemini-cli-robot in
[#19361](https://github.com/google-gemini/gemini-cli/pull/19361)
- fix(ui): preventing empty history items from being added by @devr0306 in
[#19014](https://github.com/google-gemini/gemini-cli/pull/19014)
- Changelog for v0.30.0-preview.0 by @gemini-cli-robot in
[#19364](https://github.com/google-gemini/gemini-cli/pull/19364)
- feat(core): add support for MCP progress updates by @NTaylorMullen in
[#19046](https://github.com/google-gemini/gemini-cli/pull/19046)
- fix(core): ensure directory exists before writing conversation file by
@godwiniheuwa in
[#18429](https://github.com/google-gemini/gemini-cli/pull/18429)
- fix(ui): move margin from top to bottom in ToolGroupMessage by @imadraude in
[#17198](https://github.com/google-gemini/gemini-cli/pull/17198)
- fix(cli): treat unknown slash commands as regular input instead of showing
error by @skyvanguard in
[#17393](https://github.com/google-gemini/gemini-cli/pull/17393)
- feat(core): experimental in-progress steering hints (2 of 2) by @joshualitt in
[#19307](https://github.com/google-gemini/gemini-cli/pull/19307)
- docs(plan): add documentation for plan mode command by @Adib234 in
[#19467](https://github.com/google-gemini/gemini-cli/pull/19467)
- fix(core): ripgrep fails when pattern looks like ripgrep flag by @syvb in
[#18858](https://github.com/google-gemini/gemini-cli/pull/18858)
- fix(cli): disable auto-completion on Shift+Tab to preserve mode cycling by
@NTaylorMullen in
[#19451](https://github.com/google-gemini/gemini-cli/pull/19451)
- use issuer instead of authorization_endpoint for oauth discovery by
@garrettsparks in
[#17332](https://github.com/google-gemini/gemini-cli/pull/17332)
- feat(cli): include `/dir add` directories in @ autocomplete suggestions by
@jasmeetsb in [#19246](https://github.com/google-gemini/gemini-cli/pull/19246)
- feat(admin): Admin settings should only apply if adminControlsApplicable =
true and fetch errors should be fatal by @skeshive in
[#19453](https://github.com/google-gemini/gemini-cli/pull/19453)
- Format strict-development-rules command by @g-samroberts in
[#19484](https://github.com/google-gemini/gemini-cli/pull/19484)
- feat(core): centralize compatibility checks and add TrueColor detection by
@spencer426 in
[#19478](https://github.com/google-gemini/gemini-cli/pull/19478)
- Remove unused files and update index and sidebar. by @g-samroberts in
[#19479](https://github.com/google-gemini/gemini-cli/pull/19479)
- Migrate core render util to use xterm.js as part of the rendering loop. by
@jacob314 in [#19044](https://github.com/google-gemini/gemini-cli/pull/19044)
- Changelog for v0.30.0-preview.1 by @gemini-cli-robot in
[#19496](https://github.com/google-gemini/gemini-cli/pull/19496)
- build: replace deprecated built-in punycode with userland package by @jacob314
in [#19502](https://github.com/google-gemini/gemini-cli/pull/19502)
- Speculative fixes to try to fix react error. by @jacob314 in
[#19508](https://github.com/google-gemini/gemini-cli/pull/19508)
- fix spacing by @jacob314 in
[#19494](https://github.com/google-gemini/gemini-cli/pull/19494)
- fix(core): ensure user rejections update tool outcome for telemetry by
@abhiasap in [#18982](https://github.com/google-gemini/gemini-cli/pull/18982)
- fix(acp): Initialize config (#18897) by @Mervap in
[#18898](https://github.com/google-gemini/gemini-cli/pull/18898)
- fix(core): add error logging for IDE fetch failures by @yuvrajangadsingh in
[#17981](https://github.com/google-gemini/gemini-cli/pull/17981)
- feat(acp): support set_mode interface (#18890) by @Mervap in
[#18891](https://github.com/google-gemini/gemini-cli/pull/18891)
- fix(core): robust workspace-based IDE connection discovery by @ehedlund in
[#18443](https://github.com/google-gemini/gemini-cli/pull/18443)
- Deflake windows tests. by @jacob314 in
[#19511](https://github.com/google-gemini/gemini-cli/pull/19511)
- Fix: Avoid tool confirmation timeout when no UI listeners are present by
@pdHaku0 in [#17955](https://github.com/google-gemini/gemini-cli/pull/17955)
- format md file by @scidomino in
[#19474](https://github.com/google-gemini/gemini-cli/pull/19474)
- feat(cli): add experimental.useOSC52Copy setting by @scidomino in
[#19488](https://github.com/google-gemini/gemini-cli/pull/19488)
- feat(cli): replace loading phrases boolean with enum setting by @LyalinDotCom
in [#19347](https://github.com/google-gemini/gemini-cli/pull/19347)
- Update skill to adjust for generated results. by @g-samroberts in
[#19500](https://github.com/google-gemini/gemini-cli/pull/19500)
- Fix message too large issue. by @gundermanc in
[#19499](https://github.com/google-gemini/gemini-cli/pull/19499)
- fix(core): prevent duplicate tool approval entries in auto-saved.toml by
@Abhijit-2592 in
[#19487](https://github.com/google-gemini/gemini-cli/pull/19487)
- fix(core): resolve crash in ClearcutLogger when os.cpus() is empty by @Adib234
in [#19555](https://github.com/google-gemini/gemini-cli/pull/19555)
- chore(core): improve encapsulation and remove unused exports by @adamfweidman
in [#19556](https://github.com/google-gemini/gemini-cli/pull/19556)
- Revert "Add generic searchable list to back settings and extensions (… by
@chrstnb in [#19434](https://github.com/google-gemini/gemini-cli/pull/19434)
- fix(core): improve error type extraction for telemetry by @yunaseoul in
[#19565](https://github.com/google-gemini/gemini-cli/pull/19565)
- fix: remove extra padding in Composer by @jackwotherspoon in
[#19529](https://github.com/google-gemini/gemini-cli/pull/19529)
- feat(plan): support configuring custom plans storage directory by @jerop in
[#19577](https://github.com/google-gemini/gemini-cli/pull/19577)
- Migrate files to resource or references folder. by @g-samroberts in
[#19503](https://github.com/google-gemini/gemini-cli/pull/19503)
- feat(policy): implement project-level policy support by @Abhijit-2592 in
[#18682](https://github.com/google-gemini/gemini-cli/pull/18682)
- feat(core): Implement parallel FC for read only tools. by @joshualitt in
[#18791](https://github.com/google-gemini/gemini-cli/pull/18791)
- chore(skills): adds pr-address-comments skill to work on PR feedback by
@mbleigh in [#19576](https://github.com/google-gemini/gemini-cli/pull/19576)
- refactor(sdk): introduce session-based architecture by @mbleigh in
[#19180](https://github.com/google-gemini/gemini-cli/pull/19180)
- fix(ci): add fallback JSON extraction to issue triage workflow by @bdmorgan in
[#19593](https://github.com/google-gemini/gemini-cli/pull/19593)
- feat(core): refine Edit and WriteFile tool schemas for Gemini 3 by
@SandyTao520 in
[#18829](https://github.com/google-gemini/gemini-cli/pull/18829)
- feat(plan): allow skills to be enabled in plan mode by @Adib234 in
[#18817](https://github.com/google-gemini/gemini-cli/pull/18817)
- docs(plan): add documentation for plan mode tools by @jerop in
[#18827](https://github.com/google-gemini/gemini-cli/pull/18827)
- Remove experimental note in extension settings docs by @chrstnb in
[#18822](https://github.com/google-gemini/gemini-cli/pull/18822)
- Update prompt and grep tool definition to limit context size by @gundermanc in
[#18780](https://github.com/google-gemini/gemini-cli/pull/18780)
- docs(plan): add `ask_user` tool documentation by @jerop in
[#18830](https://github.com/google-gemini/gemini-cli/pull/18830)
- Revert unintended credentials exposure by @Adib234 in
[#18840](https://github.com/google-gemini/gemini-cli/pull/18840)
- feat(core): update internal utility models to Gemini 3 by @SandyTao520 in
[#18773](https://github.com/google-gemini/gemini-cli/pull/18773)
- feat(a2a): add value-resolver for auth credential resolution by @adamfweidman
in [#18653](https://github.com/google-gemini/gemini-cli/pull/18653)
- Removed getPlainTextLength by @devr0306 in
[#18848](https://github.com/google-gemini/gemini-cli/pull/18848)
- More grep prompt tweaks by @gundermanc in
[#18846](https://github.com/google-gemini/gemini-cli/pull/18846)
- refactor(cli): Reactive useSettingsStore hook by @psinha40898 in
[#14915](https://github.com/google-gemini/gemini-cli/pull/14915)
- fix(mcp): Ensure that stdio MCP server execution has the `GEMINI_CLI=1` env
variable populated. by @richieforeman in
[#18832](https://github.com/google-gemini/gemini-cli/pull/18832)
- fix(core): improve headless mode detection for flags and query args by @galz10
in [#18855](https://github.com/google-gemini/gemini-cli/pull/18855)
- refactor(cli): simplify UI and remove legacy inline tool confirmation logic by
@abhipatel12 in
[#18566](https://github.com/google-gemini/gemini-cli/pull/18566)
- feat(cli): deprecate --allowed-tools and excludeTools in favor of policy
engine by @Abhijit-2592 in
[#18508](https://github.com/google-gemini/gemini-cli/pull/18508)
- fix(workflows): improve maintainer detection for automated PR actions by
@bdmorgan in [#18869](https://github.com/google-gemini/gemini-cli/pull/18869)
- refactor(cli): consolidate useToolScheduler and delete legacy implementation
by @abhipatel12 in
[#18567](https://github.com/google-gemini/gemini-cli/pull/18567)
- Update changelog for v0.28.0 and v0.29.0-preview0 by @g-samroberts in
[#18819](https://github.com/google-gemini/gemini-cli/pull/18819)
- fix(core): ensure sub-agents are registered regardless of tools.allowed by
[#19476](https://github.com/google-gemini/gemini-cli/pull/19476)
- Changelog for v0.30.0-preview.3 by @gemini-cli-robot in
[#19585](https://github.com/google-gemini/gemini-cli/pull/19585)
- fix(plan): exclude EnterPlanMode tool from YOLO mode by @Adib234 in
[#19570](https://github.com/google-gemini/gemini-cli/pull/19570)
- chore: resolve build warnings and update dependencies by @mattKorwel in
[#18880](https://github.com/google-gemini/gemini-cli/pull/18880)
- feat(ui): add source indicators to slash commands by @ehedlund in
[#18839](https://github.com/google-gemini/gemini-cli/pull/18839)
- docs: refine Plan Mode documentation structure and workflow by @jerop in
[#19644](https://github.com/google-gemini/gemini-cli/pull/19644)
- Docs: Update release information regarding Gemini 3.1 by @jkcinouye in
[#19568](https://github.com/google-gemini/gemini-cli/pull/19568)
- fix(security): rate limit web_fetch tool to mitigate DDoS via prompt injection
by @mattKorwel in
[#19567](https://github.com/google-gemini/gemini-cli/pull/19567)
- Add initial implementation of /extensions explore command by @chrstnb in
[#19029](https://github.com/google-gemini/gemini-cli/pull/19029)
- fix: use discoverOAuthFromWWWAuthenticate for reactive OAuth flow (#18760) by
@maximus12793 in
[#19038](https://github.com/google-gemini/gemini-cli/pull/19038)
- Search updates by @alisa-alisa in
[#19482](https://github.com/google-gemini/gemini-cli/pull/19482)
- feat(cli): add support for numpad SS3 sequences by @scidomino in
[#19659](https://github.com/google-gemini/gemini-cli/pull/19659)
- feat(cli): enhance folder trust with configuration discovery and security
warnings by @galz10 in
[#19492](https://github.com/google-gemini/gemini-cli/pull/19492)
- feat(ui): improve startup warnings UX with dismissal and show-count limits by
@spencer426 in
[#19584](https://github.com/google-gemini/gemini-cli/pull/19584)
- feat(a2a): Add API key authentication provider by @adamfweidman in
[#19548](https://github.com/google-gemini/gemini-cli/pull/19548)
- Send accepted/removed lines with ACCEPT_FILE telemetry. by @gundermanc in
[#19670](https://github.com/google-gemini/gemini-cli/pull/19670)
- feat(models): support Gemini 3.1 Pro Preview and fixes by @sehoon38 in
[#19676](https://github.com/google-gemini/gemini-cli/pull/19676)
- feat(plan): enforce read-only constraints in Plan Mode by @mattKorwel in
[#19433](https://github.com/google-gemini/gemini-cli/pull/19433)
- fix(cli): allow perfect match @scripts/test-windows-paths.js completions to
submit on Enter by @spencer426 in
[#19562](https://github.com/google-gemini/gemini-cli/pull/19562)
- fix(core): treat 503 Service Unavailable as retryable quota error by @sehoon38
in [#19642](https://github.com/google-gemini/gemini-cli/pull/19642)
- Update sidebar.json for to allow top nav tabs. by @g-samroberts in
[#19595](https://github.com/google-gemini/gemini-cli/pull/19595)
- security: strip deceptive Unicode characters from terminal output by @ehedlund
in [#19026](https://github.com/google-gemini/gemini-cli/pull/19026)
- Fixes 'input.on' is not a function error in Gemini CLI by @gundermanc in
[#19691](https://github.com/google-gemini/gemini-cli/pull/19691)
- Revert "feat(ui): add source indicators to slash commands" by @ehedlund in
[#19695](https://github.com/google-gemini/gemini-cli/pull/19695)
- security: implement deceptive URL detection and disclosure in tool
confirmations by @ehedlund in
[#19288](https://github.com/google-gemini/gemini-cli/pull/19288)
- fix(core): restore auth consent in headless mode and add unit tests by
@ehedlund in [#19689](https://github.com/google-gemini/gemini-cli/pull/19689)
- Fix unsafe assertions in code_assist folder. by @gundermanc in
[#19706](https://github.com/google-gemini/gemini-cli/pull/19706)
- feat(cli): make JetBrains warning more specific by @jacob314 in
[#19687](https://github.com/google-gemini/gemini-cli/pull/19687)
- fix(cli): extensions dialog UX polish by @jacob314 in
[#19685](https://github.com/google-gemini/gemini-cli/pull/19685)
- fix(cli): use getDisplayString for manual model selection in dialog by
@sehoon38 in [#19726](https://github.com/google-gemini/gemini-cli/pull/19726)
- feat(policy): repurpose "Always Allow" persistence to workspace level by
@Abhijit-2592 in
[#19707](https://github.com/google-gemini/gemini-cli/pull/19707)
- fix(cli): re-enable CLI banner by @sehoon38 in
[#19741](https://github.com/google-gemini/gemini-cli/pull/19741)
- Disallow and suppress unsafe assignment by @gundermanc in
[#19736](https://github.com/google-gemini/gemini-cli/pull/19736)
- feat(core): migrate read_file to 1-based start_line/end_line parameters by
@adamfweidman in
[#19526](https://github.com/google-gemini/gemini-cli/pull/19526)
- feat(cli): improve CTRL+O experience for both standard and alternate screen
buffer (ASB) modes by @jwhelangoog in
[#19010](https://github.com/google-gemini/gemini-cli/pull/19010)
- Utilize pipelining of grep_search -> read_file to eliminate turns by
@gundermanc in
[#19574](https://github.com/google-gemini/gemini-cli/pull/19574)
- refactor(core): remove unsafe type assertions in error utils (Phase 1.1) by
@mattKorwel in
[#18870](https://github.com/google-gemini/gemini-cli/pull/18870)
- Show notification when there's a conflict with an extensions command by
@chrstnb in [#17890](https://github.com/google-gemini/gemini-cli/pull/17890)
- fix(cli): dismiss '?' shortcuts help on hotkeys and active states by
@LyalinDotCom in
[#18583](https://github.com/google-gemini/gemini-cli/pull/18583)
- fix(core): prioritize conditional policy rules and harden Plan Mode by
@Abhijit-2592 in
[#18882](https://github.com/google-gemini/gemini-cli/pull/18882)
- feat(core): refine Plan Mode system prompt for agentic execution by
[#19750](https://github.com/google-gemini/gemini-cli/pull/19750)
- Disallow unsafe returns. by @gundermanc in
[#19767](https://github.com/google-gemini/gemini-cli/pull/19767)
- fix(cli): filter subagent sessions from resume history by @abhipatel12 in
[#19698](https://github.com/google-gemini/gemini-cli/pull/19698)
- chore(lint): fix lint errors seen when running npm run lint by @abhipatel12 in
[#19844](https://github.com/google-gemini/gemini-cli/pull/19844)
- feat(core): remove unnecessary login verbiage from Code Assist auth by
@NTaylorMullen in
[#18799](https://github.com/google-gemini/gemini-cli/pull/18799)
- feat(plan): create metrics for usage of `AskUser` tool by @Adib234 in
[#18820](https://github.com/google-gemini/gemini-cli/pull/18820)
- feat(cli): support Ctrl-Z suspension by @scidomino in
[#18931](https://github.com/google-gemini/gemini-cli/pull/18931)
- fix(github-actions): use robot PAT for release creation to trigger release
notes by @SandyTao520 in
[#18794](https://github.com/google-gemini/gemini-cli/pull/18794)
- feat: add strict seatbelt profiles and remove unusable closed profiles by
[#19861](https://github.com/google-gemini/gemini-cli/pull/19861)
- fix(plan): time share by approval mode dashboard reporting negative time
shares by @Adib234 in
[#19847](https://github.com/google-gemini/gemini-cli/pull/19847)
- fix(core): allow any preview model in quota access check by @bdmorgan in
[#19867](https://github.com/google-gemini/gemini-cli/pull/19867)
- fix(core): prevent omission placeholder deletions in replace/write_file by
@nsalerni in [#19870](https://github.com/google-gemini/gemini-cli/pull/19870)
- fix(core): add uniqueness guard to edit tool by @Shivangisharma4 in
[#19890](https://github.com/google-gemini/gemini-cli/pull/19890)
- refactor(config): remove enablePromptCompletion from settings by @sehoon38 in
[#19974](https://github.com/google-gemini/gemini-cli/pull/19974)
- refactor(core): move session conversion logic to core by @abhipatel12 in
[#19972](https://github.com/google-gemini/gemini-cli/pull/19972)
- Fix: Persist manual model selection on restart #19864 by @Nixxx19 in
[#19891](https://github.com/google-gemini/gemini-cli/pull/19891)
- fix(core): increase default retry attempts and add quota error backoff by
@sehoon38 in [#19949](https://github.com/google-gemini/gemini-cli/pull/19949)
- feat(core): add policy chain support for Gemini 3.1 by @sehoon38 in
[#19991](https://github.com/google-gemini/gemini-cli/pull/19991)
- Updates command reference and /stats command. by @g-samroberts in
[#19794](https://github.com/google-gemini/gemini-cli/pull/19794)
- Fix for silent failures in non-interactive mode by @owenofbrien in
[#19905](https://github.com/google-gemini/gemini-cli/pull/19905)
- fix(plan): allow plan mode writes on Windows and fix prompt paths by @Adib234
in [#19658](https://github.com/google-gemini/gemini-cli/pull/19658)
- fix(core): prevent OAuth server crash on unexpected requests by @reyyanxahmed
in [#19668](https://github.com/google-gemini/gemini-cli/pull/19668)
- feat: Map tool kinds to explicit ACP.ToolKind values and update test … by
@sripasg in [#19547](https://github.com/google-gemini/gemini-cli/pull/19547)
- chore: restrict gemini-automted-issue-triage to only allow echo by @galz10 in
[#20047](https://github.com/google-gemini/gemini-cli/pull/20047)
- Allow ask headers longer than 16 chars by @scidomino in
[#20041](https://github.com/google-gemini/gemini-cli/pull/20041)
- fix(core): prevent state corruption in McpClientManager during collis by @h30s
in [#19782](https://github.com/google-gemini/gemini-cli/pull/19782)
- fix(bundling): copy devtools package to bundle for runtime resolution by
@SandyTao520 in
[#18876](https://github.com/google-gemini/gemini-cli/pull/18876)
- chore: cleanup unused and add unlisted dependencies in packages/a2a-server by
@adamfweidman in
[#18916](https://github.com/google-gemini/gemini-cli/pull/18916)
- fix(plan): isolate plan files per session by @Adib234 in
[#18757](https://github.com/google-gemini/gemini-cli/pull/18757)
- fix: character truncation in raw markdown mode by @jackwotherspoon in
[#18938](https://github.com/google-gemini/gemini-cli/pull/18938)
- feat(cli): prototype clean UI toggle and minimal-mode bleed-through by
@LyalinDotCom in
[#18683](https://github.com/google-gemini/gemini-cli/pull/18683)
- ui(polish) blend background color with theme by @jacob314 in
[#18802](https://github.com/google-gemini/gemini-cli/pull/18802)
- Add generic searchable list to back settings and extensions by @chrstnb in
[#18838](https://github.com/google-gemini/gemini-cli/pull/18838)
- feat(ui): align `AskUser` color scheme with UX spec by @jerop in
[#18943](https://github.com/google-gemini/gemini-cli/pull/18943)
- Hide AskUser tool validation errors from UI (agent self-corrects) by @jerop in
[#18954](https://github.com/google-gemini/gemini-cli/pull/18954)
- bug(cli) fix flicker due to AppContainer continuous initialization by
@jacob314 in [#18958](https://github.com/google-gemini/gemini-cli/pull/18958)
- feat(admin): Add admin controls documentation by @skeshive in
[#18644](https://github.com/google-gemini/gemini-cli/pull/18644)
- feat(cli): disable ctrl-s shortcut outside of alternate buffer mode by
@jacob314 in [#18887](https://github.com/google-gemini/gemini-cli/pull/18887)
- fix(vim): vim support that feels (more) complete by @ppgranger in
[#18755](https://github.com/google-gemini/gemini-cli/pull/18755)
- feat(policy): add --policy flag for user defined policies by @allenhutchison
in [#18500](https://github.com/google-gemini/gemini-cli/pull/18500)
- Update installation guide by @g-samroberts in
[#18823](https://github.com/google-gemini/gemini-cli/pull/18823)
- refactor(core): centralize tool definitions (Group 1: replace, search, grep)
by @aishaneeshah in
[#18944](https://github.com/google-gemini/gemini-cli/pull/18944)
- refactor(cli): finalize event-driven transition and remove interaction bridge
by @abhipatel12 in
[#18569](https://github.com/google-gemini/gemini-cli/pull/18569)
- Fix drag and drop escaping by @scidomino in
[#18965](https://github.com/google-gemini/gemini-cli/pull/18965)
- feat(sdk): initial package bootstrap for SDK by @mbleigh in
[#18861](https://github.com/google-gemini/gemini-cli/pull/18861)
- feat(sdk): implements SessionContext for SDK tool calls by @mbleigh in
[#18862](https://github.com/google-gemini/gemini-cli/pull/18862)
- fix(plan): make question type required in AskUser tool by @Adib234 in
[#18959](https://github.com/google-gemini/gemini-cli/pull/18959)
- fix(core): ensure --yolo does not force headless mode by @NTaylorMullen in
[#18976](https://github.com/google-gemini/gemini-cli/pull/18976)
- refactor(core): adopt `CoreToolCallStatus` enum for type safety by @jerop in
[#18998](https://github.com/google-gemini/gemini-cli/pull/18998)
- Enable in-CLI extension management commands for team by @chrstnb in
[#18957](https://github.com/google-gemini/gemini-cli/pull/18957)
- Adjust lint rules to avoid unnecessary warning. by @scidomino in
[#18970](https://github.com/google-gemini/gemini-cli/pull/18970)
- fix(vscode): resolve unsafe type assertion lint errors by @ehedlund in
[#19006](https://github.com/google-gemini/gemini-cli/pull/19006)
- Remove unnecessary eslint config file by @scidomino in
[#19015](https://github.com/google-gemini/gemini-cli/pull/19015)
- fix(core): Prevent loop detection false positives on lists with long shared
prefixes by @SandyTao520 in
[#18975](https://github.com/google-gemini/gemini-cli/pull/18975)
- feat(core): fallback to chat-base when using unrecognized models for chat by
@SandyTao520 in
[#19016](https://github.com/google-gemini/gemini-cli/pull/19016)
- docs: fix inconsistent commandRegex example in policy engine by @NTaylorMullen
in [#19027](https://github.com/google-gemini/gemini-cli/pull/19027)
- fix(plan): persist the approval mode in UI even when agent is thinking by
@Adib234 in [#18955](https://github.com/google-gemini/gemini-cli/pull/18955)
- feat(sdk): Implement dynamic system instructions by @mbleigh in
[#18863](https://github.com/google-gemini/gemini-cli/pull/18863)
- Docs: Refresh docs to organize and standardize reference materials. by
@jkcinouye in [#18403](https://github.com/google-gemini/gemini-cli/pull/18403)
- fix windows escaping (and broken tests) by @scidomino in
[#19011](https://github.com/google-gemini/gemini-cli/pull/19011)
- refactor: use `CoreToolCallStatus` in the the history data model by @jerop in
[#19033](https://github.com/google-gemini/gemini-cli/pull/19033)
- feat(cleanup): enable 30-day session retention by default by @skeshive in
[#18854](https://github.com/google-gemini/gemini-cli/pull/18854)
- feat(plan): hide plan write and edit operations on plans in Plan Mode by
@jerop in [#19012](https://github.com/google-gemini/gemini-cli/pull/19012)
- bug(ui) fix flicker refreshing background color by @jacob314 in
[#19041](https://github.com/google-gemini/gemini-cli/pull/19041)
- chore: fix dep vulnerabilities by @scidomino in
[#19036](https://github.com/google-gemini/gemini-cli/pull/19036)
- Revamp automated changelog skill by @g-samroberts in
[#18974](https://github.com/google-gemini/gemini-cli/pull/18974)
- feat(sdk): implement support for custom skills by @mbleigh in
[#19031](https://github.com/google-gemini/gemini-cli/pull/19031)
- refactor(core): complete centralization of core tool definitions by
[#19766](https://github.com/google-gemini/gemini-cli/pull/19766)
- feat(policy): Support MCP Server Wildcards in Policy Engine by @jerop in
[#20024](https://github.com/google-gemini/gemini-cli/pull/20024)
- docs(CONTRIBUTING): update React DevTools version to 6 by @mmgok in
[#20014](https://github.com/google-gemini/gemini-cli/pull/20014)
- feat(core): optimize tool descriptions and schemas for Gemini 3 by
@aishaneeshah in
[#18991](https://github.com/google-gemini/gemini-cli/pull/18991)
- feat: add /commands reload to refresh custom TOML commands by @korade-krushna
in [#19078](https://github.com/google-gemini/gemini-cli/pull/19078)
- fix(cli): wrap terminal capability queries in hidden sequence by @srithreepo
in [#19080](https://github.com/google-gemini/gemini-cli/pull/19080)
- fix(workflows): fix GitHub App token permissions for maintainer detection by
@bdmorgan in [#19139](https://github.com/google-gemini/gemini-cli/pull/19139)
- test: fix hook integration test flakiness on Windows CI by @NTaylorMullen in
[#18665](https://github.com/google-gemini/gemini-cli/pull/18665)
- fix(core): Encourage non-interactive flags for scaffolding commands by
@NTaylorMullen in
[#18804](https://github.com/google-gemini/gemini-cli/pull/18804)
- fix(core): propagate User-Agent header to setup-phase CodeAssist API calls by
@gsquared94 in
[#19182](https://github.com/google-gemini/gemini-cli/pull/19182)
- docs: document .agents/skills alias and discovery precedence by @kevmoo in
[#19166](https://github.com/google-gemini/gemini-cli/pull/19166)
- feat(cli): add loading state to new agents notification by @sehoon38 in
[#19190](https://github.com/google-gemini/gemini-cli/pull/19190)
- Add base branch to workflow. by @g-samroberts in
[#19189](https://github.com/google-gemini/gemini-cli/pull/19189)
- feat(cli): handle invalid model names in useQuotaAndFallback by @sehoon38 in
[#19222](https://github.com/google-gemini/gemini-cli/pull/19222)
- docs: custom themes in extensions by @jackwotherspoon in
[#19219](https://github.com/google-gemini/gemini-cli/pull/19219)
- Disable workspace settings when starting GCLI in the home directory. by
@kevinjwang1 in
[#19034](https://github.com/google-gemini/gemini-cli/pull/19034)
- feat(cli): refactor model command to support set and manage subcommands by
@sehoon38 in [#19221](https://github.com/google-gemini/gemini-cli/pull/19221)
- Add refresh/reload aliases to slash command subcommands by @korade-krushna in
[#19218](https://github.com/google-gemini/gemini-cli/pull/19218)
- refactor: consolidate development rules and add cli guidelines by @jacob314 in
[#19214](https://github.com/google-gemini/gemini-cli/pull/19214)
- chore(ui): remove outdated tip about model routing by @sehoon38 in
[#19226](https://github.com/google-gemini/gemini-cli/pull/19226)
- feat(core): support custom reasoning models by default by @NTaylorMullen in
[#19227](https://github.com/google-gemini/gemini-cli/pull/19227)
- Add Solarized Dark and Solarized Light themes by @rmedranollamas in
[#19064](https://github.com/google-gemini/gemini-cli/pull/19064)
- fix(telemetry): replace JSON.stringify with safeJsonStringify in file
exporters by @gsquared94 in
[#19244](https://github.com/google-gemini/gemini-cli/pull/19244)
- feat(telemetry): add keychain availability and token storage metrics by
@abhipatel12 in
[#18971](https://github.com/google-gemini/gemini-cli/pull/18971)
- feat(cli): update approval mode cycle order by @jerop in
[#19254](https://github.com/google-gemini/gemini-cli/pull/19254)
- refactor(cli): code review cleanup fix for tab+tab by @jacob314 in
[#18967](https://github.com/google-gemini/gemini-cli/pull/18967)
- feat(plan): support project exploration without planning when in plan mode by
@Adib234 in [#18992](https://github.com/google-gemini/gemini-cli/pull/18992)
- feat: add role-specific statistics to telemetry and UI (cont. #15234) by
@yunaseoul in [#18824](https://github.com/google-gemini/gemini-cli/pull/18824)
- feat(cli): remove Plan Mode from rotation when actively working by @jerop in
[#19262](https://github.com/google-gemini/gemini-cli/pull/19262)
- Fix side breakage where anchors don't work in slugs. by @g-samroberts in
[#19261](https://github.com/google-gemini/gemini-cli/pull/19261)
- feat(config): add setting to make directory tree context configurable by
@kevin-ramdass in
[#19053](https://github.com/google-gemini/gemini-cli/pull/19053)
- fix(acp): Wait for mcp initialization in acp (#18893) by @Mervap in
[#18894](https://github.com/google-gemini/gemini-cli/pull/18894)
- docs: format UTC times in releases doc by @pavan-sh in
[#18169](https://github.com/google-gemini/gemini-cli/pull/18169)
- Docs: Clarify extensions documentation. by @jkcinouye in
[#19277](https://github.com/google-gemini/gemini-cli/pull/19277)
- refactor(core): modularize tool definitions by model family by @aishaneeshah
in [#19269](https://github.com/google-gemini/gemini-cli/pull/19269)
- fix(paths): Add cross-platform path normalization by @spencer426 in
[#18939](https://github.com/google-gemini/gemini-cli/pull/18939)
- feat(core): experimental in-progress steering hints (1 of 3) by @joshualitt in
[#19008](https://github.com/google-gemini/gemini-cli/pull/19008)
- fix(patch): cherry-pick 261788c to release/v0.30.0-preview.0-pr-19453 to patch
version v0.30.0-preview.0 and create version 0.30.0-preview.1 by
[#19643](https://github.com/google-gemini/gemini-cli/pull/19643)
- feat(core): implement experimental direct web fetch by @mbleigh in
[#19557](https://github.com/google-gemini/gemini-cli/pull/19557)
- feat(core): replace expected_replacements with allow_multiple in replace tool
by @SandyTao520 in
[#20033](https://github.com/google-gemini/gemini-cli/pull/20033)
- fix(sandbox): harden image packaging integrity checks by @aviralgarg05 in
[#19552](https://github.com/google-gemini/gemini-cli/pull/19552)
- fix(core): allow environment variable expansion and explicit overrides for MCP
servers by @galz10 in
[#18837](https://github.com/google-gemini/gemini-cli/pull/18837)
- feat(policy): Implement Tool Annotation Matching in Policy Engine by @jerop in
[#20029](https://github.com/google-gemini/gemini-cli/pull/20029)
- fix(core): prevent utility calls from changing session active model by
@adamfweidman in
[#20035](https://github.com/google-gemini/gemini-cli/pull/20035)
- fix(cli): skip workspace policy loading when in home directory by
@Abhijit-2592 in
[#20054](https://github.com/google-gemini/gemini-cli/pull/20054)
- fix(scripts): Add Windows (win32/x64) support to lint.js by @ZafeerMahmood in
[#16193](https://github.com/google-gemini/gemini-cli/pull/16193)
- fix(a2a-server): Remove unsafe type assertions in agent by @Nixxx19 in
[#19723](https://github.com/google-gemini/gemini-cli/pull/19723)
- Fix: Handle corrupted token file gracefully when switching auth types (#19845)
by @Nixxx19 in
[#19850](https://github.com/google-gemini/gemini-cli/pull/19850)
- fix critical dep vulnerability by @scidomino in
[#20087](https://github.com/google-gemini/gemini-cli/pull/20087)
- Add new setting to configure maxRetries by @kevinjwang1 in
[#20064](https://github.com/google-gemini/gemini-cli/pull/20064)
- Stabilize tests. by @gundermanc in
[#20095](https://github.com/google-gemini/gemini-cli/pull/20095)
- make windows tests mandatory by @scidomino in
[#20096](https://github.com/google-gemini/gemini-cli/pull/20096)
- Add 3.1 pro preview to behavioral evals. by @gundermanc in
[#20088](https://github.com/google-gemini/gemini-cli/pull/20088)
- feat:PR-rate-limit by @JagjeevanAK in
[#19804](https://github.com/google-gemini/gemini-cli/pull/19804)
- feat(cli): allow expanding full details of MCP tool on approval by @y-okt in
[#19916](https://github.com/google-gemini/gemini-cli/pull/19916)
- feat(security): Introduce Conseca framework by @shrishabh in
[#13193](https://github.com/google-gemini/gemini-cli/pull/13193)
- fix(cli): Remove unsafe type assertions in activityLogger #19713 by @Nixxx19
in [#19745](https://github.com/google-gemini/gemini-cli/pull/19745)
- feat: implement AfterTool tail tool calls by @googlestrobe in
[#18486](https://github.com/google-gemini/gemini-cli/pull/18486)
- ci(actions): fix PR rate limiter excluding maintainers by @scidomino in
[#20117](https://github.com/google-gemini/gemini-cli/pull/20117)
- Shortcuts: Move SectionHeader title below top line and refine styling by
@keithguerin in
[#18721](https://github.com/google-gemini/gemini-cli/pull/18721)
- refactor(ui): Update and simplify use of gray colors in themes by @keithguerin
in [#20141](https://github.com/google-gemini/gemini-cli/pull/20141)
- fix punycode2 by @jacob314 in
[#20154](https://github.com/google-gemini/gemini-cli/pull/20154)
- feat(ide): add GEMINI_CLI_IDE_PID env var to override IDE process detection by
@kiryltech in [#15842](https://github.com/google-gemini/gemini-cli/pull/15842)
- feat(policy): Propagate Tool Annotations for MCP Servers by @jerop in
[#20083](https://github.com/google-gemini/gemini-cli/pull/20083)
- fix(a2a-server): pass allowedTools settings to core Config by @reyyanxahmed in
[#19680](https://github.com/google-gemini/gemini-cli/pull/19680)
- feat(mcp): add progress bar, throttling, and input validation for MCP tool
progress by @jasmeetsb in
[#19772](https://github.com/google-gemini/gemini-cli/pull/19772)
- feat(policy): centralize plan mode tool visibility in policy engine by @jerop
in [#20178](https://github.com/google-gemini/gemini-cli/pull/20178)
- feat(browser): implement experimental browser agent by @gsquared94 in
[#19284](https://github.com/google-gemini/gemini-cli/pull/19284)
- feat(plan): summarize work after executing a plan by @jerop in
[#19432](https://github.com/google-gemini/gemini-cli/pull/19432)
- fix(core): create new McpClient on restart to apply updated config by @h30s in
[#20126](https://github.com/google-gemini/gemini-cli/pull/20126)
- Changelog for v0.30.0-preview.5 by @gemini-cli-robot in
[#20107](https://github.com/google-gemini/gemini-cli/pull/20107)
- Update packages. by @jacob314 in
[#20152](https://github.com/google-gemini/gemini-cli/pull/20152)
- Fix extension env dir loading issue by @chrstnb in
[#20198](https://github.com/google-gemini/gemini-cli/pull/20198)
- restrict /assign to help-wanted issues by @scidomino in
[#20207](https://github.com/google-gemini/gemini-cli/pull/20207)
- feat(plan): inject message when user manually exits Plan mode by @jerop in
[#20203](https://github.com/google-gemini/gemini-cli/pull/20203)
- feat(extensions): enforce folder trust for local extension install by @galz10
in [#19703](https://github.com/google-gemini/gemini-cli/pull/19703)
- feat(hooks): adds support for RuntimeHook functions. by @mbleigh in
[#19598](https://github.com/google-gemini/gemini-cli/pull/19598)
- Docs: Update UI links. by @jkcinouye in
[#20224](https://github.com/google-gemini/gemini-cli/pull/20224)
- feat: prompt users to run /terminal-setup with yes/no by @ishaanxgupta in
[#16235](https://github.com/google-gemini/gemini-cli/pull/16235)
- fix: additional high vulnerabilities (minimatch, cross-spawn) by @adamfweidman
in [#20221](https://github.com/google-gemini/gemini-cli/pull/20221)
- feat(telemetry): Add context breakdown to API response event by @SandyTao520
in [#19699](https://github.com/google-gemini/gemini-cli/pull/19699)
- Docs: Add nested sub-folders for related topics by @g-samroberts in
[#20235](https://github.com/google-gemini/gemini-cli/pull/20235)
- feat(plan): support automatic model switching for Plan Mode by @jerop in
[#20240](https://github.com/google-gemini/gemini-cli/pull/20240)
- fix(patch): cherry-pick 58df1c6 to release/v0.31.0-preview.0-pr-20374 to patch
version v0.31.0-preview.0 and create version 0.31.0-preview.1 by
@gemini-cli-robot in
[#19490](https://github.com/google-gemini/gemini-cli/pull/19490)
- fix(patch): cherry-pick c43500c to release/v0.30.0-preview.1-pr-19502 to patch
version v0.30.0-preview.1 and create version 0.30.0-preview.2 by
[#20568](https://github.com/google-gemini/gemini-cli/pull/20568)
- fix(patch): cherry-pick ea48bd9 to release/v0.31.0-preview.1-pr-20577
[CONFLICTS] by @gemini-cli-robot in
[#20592](https://github.com/google-gemini/gemini-cli/pull/20592)
- fix(patch): cherry-pick 32e777f to release/v0.31.0-preview.2-pr-20531 to patch
version v0.31.0-preview.2 and create version 0.31.0-preview.3 by
@gemini-cli-robot in
[#19521](https://github.com/google-gemini/gemini-cli/pull/19521)
- fix(patch): cherry-pick aa9163d to release/v0.30.0-preview.3-pr-19991 to patch
version v0.30.0-preview.3 and create version 0.30.0-preview.4 by
@gemini-cli-robot in
[#20040](https://github.com/google-gemini/gemini-cli/pull/20040)
- fix(patch): cherry-pick 2c1d6f8 to release/v0.30.0-preview.4-pr-19369 to patch
version v0.30.0-preview.4 and create version 0.30.0-preview.5 by
@gemini-cli-robot in
[#20086](https://github.com/google-gemini/gemini-cli/pull/20086)
- fix(patch): cherry-pick d96bd05 to release/v0.30.0-preview.5-pr-19867 to patch
version v0.30.0-preview.5 and create version 0.30.0-preview.6 by
@gemini-cli-robot in
[#20112](https://github.com/google-gemini/gemini-cli/pull/20112)
[#20607](https://github.com/google-gemini/gemini-cli/pull/20607)
**Full Changelog**:
https://github.com/google-gemini/gemini-cli/compare/v0.29.7...v0.30.1
https://github.com/google-gemini/gemini-cli/compare/v0.30.1...v0.31.0
+187 -395
View File
@@ -1,4 +1,4 @@
# Preview release: v0.31.0-preview.1
# Preview release: v0.32.0-preview.0
Released: February 27, 2026
@@ -13,404 +13,196 @@ npm install -g @google/gemini-cli@preview
## Highlights
- **Plan Mode Enhancements**: Numerous additions including automatic model
switching, custom storage directory configuration, message injection upon
manual exit, enforcement of read-only constraints, and centralized tool
visibility in the policy engine.
- **Policy Engine Updates**: Project-level policy support added, alongside MCP
server wildcard support, tool annotation propagation and matching, and
workspace-level "Always Allow" persistence.
- **MCP Integration Improvements**: Better integration through support for MCP
progress updates with input validation and throttling, environment variable
expansion for servers, and full details expansion on tool approval.
- **CLI & Core UX Enhancements**: Several UI and quality-of-life updates such as
Alt+D for forward word deletion, macOS run-event notifications, enhanced
folder trust configurations with security warnings, improved startup warnings,
and a new experimental browser agent.
- **Security & Stability**: Introduced the Conseca framework, deceptive URL and
Unicode character detection, stricter access checks, rate limits on web fetch,
and resolved multiple dependency vulnerabilities.
- **Plan Mode Enhancements**: Significant updates to Plan Mode, including
support for modifying plans in external editors, adaptive workflows based on
task complexity, and new integration tests.
- **Agent and Core Engine Updates**: Enabled the generalist agent, introduced
`Kind.Agent` for sub-agent classification, implemented task tracking
foundation, and improved Agent-to-Agent (A2A) streaming and content
extraction.
- **CLI & User Experience**: Introduced interactive shell autocompletion, added
a new verbosity mode for cleaner error reporting, enabled parallel loading of
extensions, and improved UI hints and shortcut handling.
- **Billing and Security**: Implemented G1 AI credits overage flow with enhanced
billing telemetry, updated the authentication handshake to specification, and
added support for a policy engine in extensions.
- **Stability and Bug Fixes**: Addressed numerous issues including 100% CPU
consumption by orphaned processes, enhanced retry logic for Code Assist,
reduced intrusive MCP errors, and merged duplicate imports across packages.
## What's Changed
- fix(patch): cherry-pick 58df1c6 to release/v0.31.0-preview.0-pr-20374 to patch
version v0.31.0-preview.0 and create version 0.31.0-preview.1 by
@gemini-cli-robot in
[#20568](https://github.com/google-gemini/gemini-cli/pull/20568)
- Use ranged reads and limited searches and fuzzy editing improvements by
@gundermanc in
[#19240](https://github.com/google-gemini/gemini-cli/pull/19240)
- Fix bottom border color by @jacob314 in
[#19266](https://github.com/google-gemini/gemini-cli/pull/19266)
- Release note generator fix by @g-samroberts in
[#19363](https://github.com/google-gemini/gemini-cli/pull/19363)
- test(evals): add behavioral tests for tool output masking by @NTaylorMullen in
[#19172](https://github.com/google-gemini/gemini-cli/pull/19172)
- docs: clarify preflight instructions in GEMINI.md by @NTaylorMullen in
[#19377](https://github.com/google-gemini/gemini-cli/pull/19377)
- feat(cli): add gemini --resume hint on exit by @Mag1ck in
[#16285](https://github.com/google-gemini/gemini-cli/pull/16285)
- fix: optimize height calculations for ask_user dialog by @jackwotherspoon in
[#19017](https://github.com/google-gemini/gemini-cli/pull/19017)
- feat(cli): add Alt+D for forward word deletion by @scidomino in
[#19300](https://github.com/google-gemini/gemini-cli/pull/19300)
- Disable failing eval test by @chrstnb in
[#19455](https://github.com/google-gemini/gemini-cli/pull/19455)
- fix(cli): support legacy onConfirm callback in ToolActionsContext by
- feat(plan): add integration tests for plan mode by @Adib234 in
[#20214](https://github.com/google-gemini/gemini-cli/pull/20214)
- fix(acp): update auth handshake to spec by @skeshive in
[#19725](https://github.com/google-gemini/gemini-cli/pull/19725)
- feat(core): implement robust A2A streaming reassembly and fix task continuity
by @adamfweidman in
[#20091](https://github.com/google-gemini/gemini-cli/pull/20091)
- feat(cli): load extensions in parallel by @scidomino in
[#20229](https://github.com/google-gemini/gemini-cli/pull/20229)
- Plumb the maxAttempts setting through Config args by @kevinjwang1 in
[#20239](https://github.com/google-gemini/gemini-cli/pull/20239)
- fix(cli): skip 404 errors in setup-github file downloads by @h30s in
[#20287](https://github.com/google-gemini/gemini-cli/pull/20287)
- fix(cli): expose model.name setting in settings dialog for persistence by
@achaljhawar in
[#19605](https://github.com/google-gemini/gemini-cli/pull/19605)
- docs: remove legacy cmd examples in favor of powershell by @scidomino in
[#20323](https://github.com/google-gemini/gemini-cli/pull/20323)
- feat(core): Enable model steering in workspace. by @joshualitt in
[#20343](https://github.com/google-gemini/gemini-cli/pull/20343)
- fix: remove trailing comma in issue triage workflow settings json by @Nixxx19
in [#20265](https://github.com/google-gemini/gemini-cli/pull/20265)
- feat(core): implement task tracker foundation and service by @anj-s in
[#19464](https://github.com/google-gemini/gemini-cli/pull/19464)
- test: support tests that include color information by @jacob314 in
[#20220](https://github.com/google-gemini/gemini-cli/pull/20220)
- feat(core): introduce Kind.Agent for sub-agent classification by @abhipatel12
in [#20369](https://github.com/google-gemini/gemini-cli/pull/20369)
- Changelog for v0.30.0 by @gemini-cli-robot in
[#20252](https://github.com/google-gemini/gemini-cli/pull/20252)
- Update changelog workflow to reject nightly builds by @g-samroberts in
[#20248](https://github.com/google-gemini/gemini-cli/pull/20248)
- Changelog for v0.31.0-preview.0 by @gemini-cli-robot in
[#20249](https://github.com/google-gemini/gemini-cli/pull/20249)
- feat(cli): hide workspace policy update dialog and auto-accept by default by
@Abhijit-2592 in
[#20351](https://github.com/google-gemini/gemini-cli/pull/20351)
- feat(core): rename grep_search include parameter to include_pattern by
@SandyTao520 in
[#19369](https://github.com/google-gemini/gemini-cli/pull/19369)
- chore(deps): bump tar from 7.5.7 to 7.5.8 by dependabot[bot] in
[#19367](https://github.com/google-gemini/gemini-cli/pull/19367)
- fix(plan): allow safe fallback when experiment setting for plan is not enabled
but approval mode at startup is plan by @Adib234 in
[#19439](https://github.com/google-gemini/gemini-cli/pull/19439)
- Add explicit color-convert dependency by @chrstnb in
[#19460](https://github.com/google-gemini/gemini-cli/pull/19460)
- feat(devtools): migrate devtools package into monorepo by @SandyTao520 in
[#18936](https://github.com/google-gemini/gemini-cli/pull/18936)
- fix(core): clarify plan mode constraints and exit mechanism by @jerop in
[#19438](https://github.com/google-gemini/gemini-cli/pull/19438)
- feat(cli): add macOS run-event notifications (interactive only) by
@LyalinDotCom in
[#19056](https://github.com/google-gemini/gemini-cli/pull/19056)
- Changelog for v0.29.0 by @gemini-cli-robot in
[#19361](https://github.com/google-gemini/gemini-cli/pull/19361)
- fix(ui): preventing empty history items from being added by @devr0306 in
[#19014](https://github.com/google-gemini/gemini-cli/pull/19014)
- Changelog for v0.30.0-preview.0 by @gemini-cli-robot in
[#19364](https://github.com/google-gemini/gemini-cli/pull/19364)
- feat(core): add support for MCP progress updates by @NTaylorMullen in
[#19046](https://github.com/google-gemini/gemini-cli/pull/19046)
- fix(core): ensure directory exists before writing conversation file by
@godwiniheuwa in
[#18429](https://github.com/google-gemini/gemini-cli/pull/18429)
- fix(ui): move margin from top to bottom in ToolGroupMessage by @imadraude in
[#17198](https://github.com/google-gemini/gemini-cli/pull/17198)
- fix(cli): treat unknown slash commands as regular input instead of showing
error by @skyvanguard in
[#17393](https://github.com/google-gemini/gemini-cli/pull/17393)
- feat(core): experimental in-progress steering hints (2 of 2) by @joshualitt in
[#19307](https://github.com/google-gemini/gemini-cli/pull/19307)
- docs(plan): add documentation for plan mode command by @Adib234 in
[#19467](https://github.com/google-gemini/gemini-cli/pull/19467)
- fix(core): ripgrep fails when pattern looks like ripgrep flag by @syvb in
[#18858](https://github.com/google-gemini/gemini-cli/pull/18858)
- fix(cli): disable auto-completion on Shift+Tab to preserve mode cycling by
@NTaylorMullen in
[#19451](https://github.com/google-gemini/gemini-cli/pull/19451)
- use issuer instead of authorization_endpoint for oauth discovery by
@garrettsparks in
[#17332](https://github.com/google-gemini/gemini-cli/pull/17332)
- feat(cli): include `/dir add` directories in @ autocomplete suggestions by
@jasmeetsb in [#19246](https://github.com/google-gemini/gemini-cli/pull/19246)
- feat(admin): Admin settings should only apply if adminControlsApplicable =
true and fetch errors should be fatal by @skeshive in
[#19453](https://github.com/google-gemini/gemini-cli/pull/19453)
- Format strict-development-rules command by @g-samroberts in
[#19484](https://github.com/google-gemini/gemini-cli/pull/19484)
- feat(core): centralize compatibility checks and add TrueColor detection by
[#20328](https://github.com/google-gemini/gemini-cli/pull/20328)
- feat(plan): support opening and modifying plan in external editor by @Adib234
in [#20348](https://github.com/google-gemini/gemini-cli/pull/20348)
- feat(cli): implement interactive shell autocompletion by @mrpmohiburrahman in
[#20082](https://github.com/google-gemini/gemini-cli/pull/20082)
- fix(core): allow /memory add to work in plan mode by @Jefftree in
[#20353](https://github.com/google-gemini/gemini-cli/pull/20353)
- feat(core): add HTTP 499 to retryable errors and map to RetryableQuotaError by
@bdmorgan in [#20432](https://github.com/google-gemini/gemini-cli/pull/20432)
- feat(core): Enable generalist agent by @joshualitt in
[#19665](https://github.com/google-gemini/gemini-cli/pull/19665)
- Updated tests in TableRenderer.test.tsx to use SVG snapshots by @devr0306 in
[#20450](https://github.com/google-gemini/gemini-cli/pull/20450)
- Refactor Github Action per b/485167538 by @google-admin in
[#19443](https://github.com/google-gemini/gemini-cli/pull/19443)
- fix(github): resolve actionlint and yamllint regressions from #19443 by @jerop
in [#20467](https://github.com/google-gemini/gemini-cli/pull/20467)
- fix: action var usage by @galz10 in
[#20492](https://github.com/google-gemini/gemini-cli/pull/20492)
- feat(core): improve A2A content extraction by @adamfweidman in
[#20487](https://github.com/google-gemini/gemini-cli/pull/20487)
- fix(cli): support quota error fallbacks for all authentication types by
@sehoon38 in [#20475](https://github.com/google-gemini/gemini-cli/pull/20475)
- fix(core): flush transcript for pure tool-call responses to ensure BeforeTool
hooks see complete state by @krishdef7 in
[#20419](https://github.com/google-gemini/gemini-cli/pull/20419)
- feat(plan): adapt planning workflow based on complexity of task by @jerop in
[#20465](https://github.com/google-gemini/gemini-cli/pull/20465)
- fix: prevent orphaned processes from consuming 100% CPU when terminal closes
by @yuvrajangadsingh in
[#16965](https://github.com/google-gemini/gemini-cli/pull/16965)
- feat(core): increase fetch timeout and fix [object Object] error
stringification by @bdmorgan in
[#20441](https://github.com/google-gemini/gemini-cli/pull/20441)
- [Gemma x Gemini CLI] Add an Experimental Gemma Router that uses a LiteRT-LM
shim into the Composite Model Classifier Strategy by @sidwan02 in
[#17231](https://github.com/google-gemini/gemini-cli/pull/17231)
- docs(plan): update documentation regarding supporting editing of plan files
during plan approval by @Adib234 in
[#20452](https://github.com/google-gemini/gemini-cli/pull/20452)
- test(cli): fix flaky ToolResultDisplay overflow test by @jwhelangoog in
[#20518](https://github.com/google-gemini/gemini-cli/pull/20518)
- ui(cli): reduce length of Ctrl+O hint by @jwhelangoog in
[#20490](https://github.com/google-gemini/gemini-cli/pull/20490)
- fix(ui): correct styled table width calculations by @devr0306 in
[#20042](https://github.com/google-gemini/gemini-cli/pull/20042)
- Avoid overaggressive unescaping by @scidomino in
[#20520](https://github.com/google-gemini/gemini-cli/pull/20520)
- feat(telemetry) Instrument traces with more attributes and make them available
to OTEL users by @heaventourist in
[#20237](https://github.com/google-gemini/gemini-cli/pull/20237)
- Add support for policy engine in extensions by @chrstnb in
[#20049](https://github.com/google-gemini/gemini-cli/pull/20049)
- Docs: Update to Terms of Service & FAQ by @jkcinouye in
[#20488](https://github.com/google-gemini/gemini-cli/pull/20488)
- Fix bottom border rendering for search and add a regression test. by @jacob314
in [#20517](https://github.com/google-gemini/gemini-cli/pull/20517)
- fix(core): apply retry logic to CodeAssistServer for all users by @bdmorgan in
[#20507](https://github.com/google-gemini/gemini-cli/pull/20507)
- Fix extension MCP server env var loading by @chrstnb in
[#20374](https://github.com/google-gemini/gemini-cli/pull/20374)
- feat(ui): add 'ctrl+o' hint to truncated content message by @jerop in
[#20529](https://github.com/google-gemini/gemini-cli/pull/20529)
- Fix flicker showing message to press ctrl-O again to collapse. by @jacob314 in
[#20414](https://github.com/google-gemini/gemini-cli/pull/20414)
- fix(cli): hide shortcuts hint while model is thinking or the user has typed a
prompt + add debounce to avoid flicker by @jacob314 in
[#19389](https://github.com/google-gemini/gemini-cli/pull/19389)
- feat(plan): update planning workflow to encourage multi-select with
descriptions of options by @Adib234 in
[#20491](https://github.com/google-gemini/gemini-cli/pull/20491)
- refactor(core,cli): useAlternateBuffer read from config by @psinha40898 in
[#20346](https://github.com/google-gemini/gemini-cli/pull/20346)
- fix(cli): ensure dialogs stay scrolled to bottom in alternate buffer mode by
@jacob314 in [#20527](https://github.com/google-gemini/gemini-cli/pull/20527)
- fix(core): revert auto-save of policies to user space by @Abhijit-2592 in
[#20531](https://github.com/google-gemini/gemini-cli/pull/20531)
- Demote unreliable test. by @gundermanc in
[#20571](https://github.com/google-gemini/gemini-cli/pull/20571)
- fix(core): handle optional response fields from code assist API by @sehoon38
in [#20345](https://github.com/google-gemini/gemini-cli/pull/20345)
- fix(cli): keep thought summary when loading phrases are off by @LyalinDotCom
in [#20497](https://github.com/google-gemini/gemini-cli/pull/20497)
- feat(cli): add temporary flag to disable workspace policies by @Abhijit-2592
in [#20523](https://github.com/google-gemini/gemini-cli/pull/20523)
- Disable expensive and scheduled workflows on personal forks by @dewitt in
[#20449](https://github.com/google-gemini/gemini-cli/pull/20449)
- Moved markdown parsing logic to a separate util file by @devr0306 in
[#20526](https://github.com/google-gemini/gemini-cli/pull/20526)
- fix(plan): prevent agent from using ask_user for shell command confirmation by
@Adib234 in [#20504](https://github.com/google-gemini/gemini-cli/pull/20504)
- fix(core): disable retries for code assist streaming requests by @sehoon38 in
[#20561](https://github.com/google-gemini/gemini-cli/pull/20561)
- feat(billing): implement G1 AI credits overage flow with billing telemetry by
@gsquared94 in
[#18590](https://github.com/google-gemini/gemini-cli/pull/18590)
- feat: better error messages by @gsquared94 in
[#20577](https://github.com/google-gemini/gemini-cli/pull/20577)
- fix(ui): persist expansion in AskUser dialog when navigating options by @jerop
in [#20559](https://github.com/google-gemini/gemini-cli/pull/20559)
- fix(cli): prevent sub-agent tool calls from leaking into UI by @abhipatel12 in
[#20580](https://github.com/google-gemini/gemini-cli/pull/20580)
- fix(cli): Shell autocomplete polish by @jacob314 in
[#20411](https://github.com/google-gemini/gemini-cli/pull/20411)
- Changelog for v0.31.0-preview.1 by @gemini-cli-robot in
[#20590](https://github.com/google-gemini/gemini-cli/pull/20590)
- Add slash command for promoting behavioral evals to CI blocking by @gundermanc
in [#20575](https://github.com/google-gemini/gemini-cli/pull/20575)
- Changelog for v0.30.1 by @gemini-cli-robot in
[#20589](https://github.com/google-gemini/gemini-cli/pull/20589)
- Add low/full CLI error verbosity mode for cleaner UI by @LyalinDotCom in
[#20399](https://github.com/google-gemini/gemini-cli/pull/20399)
- Disable Gemini PR reviews on draft PRs. by @gundermanc in
[#20362](https://github.com/google-gemini/gemini-cli/pull/20362)
- Docs: FAQ update by @jkcinouye in
[#20585](https://github.com/google-gemini/gemini-cli/pull/20585)
- fix(core): reduce intrusive MCP errors and deduplicate diagnostics by
@spencer426 in
[#19478](https://github.com/google-gemini/gemini-cli/pull/19478)
- Remove unused files and update index and sidebar. by @g-samroberts in
[#19479](https://github.com/google-gemini/gemini-cli/pull/19479)
- Migrate core render util to use xterm.js as part of the rendering loop. by
@jacob314 in [#19044](https://github.com/google-gemini/gemini-cli/pull/19044)
- Changelog for v0.30.0-preview.1 by @gemini-cli-robot in
[#19496](https://github.com/google-gemini/gemini-cli/pull/19496)
- build: replace deprecated built-in punycode with userland package by @jacob314
in [#19502](https://github.com/google-gemini/gemini-cli/pull/19502)
- Speculative fixes to try to fix react error. by @jacob314 in
[#19508](https://github.com/google-gemini/gemini-cli/pull/19508)
- fix spacing by @jacob314 in
[#19494](https://github.com/google-gemini/gemini-cli/pull/19494)
- fix(core): ensure user rejections update tool outcome for telemetry by
@abhiasap in [#18982](https://github.com/google-gemini/gemini-cli/pull/18982)
- fix(acp): Initialize config (#18897) by @Mervap in
[#18898](https://github.com/google-gemini/gemini-cli/pull/18898)
- fix(core): add error logging for IDE fetch failures by @yuvrajangadsingh in
[#17981](https://github.com/google-gemini/gemini-cli/pull/17981)
- feat(acp): support set_mode interface (#18890) by @Mervap in
[#18891](https://github.com/google-gemini/gemini-cli/pull/18891)
- fix(core): robust workspace-based IDE connection discovery by @ehedlund in
[#18443](https://github.com/google-gemini/gemini-cli/pull/18443)
- Deflake windows tests. by @jacob314 in
[#19511](https://github.com/google-gemini/gemini-cli/pull/19511)
- Fix: Avoid tool confirmation timeout when no UI listeners are present by
@pdHaku0 in [#17955](https://github.com/google-gemini/gemini-cli/pull/17955)
- format md file by @scidomino in
[#19474](https://github.com/google-gemini/gemini-cli/pull/19474)
- feat(cli): add experimental.useOSC52Copy setting by @scidomino in
[#19488](https://github.com/google-gemini/gemini-cli/pull/19488)
- feat(cli): replace loading phrases boolean with enum setting by @LyalinDotCom
in [#19347](https://github.com/google-gemini/gemini-cli/pull/19347)
- Update skill to adjust for generated results. by @g-samroberts in
[#19500](https://github.com/google-gemini/gemini-cli/pull/19500)
- Fix message too large issue. by @gundermanc in
[#19499](https://github.com/google-gemini/gemini-cli/pull/19499)
- fix(core): prevent duplicate tool approval entries in auto-saved.toml by
@Abhijit-2592 in
[#19487](https://github.com/google-gemini/gemini-cli/pull/19487)
- fix(core): resolve crash in ClearcutLogger when os.cpus() is empty by @Adib234
in [#19555](https://github.com/google-gemini/gemini-cli/pull/19555)
- chore(core): improve encapsulation and remove unused exports by @adamfweidman
in [#19556](https://github.com/google-gemini/gemini-cli/pull/19556)
- Revert "Add generic searchable list to back settings and extensions (… by
@chrstnb in [#19434](https://github.com/google-gemini/gemini-cli/pull/19434)
- fix(core): improve error type extraction for telemetry by @yunaseoul in
[#19565](https://github.com/google-gemini/gemini-cli/pull/19565)
- fix: remove extra padding in Composer by @jackwotherspoon in
[#19529](https://github.com/google-gemini/gemini-cli/pull/19529)
- feat(plan): support configuring custom plans storage directory by @jerop in
[#19577](https://github.com/google-gemini/gemini-cli/pull/19577)
- Migrate files to resource or references folder. by @g-samroberts in
[#19503](https://github.com/google-gemini/gemini-cli/pull/19503)
- feat(policy): implement project-level policy support by @Abhijit-2592 in
[#18682](https://github.com/google-gemini/gemini-cli/pull/18682)
- feat(core): Implement parallel FC for read only tools. by @joshualitt in
[#18791](https://github.com/google-gemini/gemini-cli/pull/18791)
- chore(skills): adds pr-address-comments skill to work on PR feedback by
@mbleigh in [#19576](https://github.com/google-gemini/gemini-cli/pull/19576)
- refactor(sdk): introduce session-based architecture by @mbleigh in
[#19180](https://github.com/google-gemini/gemini-cli/pull/19180)
- fix(ci): add fallback JSON extraction to issue triage workflow by @bdmorgan in
[#19593](https://github.com/google-gemini/gemini-cli/pull/19593)
- feat(core): refine Edit and WriteFile tool schemas for Gemini 3 by
@SandyTao520 in
[#19476](https://github.com/google-gemini/gemini-cli/pull/19476)
- Changelog for v0.30.0-preview.3 by @gemini-cli-robot in
[#19585](https://github.com/google-gemini/gemini-cli/pull/19585)
- fix(plan): exclude EnterPlanMode tool from YOLO mode by @Adib234 in
[#19570](https://github.com/google-gemini/gemini-cli/pull/19570)
- chore: resolve build warnings and update dependencies by @mattKorwel in
[#18880](https://github.com/google-gemini/gemini-cli/pull/18880)
- feat(ui): add source indicators to slash commands by @ehedlund in
[#18839](https://github.com/google-gemini/gemini-cli/pull/18839)
- docs: refine Plan Mode documentation structure and workflow by @jerop in
[#19644](https://github.com/google-gemini/gemini-cli/pull/19644)
- Docs: Update release information regarding Gemini 3.1 by @jkcinouye in
[#19568](https://github.com/google-gemini/gemini-cli/pull/19568)
- fix(security): rate limit web_fetch tool to mitigate DDoS via prompt injection
by @mattKorwel in
[#19567](https://github.com/google-gemini/gemini-cli/pull/19567)
- Add initial implementation of /extensions explore command by @chrstnb in
[#19029](https://github.com/google-gemini/gemini-cli/pull/19029)
- fix: use discoverOAuthFromWWWAuthenticate for reactive OAuth flow (#18760) by
@maximus12793 in
[#19038](https://github.com/google-gemini/gemini-cli/pull/19038)
- Search updates by @alisa-alisa in
[#19482](https://github.com/google-gemini/gemini-cli/pull/19482)
- feat(cli): add support for numpad SS3 sequences by @scidomino in
[#19659](https://github.com/google-gemini/gemini-cli/pull/19659)
- feat(cli): enhance folder trust with configuration discovery and security
warnings by @galz10 in
[#19492](https://github.com/google-gemini/gemini-cli/pull/19492)
- feat(ui): improve startup warnings UX with dismissal and show-count limits by
@spencer426 in
[#19584](https://github.com/google-gemini/gemini-cli/pull/19584)
- feat(a2a): Add API key authentication provider by @adamfweidman in
[#19548](https://github.com/google-gemini/gemini-cli/pull/19548)
- Send accepted/removed lines with ACCEPT_FILE telemetry. by @gundermanc in
[#19670](https://github.com/google-gemini/gemini-cli/pull/19670)
- feat(models): support Gemini 3.1 Pro Preview and fixes by @sehoon38 in
[#19676](https://github.com/google-gemini/gemini-cli/pull/19676)
- feat(plan): enforce read-only constraints in Plan Mode by @mattKorwel in
[#19433](https://github.com/google-gemini/gemini-cli/pull/19433)
- fix(cli): allow perfect match @scripts/test-windows-paths.js completions to
submit on Enter by @spencer426 in
[#19562](https://github.com/google-gemini/gemini-cli/pull/19562)
- fix(core): treat 503 Service Unavailable as retryable quota error by @sehoon38
in [#19642](https://github.com/google-gemini/gemini-cli/pull/19642)
- Update sidebar.json for to allow top nav tabs. by @g-samroberts in
[#19595](https://github.com/google-gemini/gemini-cli/pull/19595)
- security: strip deceptive Unicode characters from terminal output by @ehedlund
in [#19026](https://github.com/google-gemini/gemini-cli/pull/19026)
- Fixes 'input.on' is not a function error in Gemini CLI by @gundermanc in
[#19691](https://github.com/google-gemini/gemini-cli/pull/19691)
- Revert "feat(ui): add source indicators to slash commands" by @ehedlund in
[#19695](https://github.com/google-gemini/gemini-cli/pull/19695)
- security: implement deceptive URL detection and disclosure in tool
confirmations by @ehedlund in
[#19288](https://github.com/google-gemini/gemini-cli/pull/19288)
- fix(core): restore auth consent in headless mode and add unit tests by
@ehedlund in [#19689](https://github.com/google-gemini/gemini-cli/pull/19689)
- Fix unsafe assertions in code_assist folder. by @gundermanc in
[#19706](https://github.com/google-gemini/gemini-cli/pull/19706)
- feat(cli): make JetBrains warning more specific by @jacob314 in
[#19687](https://github.com/google-gemini/gemini-cli/pull/19687)
- fix(cli): extensions dialog UX polish by @jacob314 in
[#19685](https://github.com/google-gemini/gemini-cli/pull/19685)
- fix(cli): use getDisplayString for manual model selection in dialog by
@sehoon38 in [#19726](https://github.com/google-gemini/gemini-cli/pull/19726)
- feat(policy): repurpose "Always Allow" persistence to workspace level by
@Abhijit-2592 in
[#19707](https://github.com/google-gemini/gemini-cli/pull/19707)
- fix(cli): re-enable CLI banner by @sehoon38 in
[#19741](https://github.com/google-gemini/gemini-cli/pull/19741)
- Disallow and suppress unsafe assignment by @gundermanc in
[#19736](https://github.com/google-gemini/gemini-cli/pull/19736)
- feat(core): migrate read_file to 1-based start_line/end_line parameters by
@adamfweidman in
[#19526](https://github.com/google-gemini/gemini-cli/pull/19526)
- feat(cli): improve CTRL+O experience for both standard and alternate screen
buffer (ASB) modes by @jwhelangoog in
[#19010](https://github.com/google-gemini/gemini-cli/pull/19010)
- Utilize pipelining of grep_search -> read_file to eliminate turns by
@gundermanc in
[#19574](https://github.com/google-gemini/gemini-cli/pull/19574)
- refactor(core): remove unsafe type assertions in error utils (Phase 1.1) by
@mattKorwel in
[#19750](https://github.com/google-gemini/gemini-cli/pull/19750)
- Disallow unsafe returns. by @gundermanc in
[#19767](https://github.com/google-gemini/gemini-cli/pull/19767)
- fix(cli): filter subagent sessions from resume history by @abhipatel12 in
[#19698](https://github.com/google-gemini/gemini-cli/pull/19698)
- chore(lint): fix lint errors seen when running npm run lint by @abhipatel12 in
[#19844](https://github.com/google-gemini/gemini-cli/pull/19844)
- feat(core): remove unnecessary login verbiage from Code Assist auth by
@NTaylorMullen in
[#19861](https://github.com/google-gemini/gemini-cli/pull/19861)
- fix(plan): time share by approval mode dashboard reporting negative time
shares by @Adib234 in
[#19847](https://github.com/google-gemini/gemini-cli/pull/19847)
- fix(core): allow any preview model in quota access check by @bdmorgan in
[#19867](https://github.com/google-gemini/gemini-cli/pull/19867)
- fix(core): prevent omission placeholder deletions in replace/write_file by
@nsalerni in [#19870](https://github.com/google-gemini/gemini-cli/pull/19870)
- fix(core): add uniqueness guard to edit tool by @Shivangisharma4 in
[#19890](https://github.com/google-gemini/gemini-cli/pull/19890)
- refactor(config): remove enablePromptCompletion from settings by @sehoon38 in
[#19974](https://github.com/google-gemini/gemini-cli/pull/19974)
- refactor(core): move session conversion logic to core by @abhipatel12 in
[#19972](https://github.com/google-gemini/gemini-cli/pull/19972)
- Fix: Persist manual model selection on restart #19864 by @Nixxx19 in
[#19891](https://github.com/google-gemini/gemini-cli/pull/19891)
- fix(core): increase default retry attempts and add quota error backoff by
@sehoon38 in [#19949](https://github.com/google-gemini/gemini-cli/pull/19949)
- feat(core): add policy chain support for Gemini 3.1 by @sehoon38 in
[#19991](https://github.com/google-gemini/gemini-cli/pull/19991)
- Updates command reference and /stats command. by @g-samroberts in
[#19794](https://github.com/google-gemini/gemini-cli/pull/19794)
- Fix for silent failures in non-interactive mode by @owenofbrien in
[#19905](https://github.com/google-gemini/gemini-cli/pull/19905)
- fix(plan): allow plan mode writes on Windows and fix prompt paths by @Adib234
in [#19658](https://github.com/google-gemini/gemini-cli/pull/19658)
- fix(core): prevent OAuth server crash on unexpected requests by @reyyanxahmed
in [#19668](https://github.com/google-gemini/gemini-cli/pull/19668)
- feat: Map tool kinds to explicit ACP.ToolKind values and update test … by
@sripasg in [#19547](https://github.com/google-gemini/gemini-cli/pull/19547)
- chore: restrict gemini-automted-issue-triage to only allow echo by @galz10 in
[#20047](https://github.com/google-gemini/gemini-cli/pull/20047)
- Allow ask headers longer than 16 chars by @scidomino in
[#20041](https://github.com/google-gemini/gemini-cli/pull/20041)
- fix(core): prevent state corruption in McpClientManager during collis by @h30s
in [#19782](https://github.com/google-gemini/gemini-cli/pull/19782)
- fix(bundling): copy devtools package to bundle for runtime resolution by
@SandyTao520 in
[#19766](https://github.com/google-gemini/gemini-cli/pull/19766)
- feat(policy): Support MCP Server Wildcards in Policy Engine by @jerop in
[#20024](https://github.com/google-gemini/gemini-cli/pull/20024)
- docs(CONTRIBUTING): update React DevTools version to 6 by @mmgok in
[#20014](https://github.com/google-gemini/gemini-cli/pull/20014)
- feat(core): optimize tool descriptions and schemas for Gemini 3 by
@aishaneeshah in
[#19643](https://github.com/google-gemini/gemini-cli/pull/19643)
- feat(core): implement experimental direct web fetch by @mbleigh in
[#19557](https://github.com/google-gemini/gemini-cli/pull/19557)
- feat(core): replace expected_replacements with allow_multiple in replace tool
by @SandyTao520 in
[#20033](https://github.com/google-gemini/gemini-cli/pull/20033)
- fix(sandbox): harden image packaging integrity checks by @aviralgarg05 in
[#19552](https://github.com/google-gemini/gemini-cli/pull/19552)
- fix(core): allow environment variable expansion and explicit overrides for MCP
servers by @galz10 in
[#18837](https://github.com/google-gemini/gemini-cli/pull/18837)
- feat(policy): Implement Tool Annotation Matching in Policy Engine by @jerop in
[#20029](https://github.com/google-gemini/gemini-cli/pull/20029)
- fix(core): prevent utility calls from changing session active model by
@adamfweidman in
[#20035](https://github.com/google-gemini/gemini-cli/pull/20035)
- fix(cli): skip workspace policy loading when in home directory by
@Abhijit-2592 in
[#20054](https://github.com/google-gemini/gemini-cli/pull/20054)
- fix(scripts): Add Windows (win32/x64) support to lint.js by @ZafeerMahmood in
[#16193](https://github.com/google-gemini/gemini-cli/pull/16193)
- fix(a2a-server): Remove unsafe type assertions in agent by @Nixxx19 in
[#19723](https://github.com/google-gemini/gemini-cli/pull/19723)
- Fix: Handle corrupted token file gracefully when switching auth types (#19845)
by @Nixxx19 in
[#19850](https://github.com/google-gemini/gemini-cli/pull/19850)
- fix critical dep vulnerability by @scidomino in
[#20087](https://github.com/google-gemini/gemini-cli/pull/20087)
- Add new setting to configure maxRetries by @kevinjwang1 in
[#20064](https://github.com/google-gemini/gemini-cli/pull/20064)
- Stabilize tests. by @gundermanc in
[#20095](https://github.com/google-gemini/gemini-cli/pull/20095)
- make windows tests mandatory by @scidomino in
[#20096](https://github.com/google-gemini/gemini-cli/pull/20096)
- Add 3.1 pro preview to behavioral evals. by @gundermanc in
[#20088](https://github.com/google-gemini/gemini-cli/pull/20088)
- feat:PR-rate-limit by @JagjeevanAK in
[#19804](https://github.com/google-gemini/gemini-cli/pull/19804)
- feat(cli): allow expanding full details of MCP tool on approval by @y-okt in
[#19916](https://github.com/google-gemini/gemini-cli/pull/19916)
- feat(security): Introduce Conseca framework by @shrishabh in
[#13193](https://github.com/google-gemini/gemini-cli/pull/13193)
- fix(cli): Remove unsafe type assertions in activityLogger #19713 by @Nixxx19
in [#19745](https://github.com/google-gemini/gemini-cli/pull/19745)
- feat: implement AfterTool tail tool calls by @googlestrobe in
[#18486](https://github.com/google-gemini/gemini-cli/pull/18486)
- ci(actions): fix PR rate limiter excluding maintainers by @scidomino in
[#20117](https://github.com/google-gemini/gemini-cli/pull/20117)
- Shortcuts: Move SectionHeader title below top line and refine styling by
@keithguerin in
[#18721](https://github.com/google-gemini/gemini-cli/pull/18721)
- refactor(ui): Update and simplify use of gray colors in themes by @keithguerin
in [#20141](https://github.com/google-gemini/gemini-cli/pull/20141)
- fix punycode2 by @jacob314 in
[#20154](https://github.com/google-gemini/gemini-cli/pull/20154)
- feat(ide): add GEMINI_CLI_IDE_PID env var to override IDE process detection by
@kiryltech in [#15842](https://github.com/google-gemini/gemini-cli/pull/15842)
- feat(policy): Propagate Tool Annotations for MCP Servers by @jerop in
[#20083](https://github.com/google-gemini/gemini-cli/pull/20083)
- fix(a2a-server): pass allowedTools settings to core Config by @reyyanxahmed in
[#19680](https://github.com/google-gemini/gemini-cli/pull/19680)
- feat(mcp): add progress bar, throttling, and input validation for MCP tool
progress by @jasmeetsb in
[#19772](https://github.com/google-gemini/gemini-cli/pull/19772)
- feat(policy): centralize plan mode tool visibility in policy engine by @jerop
in [#20178](https://github.com/google-gemini/gemini-cli/pull/20178)
- feat(browser): implement experimental browser agent by @gsquared94 in
[#19284](https://github.com/google-gemini/gemini-cli/pull/19284)
- feat(plan): summarize work after executing a plan by @jerop in
[#19432](https://github.com/google-gemini/gemini-cli/pull/19432)
- fix(core): create new McpClient on restart to apply updated config by @h30s in
[#20126](https://github.com/google-gemini/gemini-cli/pull/20126)
- Changelog for v0.30.0-preview.5 by @gemini-cli-robot in
[#20107](https://github.com/google-gemini/gemini-cli/pull/20107)
- Update packages. by @jacob314 in
[#20152](https://github.com/google-gemini/gemini-cli/pull/20152)
- Fix extension env dir loading issue by @chrstnb in
[#20198](https://github.com/google-gemini/gemini-cli/pull/20198)
- restrict /assign to help-wanted issues by @scidomino in
[#20207](https://github.com/google-gemini/gemini-cli/pull/20207)
- feat(plan): inject message when user manually exits Plan mode by @jerop in
[#20203](https://github.com/google-gemini/gemini-cli/pull/20203)
- feat(extensions): enforce folder trust for local extension install by @galz10
in [#19703](https://github.com/google-gemini/gemini-cli/pull/19703)
- feat(hooks): adds support for RuntimeHook functions. by @mbleigh in
[#19598](https://github.com/google-gemini/gemini-cli/pull/19598)
- Docs: Update UI links. by @jkcinouye in
[#20224](https://github.com/google-gemini/gemini-cli/pull/20224)
- feat: prompt users to run /terminal-setup with yes/no by @ishaanxgupta in
[#16235](https://github.com/google-gemini/gemini-cli/pull/16235)
- fix: additional high vulnerabilities (minimatch, cross-spawn) by @adamfweidman
in [#20221](https://github.com/google-gemini/gemini-cli/pull/20221)
- feat(telemetry): Add context breakdown to API response event by @SandyTao520
in [#19699](https://github.com/google-gemini/gemini-cli/pull/19699)
- Docs: Add nested sub-folders for related topics by @g-samroberts in
[#20235](https://github.com/google-gemini/gemini-cli/pull/20235)
- feat(plan): support automatic model switching for Plan Mode by @jerop in
[#20240](https://github.com/google-gemini/gemini-cli/pull/20240)
[#20232](https://github.com/google-gemini/gemini-cli/pull/20232)
- docs: fix spelling typos in installation guide by @campox747 in
[#20579](https://github.com/google-gemini/gemini-cli/pull/20579)
- Promote stable tests to CI blocking. by @gundermanc in
[#20581](https://github.com/google-gemini/gemini-cli/pull/20581)
- feat(core): enable contiguous parallel admission for Kind.Agent tools by
@abhipatel12 in
[#20583](https://github.com/google-gemini/gemini-cli/pull/20583)
- Enforce import/no-duplicates as error by @Nixxx19 in
[#19797](https://github.com/google-gemini/gemini-cli/pull/19797)
- fix: merge duplicate imports in sdk and test-utils packages (1/4) by @Nixxx19
in [#19777](https://github.com/google-gemini/gemini-cli/pull/19777)
- fix: merge duplicate imports in a2a-server package (2/4) by @Nixxx19 in
[#19781](https://github.com/google-gemini/gemini-cli/pull/19781)
**Full Changelog**:
https://github.com/google-gemini/gemini-cli/compare/v0.30.0-preview.6...v0.31.0-preview.1
https://github.com/google-gemini/gemini-cli/compare/v0.31.0-preview.3...v0.32.0-preview.0
+12 -12
View File
@@ -5,18 +5,18 @@ and parameters.
## CLI commands
| Command | Description | Example |
| ---------------------------------- | ---------------------------------- | --------------------------------------------------- |
| `gemini` | Start interactive REPL | `gemini` |
| `gemini "query"` | Query non-interactively, then exit | `gemini "explain this project"` |
| `cat file \| gemini` | Process piped content | `cat logs.txt \| gemini` |
| `gemini -i "query"` | Execute and continue interactively | `gemini -i "What is the purpose of this project?"` |
| `gemini -r "latest"` | Continue most recent session | `gemini -r "latest"` |
| `gemini -r "latest" "query"` | Continue session with a new prompt | `gemini -r "latest" "Check for type errors"` |
| `gemini -r "<session-id>" "query"` | Resume session by ID | `gemini -r "abc123" "Finish this PR"` |
| `gemini update` | Update to latest version | `gemini update` |
| `gemini extensions` | Manage extensions | See [Extensions Management](#extensions-management) |
| `gemini mcp` | Configure MCP servers | See [MCP Server Management](#mcp-server-management) |
| Command | Description | Example |
| ---------------------------------- | ---------------------------------- | ------------------------------------------------------------ |
| `gemini` | Start interactive REPL | `gemini` |
| `gemini "query"` | Query non-interactively, then exit | `gemini "explain this project"` |
| `cat file \| gemini` | Process piped content | `cat logs.txt \| gemini`<br>`Get-Content logs.txt \| gemini` |
| `gemini -i "query"` | Execute and continue interactively | `gemini -i "What is the purpose of this project?"` |
| `gemini -r "latest"` | Continue most recent session | `gemini -r "latest"` |
| `gemini -r "latest" "query"` | Continue session with a new prompt | `gemini -r "latest" "Check for type errors"` |
| `gemini -r "<session-id>" "query"` | Resume session by ID | `gemini -r "abc123" "Finish this PR"` |
| `gemini update` | Update to latest version | `gemini update` |
| `gemini extensions` | Manage extensions | See [Extensions Management](#extensions-management) |
| `gemini mcp` | Configure MCP servers | See [MCP Server Management](#mcp-server-management) |
### Positional arguments
+9
View File
@@ -278,11 +278,20 @@ Let's create a global command that asks the model to refactor a piece of code.
First, ensure the user commands directory exists, then create a `refactor`
subdirectory for organization and the final TOML file.
**macOS/Linux**
```bash
mkdir -p ~/.gemini/commands/refactor
touch ~/.gemini/commands/refactor/pure.toml
```
**Windows (PowerShell)**
```powershell
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.gemini\commands\refactor"
New-Item -ItemType File -Force -Path "$env:USERPROFILE\.gemini\commands\refactor\pure.toml"
```
**2. Add the content to the file:**
Open `~/.gemini/commands/refactor/pure.toml` in your editor and add the
+19
View File
@@ -203,6 +203,15 @@ 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.
**PowerShell Profile (Windows alternative):**
On Windows, administrators can achieve similar results by adding the environment
variable to the system-wide or user-specific PowerShell profile:
```powershell
Add-Content -Path $PROFILE -Value '$env:GEMINI_CLI_SYSTEM_SETTINGS_PATH="C:\ProgramData\gemini-cli\settings.json"'
```
## User isolation in shared environments
In shared compute environments (like ML experiment runners or shared build
@@ -214,12 +223,22 @@ use the `GEMINI_CLI_HOME` environment variable to point to a unique directory
for a specific user or job. The CLI will create a `.gemini` folder inside the
specified path.
**macOS/Linux**
```bash
# Isolate state for a specific job
export GEMINI_CLI_HOME="/tmp/gemini-job-123"
gemini
```
**Windows (PowerShell)**
```powershell
# Isolate state for a specific job
$env:GEMINI_CLI_HOME="C:\temp\gemini-job-123"
gemini
```
## Restricting tool access
You can significantly enhance security by controlling which tools the Gemini
+42 -2
View File
@@ -55,12 +55,27 @@ from your organization's registry.
```bash
# Enable sandboxing with command flag
gemini -s -p "analyze the code structure"
```
# Use environment variable
**Use environment variable**
**macOS/Linux**
```bash
export GEMINI_SANDBOX=true
gemini -p "run the test suite"
```
# Configure in settings.json
**Windows (PowerShell)**
```powershell
$env:GEMINI_SANDBOX="true"
gemini -p "run the test suite"
```
**Configure in settings.json**
```json
{
"tools": {
"sandbox": "docker"
@@ -99,26 +114,51 @@ use cases.
To disable SELinux labeling for volume mounts, you can set the following:
**macOS/Linux**
```bash
export SANDBOX_FLAGS="--security-opt label=disable"
```
**Windows (PowerShell)**
```powershell
$env:SANDBOX_FLAGS="--security-opt label=disable"
```
Multiple flags can be provided as a space-separated string:
**macOS/Linux**
```bash
export SANDBOX_FLAGS="--flag1 --flag2=value"
```
**Windows (PowerShell)**
```powershell
$env:SANDBOX_FLAGS="--flag1 --flag2=value"
```
## Linux UID/GID handling
The sandbox automatically handles user permissions on Linux. Override these
permissions with:
**macOS/Linux**
```bash
export SANDBOX_SET_UID_GID=true # Force host UID/GID
export SANDBOX_SET_UID_GID=false # Disable UID/GID mapping
```
**Windows (PowerShell)**
```powershell
$env:SANDBOX_SET_UID_GID="true" # Force host UID/GID
$env:SANDBOX_SET_UID_GID="false" # Disable UID/GID mapping
```
## Troubleshooting
### Common issues
+29
View File
@@ -103,23 +103,52 @@ Before using either method below, complete these steps:
1. Set your Google Cloud project ID:
- For telemetry in a separate project from inference:
**macOS/Linux**
```bash
export OTLP_GOOGLE_CLOUD_PROJECT="your-telemetry-project-id"
```
**Windows (PowerShell)**
```powershell
$env:OTLP_GOOGLE_CLOUD_PROJECT="your-telemetry-project-id"
```
- For telemetry in the same project as inference:
**macOS/Linux**
```bash
export GOOGLE_CLOUD_PROJECT="your-project-id"
```
**Windows (PowerShell)**
```powershell
$env:GOOGLE_CLOUD_PROJECT="your-project-id"
```
2. Authenticate with Google Cloud:
- If using a user account:
```bash
gcloud auth application-default login
```
- If using a service account:
**macOS/Linux**
```bash
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/service-account.json"
```
**Windows (PowerShell)**
```powershell
$env:GOOGLE_APPLICATION_CREDENTIALS="C:\path\to\your\service-account.json"
```
3. Make sure your account or service account has these IAM roles:
- Cloud Trace Agent
- Monitoring Metric Writer
+101 -5
View File
@@ -37,10 +37,18 @@ output.
Pipe a file:
**macOS/Linux**
```bash
cat error.log | gemini "Explain why this failed"
```
**Windows (PowerShell)**
```powershell
Get-Content error.log | gemini "Explain why this failed"
```
Pipe a command:
```bash
@@ -57,7 +65,10 @@ results to a file.
You have a folder of Python scripts and want to generate a `README.md` for each
one.
1. Save the following code as `generate_docs.sh`:
1. Save the following code as `generate_docs.sh` (or `generate_docs.ps1` for
Windows):
**macOS/Linux (`generate_docs.sh`)**
```bash
#!/bin/bash
@@ -72,13 +83,34 @@ one.
done
```
**Windows PowerShell (`generate_docs.ps1`)**
```powershell
# Loop through all Python files
Get-ChildItem -Filter *.py | ForEach-Object {
Write-Host "Generating docs for $($_.Name)..."
$newName = $_.Name -replace '\.py$', '.md'
# Ask Gemini CLI to generate the documentation and print it to stdout
gemini "Generate a Markdown documentation summary for @$($_.Name). Print the result to standard output." | Out-File -FilePath $newName -Encoding utf8
}
```
2. Make the script executable and run it in your directory:
**macOS/Linux**
```bash
chmod +x generate_docs.sh
./generate_docs.sh
```
**Windows (PowerShell)**
```powershell
.\generate_docs.ps1
```
This creates a corresponding Markdown file for every Python file in the
folder.
@@ -90,7 +122,10 @@ like `jq`. To get pure JSON data from the model, combine the
### Scenario: Extract and return structured data
1. Save the following script as `generate_json.sh`:
1. Save the following script as `generate_json.sh` (or `generate_json.ps1` for
Windows):
**macOS/Linux (`generate_json.sh`)**
```bash
#!/bin/bash
@@ -105,13 +140,35 @@ like `jq`. To get pure JSON data from the model, combine the
gemini --output-format json "Return a raw JSON object with keys 'version' and 'deps' from @package.json" | jq -r '.response' > data.json
```
2. Run `generate_json.sh`:
**Windows PowerShell (`generate_json.ps1`)**
```powershell
# Ensure we are in a project root
if (-not (Test-Path "package.json")) {
Write-Error "Error: package.json not found."
exit 1
}
# Extract data (requires jq installed, or you can use ConvertFrom-Json)
$output = gemini --output-format json "Return a raw JSON object with keys 'version' and 'deps' from @package.json" | ConvertFrom-Json
$output.response | Out-File -FilePath data.json -Encoding utf8
```
2. Run the script:
**macOS/Linux**
```bash
chmod +x generate_json.sh
./generate_json.sh
```
**Windows (PowerShell)**
```powershell
.\generate_json.ps1
```
3. Check `data.json`. The file should look like this:
```json
@@ -129,8 +186,10 @@ Use headless mode to perform custom, automated AI tasks.
### Scenario: Create a "Smart Commit" alias
You can add a function to your shell configuration (like `.zshrc` or `.bashrc`)
to create a `git commit` wrapper that writes the message for you.
You can add a function to your shell configuration to create a `git commit`
wrapper that writes the message for you.
**macOS/Linux (Bash/Zsh)**
1. Open your `.zshrc` file (or `.bashrc` if you use Bash) in your preferred
text editor.
@@ -170,6 +229,43 @@ to create a `git commit` wrapper that writes the message for you.
source ~/.zshrc
```
**Windows (PowerShell)**
1. Open your PowerShell profile in your preferred text editor.
```powershell
notepad $PROFILE
```
2. Scroll to the very bottom of the file and paste this code:
```powershell
function gcommit {
# Get the diff of staged changes
$diff = git diff --staged
if (-not $diff) {
Write-Host "No staged changes to commit."
return
}
# Ask Gemini to write the message
Write-Host "Generating commit message..."
$msg = $diff | gemini "Write a concise Conventional Commit message for this diff. Output ONLY the message."
# Commit with the generated message
git commit -m "$msg"
}
```
Save your file and exit.
3. Run this command to make the function available immediately:
```powershell
. $PROFILE
```
4. Use your new command:
```bash
+8
View File
@@ -20,10 +20,18 @@ Most MCP servers require authentication. For GitHub, you need a PAT.
**Read/Write** access to **Issues** and **Pull Requests**.
3. Store it in your environment:
**macOS/Linux**
```bash
export GITHUB_PERSONAL_ACCESS_TOKEN="github_pat_..."
```
**Windows (PowerShell)**
```powershell
$env:GITHUB_PERSONAL_ACCESS_TOKEN="github_pat_..."
```
## How to configure Gemini CLI
You tell Gemini about new servers by editing your `settings.json`.
@@ -14,10 +14,18 @@ responding correctly.
1. Run the following command to create the folders:
**macOS/Linux**
```bash
mkdir -p .gemini/skills/api-auditor/scripts
```
**Windows (PowerShell)**
```powershell
New-Item -ItemType Directory -Force -Path ".gemini\skills\api-auditor\scripts"
```
### Create the definition
1. Create a file at `.gemini/skills/api-auditor/SKILL.md`. This tells the agent
+16
View File
@@ -189,10 +189,18 @@ Custom commands create shortcuts for complex prompts.
1. Create a `commands` directory and a subdirectory for your command group:
**macOS/Linux**
```bash
mkdir -p commands/fs
```
**Windows (PowerShell)**
```powershell
New-Item -ItemType Directory -Force -Path "commands\fs"
```
2. Create a file named `commands/fs/grep-code.toml`:
```toml
@@ -252,10 +260,18 @@ Skills are activated only when needed, which saves context tokens.
1. Create a `skills` directory and a subdirectory for your skill:
**macOS/Linux**
```bash
mkdir -p skills/security-audit
```
**Windows (PowerShell)**
```powershell
New-Item -ItemType Directory -Force -Path "skills\security-audit"
```
2. Create a `skills/security-audit/SKILL.md` file:
```markdown
+86 -5
View File
@@ -78,11 +78,20 @@ To authenticate and use Gemini CLI with a Gemini API key:
2. Set the `GEMINI_API_KEY` environment variable to your key. For example:
**macOS/Linux**
```bash
# Replace YOUR_GEMINI_API_KEY with the key from AI Studio
export GEMINI_API_KEY="YOUR_GEMINI_API_KEY"
```
**Windows (PowerShell)**
```powershell
# Replace YOUR_GEMINI_API_KEY with the key from AI Studio
$env:GEMINI_API_KEY="YOUR_GEMINI_API_KEY"
```
To make this setting persistent, see
[Persisting Environment Variables](#persisting-vars).
@@ -114,12 +123,22 @@ or the location where you want to run your jobs.
For example:
**macOS/Linux**
```bash
# Replace with your project ID and desired location (e.g., us-central1)
export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"
export GOOGLE_CLOUD_LOCATION="YOUR_PROJECT_LOCATION"
```
**Windows (PowerShell)**
```powershell
# Replace with your project ID and desired location (e.g., us-central1)
$env:GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"
$env:GOOGLE_CLOUD_LOCATION="YOUR_PROJECT_LOCATION"
```
To make any Vertex AI environment variable settings persistent, see
[Persisting Environment Variables](#persisting-vars).
@@ -130,9 +149,17 @@ Consider this authentication method if you have Google Cloud CLI installed.
> **Note:** If you have previously set `GOOGLE_API_KEY` or `GEMINI_API_KEY`, you
> must unset them to use ADC:
>
> **macOS/Linux**
>
> ```bash
> unset GOOGLE_API_KEY GEMINI_API_KEY
> ```
>
> **Windows (PowerShell)**
>
> ```powershell
> Remove-Item Env:\GOOGLE_API_KEY, Env:\GEMINI_API_KEY -ErrorAction Ignore
> ```
1. Verify you have a Google Cloud project and Vertex AI API is enabled.
@@ -160,9 +187,17 @@ pipelines, or if your organization restricts user-based ADC or API key creation.
> **Note:** If you have previously set `GOOGLE_API_KEY` or `GEMINI_API_KEY`, you
> must unset them:
>
> **macOS/Linux**
>
> ```bash
> unset GOOGLE_API_KEY GEMINI_API_KEY
> ```
>
> **Windows (PowerShell)**
>
> ```powershell
> Remove-Item Env:\GOOGLE_API_KEY, Env:\GEMINI_API_KEY -ErrorAction Ignore
> ```
1. [Create a service account and key](https://cloud.google.com/iam/docs/keys-create-delete)
and download the provided JSON file. Assign the "Vertex AI User" role to the
@@ -171,11 +206,20 @@ pipelines, or if your organization restricts user-based ADC or API key creation.
2. Set the `GOOGLE_APPLICATION_CREDENTIALS` environment variable to the JSON
file's absolute path. For example:
**macOS/Linux**
```bash
# Replace /path/to/your/keyfile.json with the actual path
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/keyfile.json"
```
**Windows (PowerShell)**
```powershell
# Replace C:\path\to\your\keyfile.json with the actual path
$env:GOOGLE_APPLICATION_CREDENTIALS="C:\path\to\your\keyfile.json"
```
3. [Configure your Google Cloud Project](#set-gcp).
4. Start the CLI:
@@ -195,11 +239,20 @@ pipelines, or if your organization restricts user-based ADC or API key creation.
2. Set the `GOOGLE_API_KEY` environment variable:
**macOS/Linux**
```bash
# Replace YOUR_GOOGLE_API_KEY with your Vertex AI API key
export GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"
```
**Windows (PowerShell)**
```powershell
# Replace YOUR_GOOGLE_API_KEY with your Vertex AI API key
$env:GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"
```
> **Note:** If you see errors like
> `"API keys are not supported by this API..."`, your organization might
> restrict API key usage for this service. Try the other Vertex AI
@@ -243,11 +296,20 @@ To configure Gemini CLI to use a Google Cloud project, do the following:
For example, to set the `GOOGLE_CLOUD_PROJECT_ID` variable:
**macOS/Linux**
```bash
# Replace YOUR_PROJECT_ID with your actual Google Cloud project ID
export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"
```
**Windows (PowerShell)**
```powershell
# Replace YOUR_PROJECT_ID with your actual Google Cloud project ID
$env:GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"
```
To make this setting persistent, see
[Persisting Environment Variables](#persisting-vars).
@@ -257,16 +319,22 @@ To avoid setting environment variables for every terminal session, you can
persist them with the following methods:
1. **Add your environment variables to your shell configuration file:** Append
the `export ...` commands to your shell's startup file (e.g., `~/.bashrc`,
`~/.zshrc`, or `~/.profile`) and reload your shell (e.g.,
`source ~/.bashrc`).
the environment variable commands to your shell's startup file.
**macOS/Linux** (e.g., `~/.bashrc`, `~/.zshrc`, or `~/.profile`):
```bash
# Example for .bashrc
echo 'export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"' >> ~/.bashrc
source ~/.bashrc
```
**Windows (PowerShell)** (e.g., `$PROFILE`):
```powershell
Add-Content -Path $PROFILE -Value '$env:GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"'
. $PROFILE
```
> **Warning:** Be aware that when you export API keys or service account
> paths in your shell configuration file, any process launched from that
> shell can read them.
@@ -274,10 +342,13 @@ persist them with the following methods:
2. **Use a `.env` file:** Create a `.gemini/.env` file in your project
directory or home directory. Gemini CLI automatically loads variables from
the first `.env` file it finds, searching up from the current directory,
then in `~/.gemini/.env` or `~/.env`. `.gemini/.env` is recommended.
then in your home directory's `.gemini/.env` (e.g., `~/.gemini/.env` or
`%USERPROFILE%\.gemini\.env`).
Example for user-wide settings:
**macOS/Linux**
```bash
mkdir -p ~/.gemini
cat >> ~/.gemini/.env <<'EOF'
@@ -286,6 +357,16 @@ persist them with the following methods:
EOF
```
**Windows (PowerShell)**
```powershell
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.gemini"
@"
GOOGLE_CLOUD_PROJECT="your-project-id"
# Add other variables like GEMINI_API_KEY as needed
"@ | Out-File -FilePath "$env:USERPROFILE\.gemini\.env" -Encoding utf8 -Append
```
Variables are loaded from the first file found, not merged.
## Running in Google Cloud environments <a id="cloud-env"></a>
+1 -1
View File
@@ -13,7 +13,7 @@ installation methods, and release types.
- "Casual" usage: 4GB+ RAM (short sessions, common tasks and edits)
- "Power" usage: 16GB+ RAM (long sessions, large codebases, deep context)
- **Runtime:** Node.js 20.0.0+
- **Shell:** Bash or Zsh
- **Shell:** Bash, Zsh, or PowerShell
- **Location:**
[Gemini Code Assist supported locations](https://developers.google.com/gemini-code-assist/resources/available-locations#americas)
- **Internet connection required**
+33 -1
View File
@@ -167,6 +167,8 @@ try {
Run hook scripts manually with sample JSON input to verify they behave as
expected before hooking them up to the CLI.
**macOS/Linux**
```bash
# Create test input
cat > test-input.json << 'EOF'
@@ -187,7 +189,30 @@ cat test-input.json | .gemini/hooks/my-hook.sh
# Check exit code
echo "Exit code: $?"
```
**Windows (PowerShell)**
```powershell
# Create test input
@"
{
"session_id": "test-123",
"cwd": "C:\\temp\\test",
"hook_event_name": "BeforeTool",
"tool_name": "write_file",
"tool_input": {
"file_path": "test.txt",
"content": "Test content"
}
}
"@ | Out-File -FilePath test-input.json -Encoding utf8
# Test the hook
Get-Content test-input.json | .\.gemini\hooks\my-hook.ps1
# Check exit code
Write-Host "Exit code: $LASTEXITCODE"
```
### Check exit codes
@@ -333,7 +358,7 @@ tool_name=$(echo "$input" | jq -r '.tool_name')
### Make scripts executable
Always make hook scripts executable:
Always make hook scripts executable on macOS/Linux:
```bash
chmod +x .gemini/hooks/*.sh
@@ -341,6 +366,10 @@ chmod +x .gemini/hooks/*.js
```
**Windows Note**: On Windows, PowerShell scripts (`.ps1`) don't use `chmod`, but
you may need to ensure your execution policy allows them to run (e.g.,
`Set-ExecutionPolicy RemoteSigned -Scope CurrentUser`).
### Version control
Commit hooks to share with your team:
@@ -481,6 +510,9 @@ ls -la .gemini/hooks/my-hook.sh
chmod +x .gemini/hooks/my-hook.sh
```
**Windows Note**: On Windows, ensure your execution policy allows running
scripts (e.g., `Get-ExecutionPolicy`).
**Verify script path:** Ensure the path in `settings.json` resolves correctly.
```bash
+24
View File
@@ -28,6 +28,8 @@ Create a directory for hooks and a simple logging script.
> This example uses `jq` to parse JSON. If you don't have it installed, you can
> perform similar logic using Node.js or Python.
**macOS/Linux**
```bash
mkdir -p .gemini/hooks
cat > .gemini/hooks/log-tools.sh << 'EOF'
@@ -52,6 +54,28 @@ EOF
chmod +x .gemini/hooks/log-tools.sh
```
**Windows (PowerShell)**
```powershell
New-Item -ItemType Directory -Force -Path ".gemini\hooks"
@"
# Read hook input from stdin
`$inputJson = `$input | Out-String | ConvertFrom-Json
# Extract tool name
`$toolName = `$inputJson.tool_name
# Log to stderr (visible in terminal if hook fails, or captured in logs)
[Console]::Error.WriteLine("Logging tool: `$toolName")
# Log to file
"[`$(Get-Date -Format 'o')] Tool executed: `$toolName" | Out-File -FilePath ".gemini\tool-log.txt" -Append -Encoding utf8
# Return success with empty JSON
"{}"
"@ | Out-File -FilePath ".gemini\hooks\log-tools.ps1" -Encoding utf8
```
## Exit Code Strategies
There are two ways to control or block an action in Gemini CLI:
+8
View File
@@ -177,10 +177,18 @@ standalone terminal and want to manually associate it with a specific IDE
instance, you can set the `GEMINI_CLI_IDE_PID` environment variable to the
process ID (PID) of your IDE.
**macOS/Linux**
```bash
export GEMINI_CLI_IDE_PID=12345
```
**Windows (PowerShell)**
```powershell
$env:GEMINI_CLI_IDE_PID=12345
```
When this variable is set, Gemini CLI will skip automatic detection and attempt
to connect using the provided PID.
+16 -7
View File
@@ -1332,7 +1332,8 @@ the `advanced.excludedEnvVars` setting in your `settings.json` file.
- **`GEMINI_MODEL`**:
- Specifies the default Gemini model to use.
- Overrides the hardcoded default
- Example: `export GEMINI_MODEL="gemini-3-flash-preview"`
- Example: `export GEMINI_MODEL="gemini-3-flash-preview"` (Windows PowerShell:
`$env:GEMINI_MODEL="gemini-3-flash-preview"`)
- **`GEMINI_CLI_IDE_PID`**:
- Manually specifies the PID of the IDE process to use for integration. This
is useful when running Gemini CLI in a standalone terminal while still
@@ -1344,12 +1345,14 @@ the `advanced.excludedEnvVars` setting in your `settings.json` file.
- By default, this is the user's system home directory. The CLI will create a
`.gemini` folder inside this directory.
- Useful for shared compute environments or keeping CLI state isolated.
- Example: `export GEMINI_CLI_HOME="/path/to/user/config"`
- Example: `export GEMINI_CLI_HOME="/path/to/user/config"` (Windows
PowerShell: `$env:GEMINI_CLI_HOME="C:\path\to\user\config"`)
- **`GOOGLE_API_KEY`**:
- Your Google Cloud API key.
- Required for using Vertex AI in express mode.
- Ensure you have the necessary permissions.
- Example: `export GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"`.
- Example: `export GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"` (Windows PowerShell:
`$env:GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"`).
- **`GOOGLE_CLOUD_PROJECT`**:
- Your Google Cloud Project ID.
- Required for using Code Assist or Vertex AI.
@@ -1360,18 +1363,23 @@ the `advanced.excludedEnvVars` setting in your `settings.json` file.
you have `GOOGLE_CLOUD_PROJECT` set in your global environment in Cloud
Shell, it will be overridden by this default. To use a different project in
Cloud Shell, you must define `GOOGLE_CLOUD_PROJECT` in a `.env` file.
- Example: `export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"`.
- Example: `export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"` (Windows
PowerShell: `$env:GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"`).
- **`GOOGLE_APPLICATION_CREDENTIALS`** (string):
- **Description:** The path to your Google Application Credentials JSON file.
- **Example:**
`export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/credentials.json"`
(Windows PowerShell:
`$env:GOOGLE_APPLICATION_CREDENTIALS="C:\path\to\your\credentials.json"`)
- **`GOOGLE_GENAI_API_VERSION`**:
- Specifies the API version to use for Gemini API requests.
- When set, overrides the default API version used by the SDK.
- Example: `export GOOGLE_GENAI_API_VERSION="v1"`
- Example: `export GOOGLE_GENAI_API_VERSION="v1"` (Windows PowerShell:
`$env:GOOGLE_GENAI_API_VERSION="v1"`)
- **`OTLP_GOOGLE_CLOUD_PROJECT`**:
- Your Google Cloud Project ID for Telemetry in Google Cloud
- Example: `export OTLP_GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"`.
- Example: `export OTLP_GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"` (Windows
PowerShell: `$env:OTLP_GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"`).
- **`GEMINI_TELEMETRY_ENABLED`**:
- Set to `true` or `1` to enable telemetry. Any other value is treated as
disabling it.
@@ -1399,7 +1407,8 @@ the `advanced.excludedEnvVars` setting in your `settings.json` file.
- **`GOOGLE_CLOUD_LOCATION`**:
- Your Google Cloud Project Location (e.g., us-central1).
- Required for using Vertex AI in non-express mode.
- Example: `export GOOGLE_CLOUD_LOCATION="YOUR_PROJECT_LOCATION"`.
- Example: `export GOOGLE_CLOUD_LOCATION="YOUR_PROJECT_LOCATION"` (Windows
PowerShell: `$env:GOOGLE_CLOUD_LOCATION="YOUR_PROJECT_LOCATION"`).
- **`GEMINI_SANDBOX`**:
- Alternative to the `sandbox` setting in `settings.json`.
- Accepts `true`, `false`, `docker`, `podman`, or a custom command string.
+10
View File
@@ -10,9 +10,19 @@ confirmation.
To create your first policy:
1. **Create the policy directory** if it doesn't exist:
**macOS/Linux**
```bash
mkdir -p ~/.gemini/policies
```
**Windows (PowerShell)**
```powershell
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.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:
+8
View File
@@ -88,10 +88,18 @@ You can configure your Google Cloud Project ID using an environment variable.
Set the `GOOGLE_CLOUD_PROJECT` environment variable in your shell:
**macOS/Linux**
```bash
export GOOGLE_CLOUD_PROJECT="your-project-id"
```
**Windows (PowerShell)**
```powershell
$env:GOOGLE_CLOUD_PROJECT="your-project-id"
```
To make this setting permanent, add this line to your shell's startup file
(e.g., `~/.bashrc`, `~/.zshrc`).
+4 -1
View File
@@ -55,10 +55,13 @@ topics on:
- Set the `NODE_USE_SYSTEM_CA=1` environment variable to tell Node.js to use
the operating system's native certificate store (where corporate
certificates are typically already installed).
- Example: `export NODE_USE_SYSTEM_CA=1`
- Example: `export NODE_USE_SYSTEM_CA=1` (Windows PowerShell:
`$env:NODE_USE_SYSTEM_CA=1`)
- Set the `NODE_EXTRA_CA_CERTS` environment variable to the absolute path of
your corporate root CA certificate file.
- Example: `export NODE_EXTRA_CA_CERTS=/path/to/your/corporate-ca.crt`
(Windows PowerShell:
`$env:NODE_EXTRA_CA_CERTS="C:\path\to\your\corporate-ca.crt"`)
## Common error messages and solutions
+9 -9
View File
@@ -1,12 +1,12 @@
{
"name": "@google/gemini-cli",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.33.0-nightly.20260228.1ca5c05d0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@google/gemini-cli",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.33.0-nightly.20260228.1ca5c05d0",
"workspaces": [
"packages/*"
],
@@ -17056,7 +17056,7 @@
},
"packages/a2a-server": {
"name": "@google/gemini-cli-a2a-server",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.33.0-nightly.20260228.1ca5c05d0",
"dependencies": {
"@a2a-js/sdk": "^0.3.8",
"@google-cloud/storage": "^7.16.0",
@@ -17114,7 +17114,7 @@
},
"packages/cli": {
"name": "@google/gemini-cli",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.33.0-nightly.20260228.1ca5c05d0",
"license": "Apache-2.0",
"dependencies": {
"@agentclientprotocol/sdk": "^0.12.0",
@@ -17197,7 +17197,7 @@
},
"packages/core": {
"name": "@google/gemini-cli-core",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.33.0-nightly.20260228.1ca5c05d0",
"license": "Apache-2.0",
"dependencies": {
"@a2a-js/sdk": "^0.3.8",
@@ -17462,7 +17462,7 @@
},
"packages/devtools": {
"name": "@google/gemini-cli-devtools",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.33.0-nightly.20260228.1ca5c05d0",
"license": "Apache-2.0",
"dependencies": {
"ws": "^8.16.0"
@@ -17477,7 +17477,7 @@
},
"packages/sdk": {
"name": "@google/gemini-cli-sdk",
"version": "0.29.0-nightly.20260203.71f46f116",
"version": "0.33.0-nightly.20260228.1ca5c05d0",
"license": "Apache-2.0",
"dependencies": {
"@google/gemini-cli-core": "file:../core",
@@ -17494,7 +17494,7 @@
},
"packages/test-utils": {
"name": "@google/gemini-cli-test-utils",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.33.0-nightly.20260228.1ca5c05d0",
"license": "Apache-2.0",
"dependencies": {
"@google/gemini-cli-core": "file:../core",
@@ -17511,7 +17511,7 @@
},
"packages/vscode-ide-companion": {
"name": "gemini-cli-vscode-ide-companion",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.33.0-nightly.20260228.1ca5c05d0",
"license": "LICENSE",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.23.0",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.33.0-nightly.20260228.1ca5c05d0",
"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.30.0-nightly.20260210.a2174751d"
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.33.0-nightly.20260228.1ca5c05d0"
},
"scripts": {
"start": "cross-env NODE_ENV=development node scripts/start.js",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli-a2a-server",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.33.0-nightly.20260228.1ca5c05d0",
"description": "Gemini CLI A2A Server",
"repository": {
"type": "git",
@@ -28,6 +28,7 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
const mockConfig = {
...params,
initialize: vi.fn(),
waitForMcpInit: vi.fn(),
refreshAuth: vi.fn(),
getExperiments: vi.fn().mockReturnValue({
flags: {
@@ -94,6 +95,7 @@ describe('loadConfig', () => {
const mockConfig = {
...(params as object),
initialize: vi.fn(),
waitForMcpInit: vi.fn(),
refreshAuth: vi.fn(),
getExperiments: vi.fn().mockReturnValue({
flags: {
+2
View File
@@ -166,6 +166,8 @@ export async function loadConfig(
// Needed to initialize ToolRegistry, and git checkpointing if enabled
await config.initialize();
await config.waitForMcpInit();
startupProfiler.flush(config);
await refreshAuthentication(config, adcFilePath, 'Config');
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.33.0-nightly.20260228.1ca5c05d0",
"description": "Gemini CLI",
"license": "Apache-2.0",
"repository": {
@@ -26,7 +26,7 @@
"dist"
],
"config": {
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.30.0-nightly.20260210.a2174751d"
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.33.0-nightly.20260228.1ca5c05d0"
},
"dependencies": {
"@agentclientprotocol/sdk": "^0.12.0",
+1
View File
@@ -604,6 +604,7 @@ const mockUIActions: UIActions = {
revealCleanUiDetailsTemporarily: vi.fn(),
handleWarning: vi.fn(),
setEmbeddedShellFocused: vi.fn(),
setActivePtyId: vi.fn(),
dismissBackgroundShell: vi.fn(),
setActiveBackgroundShellPid: vi.fn(),
setIsBackgroundShellListOpen: vi.fn(),
+3
View File
@@ -1109,6 +1109,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
backgroundShells,
dismissBackgroundShell,
retryStatus,
setActivePtyId,
} = useGeminiStream(
config.getGeminiClient(),
historyManager.history,
@@ -2509,6 +2510,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
revealCleanUiDetailsTemporarily,
handleWarning,
setEmbeddedShellFocused,
setActivePtyId,
dismissBackgroundShell,
setActiveBackgroundShellPid,
setIsBackgroundShellListOpen,
@@ -2601,6 +2603,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
revealCleanUiDetailsTemporarily,
handleWarning,
setEmbeddedShellFocused,
setActivePtyId,
dismissBackgroundShell,
setActiveBackgroundShellPid,
setIsBackgroundShellListOpen,
@@ -26,7 +26,6 @@ export const StickyHeader: React.FC<StickyHeaderProps> = ({
containerRef,
}) => (
<Box
ref={containerRef}
sticky
minHeight={1}
flexShrink={0}
@@ -58,6 +57,7 @@ export const StickyHeader: React.FC<StickyHeaderProps> = ({
}
>
<Box
ref={containerRef}
borderStyle="round"
width={width}
borderColor={borderColor}
@@ -11,50 +11,6 @@ Enter to submit · Esc to cancel
"
`;
exports[`AskUserDialog > Choice question placeholder > uses default placeholder when not provided 2`] = `
"
ERROR Cannot read properties of undefined (reading '$$typeof')
/Users/spencertang/Workspace/gemini-cli/node_modules/react/cjs/react.developme
nt.js:1208:15
1205: };
1206: exports.useContext = function (Context) {
1207: var dispatcher = resolveDispatcher();
1208: Context.$$typeof === REACT_CONSUMER_TYPE &&
1209: console.error(
1210 "Calling useContext(Context.Consumer) is not supported and will
: cause bugs. Did you mean to call useContext(Context) instead?"
1211: );
-process.env.NODE_ENV.expo
ts.useContext (/Users/spencertang/Workspace/gemini-cli/node_module
s/react/cjs/react.development.js:1208:15)
- useTerminalCapabilities (src/ui/hooks/useTerminalCapabilities.ts:15:19)
- useAlternateBuffer (src/ui/hooks/useAlternateBuffer.ts:15:24)
- ChoiceQuestionView (src/ui/components/AskUserDialog.tsx:484:29)
-Object.react-stack-bot
om-frame (/Users/spencertang/Workspace/gemini-cli/node_modules/r
eact-reconciler/cjs/react-reconciler.development.js:158
59:20)
-renderWithHo
ks (/Users/spencertang/Workspace/gemini-cli/node_modules/react-recon
ciler/cjs/react-reconciler.development.js:3221:22)
-updateFunctionCom
onent (/Users/spencertang/Workspace/gemini-cli/node_modules/react-
reconciler/cjs/react-reconciler.development.js:6475:19)
-beginWor
(/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconcile
r/cjs/react-reconciler.development.js:8009:18)
-runWithFiberIn
EV (/Users/spencertang/Workspace/gemini-cli/node_modules/react-rec
onciler/cjs/react-reconciler.development.js:1738:13)
-performUnitOfW
rk (/Users/spencertang/Workspace/gemini-cli/node_modules/react-rec
onciler/cjs/react-reconciler.development.js:12834:22)
"
`;
exports[`AskUserDialog > Choice question placeholder > uses placeholder for "Other" option when provided 1`] = `
"Select your preferred language:
@@ -66,50 +22,6 @@ Enter to submit · Esc to cancel
"
`;
exports[`AskUserDialog > Choice question placeholder > uses placeholder for "Other" option when provided 2`] = `
"
ERROR Cannot read properties of undefined (reading '$$typeof')
/Users/spencertang/Workspace/gemini-cli/node_modules/react/cjs/react.developme
nt.js:1208:15
1205: };
1206: exports.useContext = function (Context) {
1207: var dispatcher = resolveDispatcher();
1208: Context.$$typeof === REACT_CONSUMER_TYPE &&
1209: console.error(
1210 "Calling useContext(Context.Consumer) is not supported and will
: cause bugs. Did you mean to call useContext(Context) instead?"
1211: );
-process.env.NODE_ENV.expo
ts.useContext (/Users/spencertang/Workspace/gemini-cli/node_module
s/react/cjs/react.development.js:1208:15)
- useTerminalCapabilities (src/ui/hooks/useTerminalCapabilities.ts:15:19)
- useAlternateBuffer (src/ui/hooks/useAlternateBuffer.ts:15:24)
- ChoiceQuestionView (src/ui/components/AskUserDialog.tsx:484:29)
-Object.react-stack-bot
om-frame (/Users/spencertang/Workspace/gemini-cli/node_modules/r
eact-reconciler/cjs/react-reconciler.development.js:158
59:20)
-renderWithHo
ks (/Users/spencertang/Workspace/gemini-cli/node_modules/react-recon
ciler/cjs/react-reconciler.development.js:3221:22)
-updateFunctionCom
onent (/Users/spencertang/Workspace/gemini-cli/node_modules/react-
reconciler/cjs/react-reconciler.development.js:6475:19)
-beginWor
(/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconcile
r/cjs/react-reconciler.development.js:8009:18)
-runWithFiberIn
EV (/Users/spencertang/Workspace/gemini-cli/node_modules/react-rec
onciler/cjs/react-reconciler.development.js:1738:13)
-performUnitOfW
rk (/Users/spencertang/Workspace/gemini-cli/node_modules/react-rec
onciler/cjs/react-reconciler.development.js:12834:22)
"
`;
exports[`AskUserDialog > Scroll Arrows (useAlternateBuffer: false) > shows scroll arrows correctly when useAlternateBuffer is false 1`] = `
"Choose an option
@@ -163,48 +75,6 @@ Enter to select · ↑/↓ to navigate · Esc to cancel
"
`;
exports[`AskUserDialog > Scroll Arrows (useAlternateBuffer: true) > shows scroll arrows correctly when useAlternateBuffer is true 2`] = `
"
ERROR Cannot read properties of undefined (reading '$$typeof')
/Users/spencertang/Workspace/gemini-cli/node_modules/react/cjs/react.development.js:1208:15
1205: };
1206: exports.useContext = function (Context) {
1207: var dispatcher = resolveDispatcher();
1208: Context.$$typeof === REACT_CONSUMER_TYPE &&
1209: console.error(
1210 "Calling useContext(Context.Consumer) is not supported and will cause bugs. Did you
: mean to call useContext(Context) instead?"
1211: );
-process.env.NODE_ENV.exports
useContext (/Users/spencertang/Workspace/gemini-cli/node_modules/react/cjs/react
.development.js:1208:15)
- useTerminalCapabilities (src/ui/hooks/useTerminalCapabilities.ts:15:19)
- useAlternateBuffer (src/ui/hooks/useAlternateBuffer.ts:15:24)
- ChoiceQuestionView (src/ui/components/AskUserDialog.tsx:484:29)
-Object.react-stack-bott
m-frame (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs
/react-reconciler.development.js:15859:20)
-renderWithHoo
s (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/react-rec
onciler.development.js:3221:22)
-updateFunctionComp
nent (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/reac
t-reconciler.development.js:6475:19)
-beginWor
(/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/react-reconcil
er.development.js:8009:18)
-runWithFiberIn
EV (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/react-re
conciler.development.js:1738:13)
-performUnitOfW
rk (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/react-re
conciler.development.js:12834:22)
"
`;
exports[`AskUserDialog > Text type questions > renders text input for type: "text" 1`] = `
"What should we name this component?
@@ -331,45 +201,3 @@ README → (not answered)
Enter to submit · Tab/Shift+Tab to edit answers · Esc to cancel
"
`;
exports[`AskUserDialog > shows warning for unanswered questions on Review tab 2`] = `
"
ERROR Cannot read properties of undefined (reading '$$typeof')
/Users/spencertang/Workspace/gemini-cli/node_modules/react/cjs/react.development.js:1208:15
1205: };
1206: exports.useContext = function (Context) {
1207: var dispatcher = resolveDispatcher();
1208: Context.$$typeof === REACT_CONSUMER_TYPE &&
1209: console.error(
1210: "Calling useContext(Context.Consumer) is not supported and will cause bugs. Did you mean to call
useContext(Context) instead?"
1211: );
-process.env.NODE_ENV.exports.useCo
text (/Users/spencertang/Workspace/gemini-cli/node_modules/react/cjs/react.development.j
s:1208:15)
- useTerminalCapabilities (src/ui/hooks/useTerminalCapabilities.ts:15:19)
- useAlternateBuffer (src/ui/hooks/useAlternateBuffer.ts:15:24)
- ChoiceQuestionView (src/ui/components/AskUserDialog.tsx:484:29)
-Object.react-stack-botto
-frame (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/react-reconciler.d
evelopment.js:15859:20)
-renderWithHook
(/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/react-reconciler.development
.js:3221:22)
-updateFunctionCompo
ent (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/react-reconciler.develo
pment.js:6475:19)
-beginWork
(/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/react-reconciler.development.js:8
009:18)
-runWithFiberInD
V (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/react-reconciler.developmen
t.js:1738:13)
-performUnitOfWo
k (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/react-reconciler.developmen
t.js:12834:22)
"
`;
@@ -6,51 +6,6 @@ Spinner Initializing...
"
`;
exports[`ConfigInitDisplay > handles empty clients map 2`] = `
"
ERROR Cannot read properties of undefined (reading 'isKittyProtocolEnabled')
src/ui/contexts/KeypressContext.tsx:797:36
794: process.stdin.setEncoding('utf8'); // Make data events emit strings
795:
796: let processor = nonKeyboardEventFilter(broadcast);
797: if (!terminalCapabilityManager.isKittyProtocolEnabled()) {
798: processor = bufferFastReturn(processor);
799: }
800: processor = bufferBackslashEnter(processor);
- (src/ui/contexts/KeypressContext.tsx:797:36)
-Object.react-stack-bott
m-frame (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs
/react-reconciler.development.js:15945:20)
-runWithFiberIn
EV (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/react-re
conciler.development.js:1738:13)
-commitHookEffectList
ount (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/re
act-reconciler.development.js:9516:29)
-commitHookPassiveMount
ffects (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/
react-reconciler.development.js:9639:11)
-commitPassiveMountOn
iber (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/re
act-reconciler.development.js:11364:13)
-recursivelyTraversePassiveM
untEffects (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler
/cjs/react-reconciler.development.js:11338:11)
-commitPassiveMountOn
iber (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/re
act-reconciler.development.js:11479:11)
-recursivelyTraversePassiveM
untEffects (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler
/cjs/react-reconciler.development.js:11338:11)
-commitPassiveMountOn
iber (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/re
act-reconciler.development.js:11357:11)
"
`;
exports[`ConfigInitDisplay > renders initial state 1`] = `
"
Spinner Initializing...
@@ -63,98 +18,8 @@ Spinner Connecting to MCP servers... (0/5) - Waiting for: s1, s2, s3, +2 more
"
`;
exports[`ConfigInitDisplay > truncates list of waiting servers if too many 2`] = `
"
ERROR Cannot read properties of undefined (reading 'isKittyProtocolEnabled')
src/ui/contexts/KeypressContext.tsx:797:36
794: process.stdin.setEncoding('utf8'); // Make data events emit strings
795:
796: let processor = nonKeyboardEventFilter(broadcast);
797: if (!terminalCapabilityManager.isKittyProtocolEnabled()) {
798: processor = bufferFastReturn(processor);
799: }
800: processor = bufferBackslashEnter(processor);
- (src/ui/contexts/KeypressContext.tsx:797:36)
-Object.react-stack-bott
m-frame (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs
/react-reconciler.development.js:15945:20)
-runWithFiberIn
EV (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/react-re
conciler.development.js:1738:13)
-commitHookEffectList
ount (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/re
act-reconciler.development.js:9516:29)
-commitHookPassiveMount
ffects (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/
react-reconciler.development.js:9639:11)
-commitPassiveMountOn
iber (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/re
act-reconciler.development.js:11364:13)
-recursivelyTraversePassiveM
untEffects (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler
/cjs/react-reconciler.development.js:11338:11)
-commitPassiveMountOn
iber (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/re
act-reconciler.development.js:11479:11)
-recursivelyTraversePassiveM
untEffects (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler
/cjs/react-reconciler.development.js:11338:11)
-commitPassiveMountOn
iber (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/re
act-reconciler.development.js:11357:11)
"
`;
exports[`ConfigInitDisplay > updates message on McpClientUpdate event 1`] = `
"
Spinner Connecting to MCP servers... (1/2) - Waiting for: server2
"
`;
exports[`ConfigInitDisplay > updates message on McpClientUpdate event 2`] = `
"
ERROR Cannot read properties of undefined (reading 'isKittyProtocolEnabled')
src/ui/contexts/KeypressContext.tsx:797:36
794: process.stdin.setEncoding('utf8'); // Make data events emit strings
795:
796: let processor = nonKeyboardEventFilter(broadcast);
797: if (!terminalCapabilityManager.isKittyProtocolEnabled()) {
798: processor = bufferFastReturn(processor);
799: }
800: processor = bufferBackslashEnter(processor);
- (src/ui/contexts/KeypressContext.tsx:797:36)
-Object.react-stack-bott
m-frame (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs
/react-reconciler.development.js:15945:20)
-runWithFiberIn
EV (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/react-re
conciler.development.js:1738:13)
-commitHookEffectList
ount (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/re
act-reconciler.development.js:9516:29)
-commitHookPassiveMount
ffects (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/
react-reconciler.development.js:9639:11)
-commitPassiveMountOn
iber (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/re
act-reconciler.development.js:11364:13)
-recursivelyTraversePassiveM
untEffects (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler
/cjs/react-reconciler.development.js:11338:11)
-commitPassiveMountOn
iber (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/re
act-reconciler.development.js:11479:11)
-recursivelyTraversePassiveM
untEffects (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler
/cjs/react-reconciler.development.js:11338:11)
-commitPassiveMountOn
iber (/Users/spencertang/Workspace/gemini-cli/node_modules/react-reconciler/cjs/re
act-reconciler.development.js:11357:11)
"
`;
File diff suppressed because it is too large Load Diff
@@ -78,7 +78,7 @@ export const ShellToolMessage: React.FC<ShellToolMessageProps> = ({
embeddedShellFocused,
);
const { setEmbeddedShellFocused } = useUIActions();
const { setEmbeddedShellFocused, setActivePtyId } = useUIActions();
const wasFocusedRef = React.useRef(false);
React.useEffect(() => {
@@ -102,13 +102,14 @@ export const ShellToolMessage: React.FC<ShellToolMessageProps> = ({
const handleFocus = () => {
if (isThisShellFocusable) {
setActivePtyId(ptyId ?? null);
setEmbeddedShellFocused(true);
}
};
useMouseClick(headerRef, handleFocus, { isActive: !!isThisShellFocusable });
useMouseClick(headerRef, handleFocus, { isActive: true });
useMouseClick(contentRef, handleFocus, { isActive: !!isThisShellFocusable });
useMouseClick(contentRef, handleFocus, { isActive: true });
const { shouldShowFocusHint } = useFocusHint(
isThisShellFocusable,
@@ -16,12 +16,11 @@ import {
} from '../../constants.js';
import { theme } from '../../semantic-colors.js';
import {
type Config,
SHELL_TOOL_NAME,
isCompletedAskUserTool,
type ToolResultDisplay,
CoreToolCallStatus,
isCompletedAskUserTool
} from '@google/gemini-cli-core';
import type { Config, ToolResultDisplay ,
CoreToolCallStatus} from '@google/gemini-cli-core';
import { useInactivityTimer } from '../../hooks/useInactivityTimer.js';
import { formatCommand } from '../../utils/keybindingUtils.js';
import { Command } from '../../../config/keyBindings.js';
@@ -44,14 +43,10 @@ export function isShellTool(name: string): boolean {
*/
export function isThisShellFocusable(
name: string,
status: CoreToolCallStatus,
config?: Config,
_status: CoreToolCallStatus,
_config?: Config,
): boolean {
return !!(
isShellTool(name) &&
status === CoreToolCallStatus.Executing &&
config?.getEnableInteractiveShell()
);
return isShellTool(name);
}
/**
@@ -59,14 +54,13 @@ export function isThisShellFocusable(
*/
export function isThisShellFocused(
name: string,
status: CoreToolCallStatus,
_status: CoreToolCallStatus,
ptyId?: number,
activeShellPtyId?: number | null,
embeddedShellFocused?: boolean,
): boolean {
return !!(
isShellTool(name) &&
status === CoreToolCallStatus.Executing &&
ptyId === activeShellPtyId &&
embeddedShellFocused
);
@@ -5,12 +5,20 @@
*/
import type React from 'react';
import { useState, useRef, useCallback, useMemo, useLayoutEffect } from 'react';
import { Box, ResizeObserver, type DOMElement } from 'ink';
import {
useState,
useRef,
useCallback,
useMemo,
useLayoutEffect,
useEffect,
} from 'react';
import { Box, ResizeObserver, getBoundingBox, type DOMElement } from 'ink';
import { useKeypress, type Key } from '../../hooks/useKeypress.js';
import { useScrollable } from '../../contexts/ScrollProvider.js';
import { useAnimatedScrollbar } from '../../hooks/useAnimatedScrollbar.js';
import { useBatchedScroll } from '../../hooks/useBatchedScroll.js';
import { useMouse, type MouseEvent } from '../../hooks/useMouse.js';
import { keyMatchers, Command } from '../../keyMatchers.js';
interface ScrollableProps {
@@ -115,6 +123,30 @@ export const Scrollable: React.FC<ScrollableProps> = ({
[scrollToBottom],
);
const [isHovered, setIsHovered] = useState(false);
useMouse(
(event: MouseEvent) => {
if (event.name === 'move' && viewportRef.current) {
const boundingBox = getBoundingBox(viewportRef.current);
if (boundingBox) {
const { x, y, width, height } = boundingBox;
const inside =
event.col >= x &&
event.col < x + width + 1 &&
event.row >= y &&
event.row < y + height;
if (inside !== isHovered) {
setIsHovered(inside);
}
}
}
return false;
},
{ isActive: true },
);
const { getScrollTop, setPendingScrollTop } = useBatchedScroll(scrollTop);
const scrollBy = useCallback(
@@ -135,8 +167,20 @@ export const Scrollable: React.FC<ScrollableProps> = ({
const { scrollbarColor, flashScrollbar, scrollByWithAnimation } =
useAnimatedScrollbar(hasFocus, scrollBy);
// Flash scrollbar on hover for discoverability.
const wasHovered = useRef(isHovered);
useEffect(() => {
if (isHovered && !wasHovered.current && !hasFocus) {
flashScrollbar();
}
wasHovered.current = isHovered;
}, [isHovered, hasFocus, flashScrollbar]);
useKeypress(
(key: Key) => {
if (!hasFocus) {
return false;
}
const { scrollHeight, innerHeight } = sizeRef.current;
const scrollTop = getScrollTop();
const maxScroll = Math.max(0, scrollHeight - innerHeight);
@@ -175,13 +219,20 @@ export const Scrollable: React.FC<ScrollableProps> = ({
);
const getScrollState = useCallback(() => {
if (!hasFocus && !isHovered) {
return {
scrollTop: 0,
scrollHeight: 0,
innerHeight: 0,
};
}
const maxScroll = Math.max(0, size.scrollHeight - size.innerHeight);
return {
scrollTop: Math.min(getScrollTop(), maxScroll),
scrollHeight: size.scrollHeight,
innerHeight: size.innerHeight,
};
}, [getScrollTop, size.scrollHeight, size.innerHeight]);
}, [hasFocus, isHovered, getScrollTop, size.scrollHeight, size.innerHeight]);
const hasFocusCallback = useCallback(() => hasFocus, [hasFocus]);
@@ -206,11 +257,11 @@ export const Scrollable: React.FC<ScrollableProps> = ({
width={width ?? maxWidth}
height={height}
flexDirection="column"
overflowY="scroll"
overflowY={hasFocus || isHovered ? 'scroll' : 'hidden'}
overflowX="hidden"
scrollTop={scrollTop}
flexGrow={flexGrow}
scrollbarThumbColor={scrollbarColor}
scrollbarThumbColor={hasFocus || isHovered ? scrollbarColor : undefined}
>
{/*
This inner box is necessary to prevent the parent from shrinking
@@ -80,6 +80,7 @@ export interface UIActions {
revealCleanUiDetailsTemporarily: (durationMs?: number) => void;
handleWarning: (message: string) => void;
setEmbeddedShellFocused: (value: boolean) => void;
setActivePtyId: (pid: number | null) => void;
dismissBackgroundShell: (pid: number) => void;
setActiveBackgroundShellPid: (pid: number) => void;
setIsBackgroundShellListOpen: (isOpen: boolean) => void;
@@ -152,6 +152,13 @@ export const useShellCommandProcessor = (
[m],
);
const setActivePtyId = useCallback(
(pid: number | null) => {
dispatch({ type: 'SET_ACTIVE_PTY', pid });
},
[dispatch],
);
const toggleBackgroundShell = useCallback(() => {
if (state.backgroundShells.size > 0) {
const willBeVisible = !state.isBackgroundShellVisible;
@@ -550,5 +557,6 @@ export const useShellCommandProcessor = (
registerBackgroundShell,
dismissBackgroundShell,
backgroundShells: state.backgroundShells,
setActivePtyId,
};
};
+8 -7
View File
@@ -204,9 +204,8 @@ export const useGeminiStream = (
consumeUserHint?: () => string | null,
) => {
const [initError, setInitError] = useState<string | null>(null);
const [retryStatus, setRetryStatus] = useState<RetryAttemptPayload | null>(
null,
);
const [modelRetryStatus, setModelRetryStatus] =
useState<RetryAttemptPayload | null>(null);
const isLowErrorVerbosity = settings.merged.ui?.errorVerbosity !== 'full';
const suppressedToolErrorCountRef = useRef(0);
const suppressedToolErrorNoteShownRef = useRef(false);
@@ -242,7 +241,7 @@ export const useGeminiStream = (
useEffect(() => {
const handleRetryAttempt = (payload: RetryAttemptPayload) => {
setRetryStatus(payload);
setModelRetryStatus(payload);
};
coreEvents.on(CoreEvent.RetryAttempt, handleRetryAttempt);
return () => {
@@ -338,6 +337,7 @@ export const useGeminiStream = (
registerBackgroundShell,
dismissBackgroundShell,
backgroundShells,
setActivePtyId,
} = useShellCommandProcessor(
addItem,
setPendingHistoryItem,
@@ -564,7 +564,7 @@ export const useGeminiStream = (
useEffect(() => {
if (!isResponding) {
setRetryStatus(null);
setModelRetryStatus(null);
}
}, [isResponding]);
@@ -844,7 +844,7 @@ export const useGeminiStream = (
currentGeminiMessageBuffer: string,
userMessageTimestamp: number,
): string => {
setRetryStatus(null);
setModelRetryStatus(null);
if (turnCancelledRef.current) {
// Prevents additional output after a user initiated cancel.
return '';
@@ -1897,6 +1897,7 @@ export const useGeminiStream = (
backgroundCurrentShell,
backgroundShells,
dismissBackgroundShell,
retryStatus,
retryStatus: modelRetryStatus,
setActivePtyId,
};
};
@@ -196,6 +196,36 @@ describe('useTerminalTheme', () => {
expect(mockHandleThemeSelect).not.toHaveBeenCalled();
});
it('should switch theme even if terminal background report is identical to previousColor if current theme is mismatched', () => {
// Background is dark at startup
config.setTerminalBackground('#000000');
vi.mocked(config.setTerminalBackground).mockClear();
// But theme is light
mockSettings.merged.ui.theme = 'default-light';
const refreshStatic = vi.fn();
const { unmount } = renderHook(() =>
useTerminalTheme(mockHandleThemeSelect, config, refreshStatic),
);
const handler = mockSubscribe.mock.calls[0][0];
// Terminal reports the same dark background
handler('rgb:0000/0000/0000');
expect(config.setTerminalBackground).not.toHaveBeenCalled();
expect(themeManager.setTerminalBackground).not.toHaveBeenCalled();
expect(refreshStatic).not.toHaveBeenCalled();
// But it SHOULD select the dark theme because of the mismatch!
expect(mockHandleThemeSelect).toHaveBeenCalledWith(
'default',
expect.anything(),
);
mockSettings.merged.ui.theme = 'default';
unmount();
});
it('should not switch theme if autoThemeSwitching is disabled', () => {
mockSettings.merged.ui.autoThemeSwitching = false;
const { unmount } = renderHook(() =>
+10 -8
View File
@@ -59,14 +59,6 @@ export function useTerminalTheme(
if (!hexColor) return;
const previousColor = config.getTerminalBackground();
if (previousColor === hexColor) {
return;
}
config.setTerminalBackground(hexColor);
themeManager.setTerminalBackground(hexColor);
const luminance = getLuminance(hexColor);
const currentThemeName = settings.merged.ui.theme;
@@ -77,6 +69,16 @@ export function useTerminalTheme(
DefaultLight.name,
);
if (previousColor === hexColor) {
if (newTheme) {
void handleThemeSelect(newTheme, SettingScope.User);
}
return;
}
config.setTerminalBackground(hexColor);
themeManager.setTerminalBackground(hexColor);
if (newTheme) {
void handleThemeSelect(newTheme, SettingScope.User);
} else {
@@ -20,6 +20,7 @@ import {
type AnyToolInvocation,
ROOT_SCHEDULER_ID,
CoreToolCallStatus,
type WaitingToolCall,
} from '@google/gemini-cli-core';
import { createMockMessageBus } from '@google/gemini-cli-core/src/test-utils/mock-message-bus.js';
@@ -32,6 +33,7 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
Scheduler: vi.fn().mockImplementation(() => ({
schedule: vi.fn().mockResolvedValue([]),
cancelAll: vi.fn(),
dispose: vi.fn(),
})),
};
});
@@ -341,7 +343,9 @@ describe('useToolScheduler', () => {
const callSub = {
...callRoot,
request: { ...callRoot.request, callId: 'call-sub' },
status: CoreToolCallStatus.AwaitingApproval as const, // Must be awaiting approval to be tracked
schedulerId: 'subagent-1',
confirmationDetails: { type: 'info', title: 'Confirm', prompt: 'Yes?' },
};
// 1. Populate state with multiple schedulers
@@ -360,9 +364,13 @@ describe('useToolScheduler', () => {
});
const [toolCalls] = result.current;
expect(toolCalls).toHaveLength(1);
expect(toolCalls[0].request.callId).toBe('call-root');
expect(toolCalls[0].schedulerId).toBe(ROOT_SCHEDULER_ID);
expect(toolCalls).toHaveLength(2);
expect(
toolCalls.find((t) => t.request.callId === 'call-root'),
).toBeDefined();
expect(
toolCalls.find((t) => t.request.callId === 'call-sub'),
).toBeDefined();
// 2. Call setToolCallsForDisplay (e.g., simulate a manual update or clear)
act(() => {
@@ -374,12 +382,11 @@ describe('useToolScheduler', () => {
// 3. Verify that tools are still present and maintain their scheduler IDs
const [toolCalls2] = result.current;
expect(toolCalls2).toHaveLength(1);
expect(toolCalls2[0].responseSubmittedToGemini).toBe(true);
expect(toolCalls2[0].schedulerId).toBe(ROOT_SCHEDULER_ID);
expect(toolCalls2).toHaveLength(2);
expect(toolCalls2.every((t) => t.responseSubmittedToGemini)).toBe(true);
});
it('ignores TOOL_CALLS_UPDATE from non-root schedulers', () => {
it('ignores TOOL_CALLS_UPDATE from non-root schedulers when no tools await approval', () => {
const { result } = renderHook(() =>
useToolScheduler(
vi.fn().mockResolvedValue(undefined),
@@ -410,8 +417,125 @@ describe('useToolScheduler', () => {
} as ToolCallsUpdateMessage);
});
expect(result.current[0]).toHaveLength(0);
});
it('allows TOOL_CALLS_UPDATE from non-root schedulers when tools are awaiting approval', () => {
const { result } = renderHook(() =>
useToolScheduler(
vi.fn().mockResolvedValue(undefined),
mockConfig,
() => undefined,
),
);
const subagentCall = {
status: CoreToolCallStatus.AwaitingApproval as const,
request: {
callId: 'call-sub',
name: 'test',
args: {},
isClientInitiated: false,
prompt_id: 'p1',
},
tool: createMockTool(),
invocation: createMockInvocation(),
schedulerId: 'subagent-1',
confirmationDetails: { type: 'info', title: 'Confirm', prompt: 'Yes?' },
} as WaitingToolCall;
act(() => {
void mockMessageBus.publish({
type: MessageBusType.TOOL_CALLS_UPDATE,
toolCalls: [subagentCall],
schedulerId: 'subagent-1',
} as ToolCallsUpdateMessage);
});
const [toolCalls] = result.current;
expect(toolCalls).toHaveLength(0);
expect(toolCalls).toHaveLength(1);
expect(toolCalls[0].request.callId).toBe('call-sub');
expect(toolCalls[0].status).toBe(CoreToolCallStatus.AwaitingApproval);
});
it('preserves subagent tools in the UI after they have been approved', () => {
const { result } = renderHook(() =>
useToolScheduler(
vi.fn().mockResolvedValue(undefined),
mockConfig,
() => undefined,
),
);
const subagentCall = {
status: CoreToolCallStatus.AwaitingApproval as const,
request: {
callId: 'call-sub',
name: 'test',
args: {},
isClientInitiated: false,
prompt_id: 'p1',
},
tool: createMockTool(),
invocation: createMockInvocation(),
schedulerId: 'subagent-1',
confirmationDetails: { type: 'info', title: 'Confirm', prompt: 'Yes?' },
} as WaitingToolCall;
// 1. Initial approval request
act(() => {
void mockMessageBus.publish({
type: MessageBusType.TOOL_CALLS_UPDATE,
toolCalls: [subagentCall],
schedulerId: 'subagent-1',
} as ToolCallsUpdateMessage);
});
expect(result.current[0]).toHaveLength(1);
// 2. Approved and executing
const approvedCall = {
...subagentCall,
status: CoreToolCallStatus.Executing as const,
} as unknown as ExecutingToolCall;
act(() => {
void mockMessageBus.publish({
type: MessageBusType.TOOL_CALLS_UPDATE,
toolCalls: [approvedCall],
schedulerId: 'subagent-1',
} as ToolCallsUpdateMessage);
});
expect(result.current[0]).toHaveLength(1);
expect(result.current[0][0].status).toBe(CoreToolCallStatus.Executing);
// 3. New turn with a background tool (should NOT be shown)
const backgroundTool = {
status: CoreToolCallStatus.Executing as const,
request: {
callId: 'call-background',
name: 'read_file',
args: {},
isClientInitiated: false,
prompt_id: 'p1',
},
tool: createMockTool(),
invocation: createMockInvocation(),
schedulerId: 'subagent-1',
} as ExecutingToolCall;
act(() => {
void mockMessageBus.publish({
type: MessageBusType.TOOL_CALLS_UPDATE,
toolCalls: [backgroundTool],
schedulerId: 'subagent-1',
} as ToolCallsUpdateMessage);
});
// The subagent list should now be empty because the previously approved tool
// is gone from the current list, and the new tool doesn't need approval.
expect(result.current[0]).toHaveLength(0);
});
it('adapts success/error status to executing when a tail call is present', () => {
+36 -17
View File
@@ -115,11 +115,42 @@ export function useToolScheduler(
useEffect(() => {
const handler = (event: ToolCallsUpdateMessage) => {
// Only process updates for the root scheduler.
// Subagent internal tools should not be displayed in the main tool list.
if (event.schedulerId !== ROOT_SCHEDULER_ID) {
return;
}
const isRoot = event.schedulerId === ROOT_SCHEDULER_ID;
setToolCallsMap((prev) => {
const prevCalls = prev[event.schedulerId] ?? [];
const prevCallIds = new Set(prevCalls.map((tc) => tc.request.callId));
// For non-root schedulers, we only show tool calls that:
// 1. Are currently awaiting approval.
// 2. Were previously shown (e.g., they are now executing or completed).
// This prevents "thinking" tools (reads/searches) from flickering in the UI
// unless they specifically required user interaction.
const filteredToolCalls = isRoot
? event.toolCalls
: event.toolCalls.filter(
(tc) =>
tc.status === CoreToolCallStatus.AwaitingApproval ||
prevCallIds.has(tc.request.callId),
);
// If this is a subagent and we have no tools to show and weren't showing any,
// we can skip the update entirely to avoid unnecessary re-renders.
if (
!isRoot &&
filteredToolCalls.length === 0 &&
prevCalls.length === 0
) {
return prev;
}
const adapted = internalAdaptToolCalls(filteredToolCalls, prevCalls);
return {
...prev,
[event.schedulerId]: adapted,
};
});
// Update output timer for UI spinners (Side Effect)
const hasExecuting = event.toolCalls.some(
@@ -134,18 +165,6 @@ export function useToolScheduler(
if (hasExecuting) {
setLastToolOutputTime(Date.now());
}
setToolCallsMap((prev) => {
const adapted = internalAdaptToolCalls(
event.toolCalls,
prev[event.schedulerId] ?? [],
);
return {
...prev,
[event.schedulerId]: adapted,
};
});
};
messageBus.subscribe(MessageBusType.TOOL_CALLS_UPDATE, handler);
@@ -56,7 +56,8 @@ const validCustomTheme: CustomTheme = {
describe('ThemeManager', () => {
beforeEach(() => {
// Reset themeManager state
// Reset themeManager state and inject mocks
themeManager.reinitialize({ fs, homedir: os.homedir });
themeManager.loadCustomThemes({});
themeManager.setActiveTheme(DEFAULT_THEME.name);
themeManager.setTerminalBackground(undefined);
+47 -7
View File
@@ -61,7 +61,13 @@ class ThemeManager {
private cachedSemanticColors: SemanticColors | undefined;
private lastCacheKey: string | undefined;
constructor() {
private fs: typeof fs;
private homedir: () => string;
constructor(dependencies?: { fs?: typeof fs; homedir?: () => string }) {
this.fs = dependencies?.fs ?? fs;
this.homedir = dependencies?.homedir ?? homedir;
this.availableThemes = [
AyuDark,
AyuLight,
@@ -242,10 +248,44 @@ class ThemeManager {
}
/**
* Sets the active theme.
* @param themeName The name of the theme to set as active.
* @returns True if the theme was successfully set, false otherwise.
* Clears all themes loaded from files.
* This is primarily for testing purposes to reset state between tests.
*/
clearFileThemes(): void {
this.fileThemes.clear();
}
/**
* Re-initializes the ThemeManager with new dependencies.
* This is primarily for testing to allow injecting mocks.
*/
reinitialize(dependencies: { fs?: typeof fs; homedir?: () => string }): void {
if (dependencies.fs) {
this.fs = dependencies.fs;
}
if (dependencies.homedir) {
this.homedir = dependencies.homedir;
}
}
/**
* Resets the ThemeManager state to defaults.
* This is for testing purposes to ensure test isolation.
*/
resetForTesting(dependencies?: {
fs?: typeof fs;
homedir?: () => string;
}): void {
if (dependencies) {
this.reinitialize(dependencies);
}
this.settingsThemes.clear();
this.extensionThemes.clear();
this.fileThemes.clear();
this.activeTheme = DEFAULT_THEME;
this.terminalBackground = undefined;
this.clearCache();
}
setActiveTheme(themeName: string | undefined): boolean {
const theme = this.findThemeByName(themeName);
if (!theme) {
@@ -505,7 +545,7 @@ class ThemeManager {
private loadThemeFromFile(themePath: string): Theme | undefined {
try {
// realpathSync resolves the path and throws if it doesn't exist.
const canonicalPath = fs.realpathSync(path.resolve(themePath));
const canonicalPath = this.fs.realpathSync(path.resolve(themePath));
// 1. Check cache using the canonical path.
if (this.fileThemes.has(canonicalPath)) {
@@ -513,7 +553,7 @@ class ThemeManager {
}
// 2. Perform security check.
const homeDir = path.resolve(homedir());
const homeDir = path.resolve(this.homedir());
if (!canonicalPath.startsWith(homeDir)) {
debugLogger.warn(
`Theme file at "${themePath}" is outside your home directory. ` +
@@ -523,7 +563,7 @@ class ThemeManager {
}
// 3. Read, parse, and validate the theme file.
const themeContent = fs.readFileSync(canonicalPath, 'utf-8');
const themeContent = this.fs.readFileSync(canonicalPath, 'utf-8');
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const customThemeConfig = JSON.parse(themeContent) as CustomTheme;
@@ -0,0 +1,378 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { TerminalCapabilityManager } from './terminalCapabilityManager.js';
import { EventEmitter } from 'node:events';
import {
enableKittyKeyboardProtocol,
enableModifyOtherKeys,
} from '@google/gemini-cli-core';
import * as fs from 'node:fs';
// Mock fs
vi.mock('node:fs', () => ({
writeSync: vi.fn(),
}));
// Mock core
vi.mock('@google/gemini-cli-core', () => ({
debugLogger: {
log: vi.fn(),
warn: vi.fn(),
},
enableKittyKeyboardProtocol: vi.fn(),
disableKittyKeyboardProtocol: vi.fn(),
enableModifyOtherKeys: vi.fn(),
disableModifyOtherKeys: vi.fn(),
enableBracketedPasteMode: vi.fn(),
disableBracketedPasteMode: vi.fn(),
}));
describe('TerminalCapabilityManager', () => {
let stdin: EventEmitter & {
isTTY?: boolean;
isRaw?: boolean;
setRawMode?: (mode: boolean) => void;
removeListener?: (
event: string,
listener: (...args: unknown[]) => void,
) => void;
};
let stdout: { isTTY?: boolean; fd?: number };
// Save original process properties
const originalStdin = process.stdin;
const originalStdout = process.stdout;
beforeEach(() => {
vi.resetAllMocks();
// Reset singleton
TerminalCapabilityManager.resetInstanceForTesting();
// Setup process mocks
stdin = new EventEmitter();
stdin.isTTY = true;
stdin.isRaw = false;
stdin.setRawMode = vi.fn();
stdin.removeListener = vi.fn();
stdout = { isTTY: true, fd: 1 };
// Use defineProperty to mock process.stdin/stdout
Object.defineProperty(process, 'stdin', {
value: stdin,
configurable: true,
});
Object.defineProperty(process, 'stdout', {
value: stdout,
configurable: true,
});
vi.useFakeTimers();
});
afterEach(() => {
vi.useRealTimers();
// Restore original process properties
Object.defineProperty(process, 'stdin', {
value: originalStdin,
configurable: true,
});
Object.defineProperty(process, 'stdout', {
value: originalStdout,
configurable: true,
});
});
it('should detect Kitty support when u response is received', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
// Simulate Kitty response: \x1b[?1u
stdin.emit('data', Buffer.from('\x1b[?1u'));
// Complete detection with DA1
stdin.emit('data', Buffer.from('\x1b[?62c'));
await promise;
expect(manager.isKittyProtocolEnabled()).toBe(true);
});
it('should detect Background Color', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
// Simulate OSC 11 response
// \x1b]11;rgb:0000/ff00/0000\x1b\
// RGB: 0, 255, 0 -> #00ff00
stdin.emit('data', Buffer.from('\x1b]11;rgb:0000/ffff/0000\x1b\\'));
// Complete detection with DA1
stdin.emit('data', Buffer.from('\x1b[?62c'));
await promise;
expect(manager.getTerminalBackgroundColor()).toBe('#00ff00');
});
it('should detect Terminal Name', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
// Simulate Terminal Name response
stdin.emit('data', Buffer.from('\x1bP>|WezTerm 20240203\x1b\\'));
// Complete detection with DA1
stdin.emit('data', Buffer.from('\x1b[?62c'));
await promise;
expect(manager.getTerminalName()).toBe('WezTerm 20240203');
});
it('should complete early if sentinel (DA1) is found', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
stdin.emit('data', Buffer.from('\x1b[?1u'));
stdin.emit('data', Buffer.from('\x1b]11;rgb:0000/0000/0000\x1b\\'));
// Sentinel
stdin.emit('data', Buffer.from('\x1b[?62c'));
// Should resolve without waiting for timeout
await promise;
expect(manager.isKittyProtocolEnabled()).toBe(true);
expect(manager.getTerminalBackgroundColor()).toBe('#000000');
});
it('should timeout if no DA1 (c) is received', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
// Simulate only Kitty response
stdin.emit('data', Buffer.from('\x1b[?1u'));
// Advance to timeout
vi.advanceTimersByTime(1000);
await promise;
expect(manager.isKittyProtocolEnabled()).toBe(true);
});
it('should not detect Kitty if only DA1 (c) is received', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
// Simulate DA1 response only: \x1b[?62;c
stdin.emit('data', Buffer.from('\x1b[?62c'));
await promise;
expect(manager.isKittyProtocolEnabled()).toBe(false);
});
it('should handle split chunks', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
// Split response: \x1b[? 1u
stdin.emit('data', Buffer.from('\x1b[?'));
stdin.emit('data', Buffer.from('1u'));
// Complete with DA1
stdin.emit('data', Buffer.from('\x1b[?62c'));
await promise;
expect(manager.isKittyProtocolEnabled()).toBe(true);
});
describe('modifyOtherKeys detection', () => {
it('should detect modifyOtherKeys support (level 2)', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
// Simulate modifyOtherKeys level 2 response: \x1b[>4;2m
stdin.emit('data', Buffer.from('\x1b[>4;2m'));
// Complete detection with DA1
stdin.emit('data', Buffer.from('\x1b[?62c'));
await promise;
expect(enableModifyOtherKeys).toHaveBeenCalled();
});
it('should not enable modifyOtherKeys for level 0', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
// Simulate modifyOtherKeys level 0 response: \x1b[>4;0m
stdin.emit('data', Buffer.from('\x1b[>4;0m'));
// Complete detection with DA1
stdin.emit('data', Buffer.from('\x1b[?62c'));
await promise;
expect(enableModifyOtherKeys).not.toHaveBeenCalled();
});
it('should prefer Kitty over modifyOtherKeys', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
// Simulate both Kitty and modifyOtherKeys responses
stdin.emit('data', Buffer.from('\x1b[?1u'));
stdin.emit('data', Buffer.from('\x1b[>4;2m'));
// Complete detection with DA1
stdin.emit('data', Buffer.from('\x1b[?62c'));
await promise;
expect(manager.isKittyProtocolEnabled()).toBe(true);
expect(enableKittyKeyboardProtocol).toHaveBeenCalled();
expect(enableModifyOtherKeys).not.toHaveBeenCalled();
});
it('should enable modifyOtherKeys when Kitty not supported', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
// Simulate only modifyOtherKeys response (no Kitty)
stdin.emit('data', Buffer.from('\x1b[>4;2m'));
// Complete detection with DA1
stdin.emit('data', Buffer.from('\x1b[?62c'));
await promise;
expect(manager.isKittyProtocolEnabled()).toBe(false);
expect(enableModifyOtherKeys).toHaveBeenCalled();
});
it('should handle split modifyOtherKeys response chunks', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
// Split response: \x1b[>4;2m
stdin.emit('data', Buffer.from('\x1b[>4;'));
stdin.emit('data', Buffer.from('2m'));
// Complete detection with DA1
stdin.emit('data', Buffer.from('\x1b[?62c'));
await promise;
expect(enableModifyOtherKeys).toHaveBeenCalled();
});
it('should detect modifyOtherKeys with other capabilities', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
stdin.emit('data', Buffer.from('\x1b]11;rgb:1a1a/1a1a/1a1a\x1b\\')); // background color
stdin.emit('data', Buffer.from('\x1bP>|tmux\x1b\\')); // Terminal name
stdin.emit('data', Buffer.from('\x1b[>4;2m')); // modifyOtherKeys
// Complete detection with DA1
stdin.emit('data', Buffer.from('\x1b[?62c'));
await promise;
expect(manager.getTerminalBackgroundColor()).toBe('#1a1a1a');
expect(manager.getTerminalName()).toBe('tmux');
expect(enableModifyOtherKeys).toHaveBeenCalled();
});
it('should not enable modifyOtherKeys without explicit response', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
// Simulate only DA1 response (no specific MOK or Kitty response)
stdin.emit('data', Buffer.from('\x1b[?62c'));
await promise;
expect(manager.isKittyProtocolEnabled()).toBe(false);
expect(enableModifyOtherKeys).not.toHaveBeenCalled();
});
it('should wrap queries in hidden/clear sequence', async () => {
const manager = TerminalCapabilityManager.getInstance();
void manager.detectCapabilities();
expect(fs.writeSync).toHaveBeenCalledWith(
expect.anything(),
// eslint-disable-next-line no-control-regex
expect.stringMatching(/^\x1b\[8m.*\x1b\[2K\r\x1b\[0m$/s),
);
});
});
describe('supportsOsc9Notifications', () => {
const manager = TerminalCapabilityManager.getInstance();
it.each([
{
name: 'WezTerm (terminal name)',
terminalName: 'WezTerm',
env: {},
expected: true,
},
{
name: 'iTerm.app (terminal name)',
terminalName: 'iTerm.app',
env: {},
expected: true,
},
{
name: 'ghostty (terminal name)',
terminalName: 'ghostty',
env: {},
expected: true,
},
{
name: 'kitty (terminal name)',
terminalName: 'kitty',
env: {},
expected: true,
},
{
name: 'some-other-term (terminal name)',
terminalName: 'some-other-term',
env: {},
expected: false,
},
{
name: 'iTerm.app (TERM_PROGRAM)',
terminalName: undefined,
env: { TERM_PROGRAM: 'iTerm.app' },
expected: true,
},
{
name: 'vscode (TERM_PROGRAM)',
terminalName: undefined,
env: { TERM_PROGRAM: 'vscode' },
expected: false,
},
{
name: 'xterm-kitty (TERM)',
terminalName: undefined,
env: { TERM: 'xterm-kitty' },
expected: true,
},
{
name: 'xterm-256color (TERM)',
terminalName: undefined,
env: { TERM: 'xterm-256color' },
expected: false,
},
{
name: 'Windows Terminal (WT_SESSION)',
terminalName: 'iTerm.app',
env: { WT_SESSION: 'some-guid' },
expected: false,
},
])(
'should return $expected for $name',
({ terminalName, env, expected }) => {
vi.spyOn(manager, 'getTerminalName').mockReturnValue(terminalName);
expect(manager.supportsOsc9Notifications(env)).toBe(expected);
},
);
});
});
@@ -129,6 +129,7 @@ describe('GeminiAgent', () => {
mockConfig = {
refreshAuth: vi.fn(),
initialize: vi.fn(),
waitForMcpInit: vi.fn(),
getFileSystemService: vi.fn(),
setFileSystemService: vi.fn(),
getContentGeneratorConfig: vi.fn(),
@@ -486,6 +487,7 @@ describe('Session', () => {
getMessageBus: vi.fn().mockReturnValue(mockMessageBus),
setApprovalMode: vi.fn(),
isPlanEnabled: vi.fn().mockReturnValue(false),
waitForMcpInit: vi.fn(),
} as unknown as Mocked<Config>;
mockConnection = {
sessionUpdate: vi.fn(),
@@ -500,6 +502,28 @@ describe('Session', () => {
vi.clearAllMocks();
});
it('should await MCP initialization before processing a prompt', async () => {
const stream = createMockStream([
{
type: StreamEventType.CHUNK,
value: { candidates: [{ content: { parts: [{ text: 'Hi' }] } }] },
},
]);
mockChat.sendMessageStream.mockResolvedValue(stream);
await session.prompt({
sessionId: 'session-1',
prompt: [{ type: 'text', text: 'test' }],
});
expect(mockConfig.waitForMcpInit).toHaveBeenCalledOnce();
const waitOrder = (mockConfig.waitForMcpInit as Mock).mock
.invocationCallOrder[0];
const sendOrder = (mockChat.sendMessageStream as Mock).mock
.invocationCallOrder[0];
expect(waitOrder).toBeLessThan(sendOrder);
});
it('should handle prompt with text response', async () => {
const stream = createMockStream([
{
@@ -625,6 +649,133 @@ describe('Session', () => {
);
});
it('should use filePath for ACP diff content in permission request', async () => {
const confirmationDetails = {
type: 'edit',
title: 'Confirm Write: test.txt',
fileName: 'test.txt',
filePath: '/tmp/test.txt',
originalContent: 'old',
newContent: 'new',
onConfirm: vi.fn(),
};
mockTool.build.mockReturnValue({
getDescription: () => 'Test Tool',
toolLocations: () => [],
shouldConfirmExecute: vi.fn().mockResolvedValue(confirmationDetails),
execute: vi.fn().mockResolvedValue({ llmContent: 'Tool Result' }),
});
mockConnection.requestPermission.mockResolvedValue({
outcome: {
outcome: 'selected',
optionId: ToolConfirmationOutcome.ProceedOnce,
},
});
const stream1 = createMockStream([
{
type: StreamEventType.CHUNK,
value: {
functionCalls: [{ name: 'test_tool', args: {} }],
},
},
]);
const stream2 = createMockStream([
{
type: StreamEventType.CHUNK,
value: { candidates: [] },
},
]);
mockChat.sendMessageStream
.mockResolvedValueOnce(stream1)
.mockResolvedValueOnce(stream2);
await session.prompt({
sessionId: 'session-1',
prompt: [{ type: 'text', text: 'Call tool' }],
});
expect(mockConnection.requestPermission).toHaveBeenCalledWith(
expect.objectContaining({
toolCall: expect.objectContaining({
content: expect.arrayContaining([
expect.objectContaining({
type: 'diff',
path: '/tmp/test.txt',
oldText: 'old',
newText: 'new',
}),
]),
}),
}),
);
});
it('should use filePath for ACP diff content in tool result', async () => {
mockTool.build.mockReturnValue({
getDescription: () => 'Test Tool',
toolLocations: () => [],
shouldConfirmExecute: vi.fn().mockResolvedValue(null),
execute: vi.fn().mockResolvedValue({
llmContent: 'Tool Result',
returnDisplay: {
fileName: 'test.txt',
filePath: '/tmp/test.txt',
originalContent: 'old',
newContent: 'new',
},
}),
});
const stream1 = createMockStream([
{
type: StreamEventType.CHUNK,
value: {
functionCalls: [{ name: 'test_tool', args: {} }],
},
},
]);
const stream2 = createMockStream([
{
type: StreamEventType.CHUNK,
value: { candidates: [] },
},
]);
mockChat.sendMessageStream
.mockResolvedValueOnce(stream1)
.mockResolvedValueOnce(stream2);
await session.prompt({
sessionId: 'session-1',
prompt: [{ type: 'text', text: 'Call tool' }],
});
const updateCalls = mockConnection.sessionUpdate.mock.calls.map(
(call) => call[0],
);
const toolCallUpdate = updateCalls.find(
(call) => call.update?.sessionUpdate === 'tool_call_update',
);
expect(toolCallUpdate).toEqual(
expect.objectContaining({
update: expect.objectContaining({
content: expect.arrayContaining([
expect.objectContaining({
type: 'diff',
path: '/tmp/test.txt',
oldText: 'old',
newText: 'new',
}),
]),
}),
}),
);
});
it('should handle tool call cancellation by user', async () => {
const confirmationDetails = {
type: 'info',
@@ -521,6 +521,8 @@ export class Session {
const pendingSend = new AbortController();
this.pendingPrompt = pendingSend;
await this.config.waitForMcpInit();
const promptId = Math.random().toString(16).slice(2);
const chat = this.chat;
@@ -700,7 +702,7 @@ export class Session {
if (confirmationDetails.type === 'edit') {
content.push({
type: 'diff',
path: confirmationDetails.fileName,
path: confirmationDetails.filePath,
oldText: confirmationDetails.originalContent,
newText: confirmationDetails.newContent,
_meta: {
@@ -1228,7 +1230,9 @@ function toToolCallContent(toolResult: ToolResult): acp.ToolCallContent | null {
if ('fileName' in toolResult.returnDisplay) {
return {
type: 'diff',
path: toolResult.returnDisplay.fileName,
path:
toolResult.returnDisplay.filePath ??
toolResult.returnDisplay.fileName,
oldText: toolResult.returnDisplay.originalContent,
newText: toolResult.returnDisplay.newContent,
_meta: {
+4
View File
@@ -7,6 +7,7 @@
import { vi, beforeEach, afterEach } from 'vitest';
import { format } from 'node:util';
import { coreEvents } from '@google/gemini-cli-core';
import { themeManager } from './src/ui/themes/theme-manager.js';
// Unset CI environment variable so that ink renders dynamically as it does in a real terminal
if (process.env.CI !== undefined) {
@@ -32,6 +33,9 @@ let consoleErrorSpy: vi.SpyInstance;
let actWarnings: Array<{ message: string; stack: string }> = [];
beforeEach(() => {
// Reset themeManager state to ensure test isolation
themeManager.resetForTesting();
actWarnings = [];
consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
const firstArg = args[0];
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli-core",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.33.0-nightly.20260228.1ca5c05d0",
"description": "Gemini CLI Core",
"license": "Apache-2.0",
"repository": {
@@ -84,7 +84,6 @@ describe('SubAgentInvocation', () => {
params: {},
getDescription: vi.fn(),
toolLocations: vi.fn(),
isSensitive: false,
};
MockSubagentToolWrapper.prototype.build = vi
+9 -2
View File
@@ -716,6 +716,7 @@ export class Config implements McpContext {
private compressionTruncationCounter = 0;
private initialized = false;
private initPromise: Promise<void> | undefined;
private mcpInitializationPromise: Promise<void> | null = null;
readonly storage: Storage;
private readonly fileExclusions: FileExclusions;
private readonly eventEmitter?: EventEmitter;
@@ -1124,7 +1125,7 @@ export class Config implements McpContext {
);
// We do not await this promise so that the CLI can start up even if
// MCP servers are slow to connect.
const mcpInitialization = Promise.allSettled([
this.mcpInitializationPromise = Promise.allSettled([
this.mcpClientManager.startConfiguredMcpServers(),
this.getExtensionLoader().start(this),
]).then((results) => {
@@ -1136,7 +1137,7 @@ export class Config implements McpContext {
});
if (!this.interactive || this.experimentalZedIntegration) {
await mcpInitialization;
await this.mcpInitializationPromise;
}
if (this.skillsSupport) {
@@ -2234,6 +2235,12 @@ export class Config implements McpContext {
return this.experimentalZedIntegration;
}
async waitForMcpInit(): Promise<void> {
if (this.mcpInitializationPromise) {
await this.mcpInitializationPromise;
}
}
getListExtensions(): boolean {
return this.listExtensions;
}
@@ -121,7 +121,6 @@ export interface UpdatePolicy {
argsPattern?: string;
commandPrefix?: string | string[];
mcpName?: string;
isSensitive?: boolean;
}
export interface ToolPolicyRejection {
@@ -243,6 +243,78 @@ describe('LoggingContentGenerator', () => {
expect(errorEvent.error_type).toBe('FatalAuthenticationError');
});
});
describe('Gaxios error parsing', () => {
it('should parse raw ASCII buffer strings in Gaxios errors', async () => {
const req = { contents: [], model: 'gemini-pro' };
// Simulate a Gaxios error with comma-separated ASCII codes
const asciiData = '72,101,108,108,111'; // "Hello"
const gaxiosError = Object.assign(new Error('Gaxios Error'), {
response: { data: asciiData },
});
vi.mocked(wrapped.generateContent).mockRejectedValue(gaxiosError);
await expect(
loggingContentGenerator.generateContent(
req,
'prompt-123',
LlmRole.MAIN,
),
).rejects.toSatisfy((error: unknown) => {
const gError = error as { response: { data: unknown } };
expect(gError.response.data).toBe('Hello');
return true;
});
});
it('should leave data alone if it is not a comma-separated string', async () => {
const req = { contents: [], model: 'gemini-pro' };
const normalData = 'Normal error message';
const gaxiosError = Object.assign(new Error('Gaxios Error'), {
response: { data: normalData },
});
vi.mocked(wrapped.generateContent).mockRejectedValue(gaxiosError);
await expect(
loggingContentGenerator.generateContent(
req,
'prompt-123',
LlmRole.MAIN,
),
).rejects.toSatisfy((error: unknown) => {
const gError = error as { response: { data: unknown } };
expect(gError.response.data).toBe(normalData);
return true;
});
});
it('should leave data alone if parsing fails', async () => {
const req = { contents: [], model: 'gemini-pro' };
const invalidAscii = '72,invalid,101';
const gaxiosError = Object.assign(new Error('Gaxios Error'), {
response: { data: invalidAscii },
});
vi.mocked(wrapped.generateContent).mockRejectedValue(gaxiosError);
await expect(
loggingContentGenerator.generateContent(
req,
'prompt-123',
LlmRole.MAIN,
),
).rejects.toSatisfy((error: unknown) => {
const gError = error as { response: { data: unknown } };
expect(gError.response.data).toBe(invalidAscii);
return true;
});
});
});
});
describe('generateContentStream', () => {
@@ -274,6 +274,32 @@ export class LoggingContentGenerator implements ContentGenerator {
logApiResponse(this.config, event);
}
private _fixGaxiosErrorData(error: unknown): void {
// Fix for raw ASCII buffer strings appearing in dev with the latest
// Gaxios updates.
if (
typeof error === 'object' &&
error !== null &&
'response' in error &&
typeof error.response === 'object' &&
error.response !== null &&
'data' in error.response
) {
const response = error.response as { data: unknown };
const data = response.data;
if (typeof data === 'string' && data.includes(',')) {
try {
const charCodes = data.split(',').map(Number);
if (charCodes.every((code) => !isNaN(code))) {
response.data = String.fromCharCode(...charCodes);
}
} catch (_e) {
// If parsing fails, just leave it alone
}
}
}
}
private _logApiError(
durationMs: number,
error: unknown,
@@ -380,6 +406,9 @@ export class LoggingContentGenerator implements ContentGenerator {
} catch (error) {
spanMetadata.error = error;
const durationMs = Date.now() - startTime;
this._fixGaxiosErrorData(error);
this._logApiError(
durationMs,
error,
@@ -447,6 +476,9 @@ export class LoggingContentGenerator implements ContentGenerator {
);
} catch (error) {
const durationMs = Date.now() - startTime;
this._fixGaxiosErrorData(error);
this._logApiError(
durationMs,
error,
+1
View File
@@ -188,6 +188,7 @@ export { OAuthUtils } from './mcp/oauth-utils.js';
export * from './telemetry/index.js';
export * from './telemetry/billingEvents.js';
export { logBillingEvent } from './telemetry/loggers.js';
export * from './telemetry/constants.js';
export { sessionId, createSessionId } from './utils/session.js';
export * from './utils/compatibility.js';
export * from './utils/browser.js';
+16 -6
View File
@@ -17,6 +17,16 @@ import {
SHELL_TOOL_NAME,
WRITE_FILE_TOOL_NAME,
WRITE_TODOS_TOOL_NAME,
GREP_PARAM_TOTAL_MAX_MATCHES,
GREP_PARAM_INCLUDE_PATTERN,
GREP_PARAM_EXCLUDE_PATTERN,
GREP_PARAM_CONTEXT,
GREP_PARAM_BEFORE,
GREP_PARAM_AFTER,
READ_FILE_PARAM_START_LINE,
READ_FILE_PARAM_END_LINE,
SHELL_PARAM_IS_BACKGROUND,
EDIT_PARAM_OLD_STRING,
} from '../tools/tool-names.js';
import type { HierarchicalMemory } from '../config/memory.js';
import { DEFAULT_CONTEXT_FILENAME } from '../tools/memoryTool.js';
@@ -183,16 +193,16 @@ Use the following guidelines to optimize your search and read patterns.
- Prefer using tools like ${GREP_TOOL_NAME} to identify points of interest instead of reading lots of files individually.
- If you need to read multiple ranges in a file, do so parallel, in as few turns as possible.
- It is more important to reduce extra turns, but please also try to minimize unnecessarily large file reads and search results, when doing so doesn't result in extra turns. Do this by always providing conservative limits and scopes to tools like ${READ_FILE_TOOL_NAME} and ${GREP_TOOL_NAME}.
- ${READ_FILE_TOOL_NAME} fails if old_string is ambiguous, causing extra turns. Take care to read enough with ${READ_FILE_TOOL_NAME} and ${GREP_TOOL_NAME} to make the edit unambiguous.
- ${READ_FILE_TOOL_NAME} fails if ${EDIT_PARAM_OLD_STRING} is ambiguous, causing extra turns. Take care to read enough with ${READ_FILE_TOOL_NAME} and ${GREP_TOOL_NAME} to make the edit unambiguous.
- You can compensate for the risk of missing results with scoped or limited searches by doing multiple searches in parallel.
- Your primary goal is still to do your best quality work. Efficiency is an important, but secondary concern.
</guidelines>
<examples>
- **Searching:** utilize search tools like ${GREP_TOOL_NAME} and ${GLOB_TOOL_NAME} with a conservative result count (\`total_max_matches\`) and a narrow scope (\`include_pattern\` and \`exclude_pattern\` parameters).
- **Searching and editing:** utilize search tools like ${GREP_TOOL_NAME} with a conservative result count and a narrow scope. Use \`context\`, \`before\`, and/or \`after\` to request enough context to avoid the need to read the file before editing matches.
- **Searching:** utilize search tools like ${GREP_TOOL_NAME} and ${GLOB_TOOL_NAME} with a conservative result count (\`${GREP_PARAM_TOTAL_MAX_MATCHES}\`) and a narrow scope (\`${GREP_PARAM_INCLUDE_PATTERN}\` and \`${GREP_PARAM_EXCLUDE_PATTERN}\` parameters).
- **Searching and editing:** utilize search tools like ${GREP_TOOL_NAME} with a conservative result count and a narrow scope. Use \`${GREP_PARAM_CONTEXT}\`, \`${GREP_PARAM_BEFORE}\`, and/or \`${GREP_PARAM_AFTER}\` to request enough context to avoid the need to read the file before editing matches.
- **Understanding:** minimize turns needed to understand a file. It's most efficient to read small files in their entirety.
- **Large files:** utilize search tools like ${GREP_TOOL_NAME} and/or ${READ_FILE_TOOL_NAME} called in parallel with 'start_line' and 'end_line' to reduce the impact on context. Minimize extra turns, unless unavoidable due to the file being too large.
- **Large files:** utilize search tools like ${GREP_TOOL_NAME} and/or ${READ_FILE_TOOL_NAME} called in parallel with '${READ_FILE_PARAM_START_LINE}' and '${READ_FILE_PARAM_END_LINE}' to reduce the impact on context. Minimize extra turns, unless unavoidable due to the file being too large.
- **Navigating:** read the minimum required to not require additional turns spent reading the file.
</examples>
@@ -659,11 +669,11 @@ function toolUsageInteractive(
? ' If you choose to execute an interactive command consider letting the user know they can press `ctrl + f` to focus into the shell to provide input.'
: '';
return `
- **Background Processes:** To run a command in the background, set the \`is_background\` parameter to true. If unsure, ask the user.
- **Background Processes:** To run a command in the background, set the \`${SHELL_PARAM_IS_BACKGROUND}\` parameter to true. If unsure, ask the user.
- **Interactive Commands:** Always prefer non-interactive commands (e.g., using 'run once' or 'CI' flags for test runners to avoid persistent watch modes or 'git --no-pager') unless a persistent process is specifically required; however, some commands are only interactive and expect user input during their execution (e.g. ssh, vim).${ctrlF}`;
}
return `
- **Background Processes:** To run a command in the background, set the \`is_background\` parameter to true.
- **Background Processes:** To run a command in the background, set the \`${SHELL_PARAM_IS_BACKGROUND}\` parameter to true.
- **Interactive Commands:** Always prefer non-interactive commands (e.g., using 'run once' or 'CI' flags for test runners to avoid persistent watch modes or 'git --no-pager') unless a persistent process is specifically required; however, some commands are only interactive and expect user input during their execution (e.g. ssh, vim).`;
}
+2 -11
View File
@@ -43,15 +43,7 @@ class ActivateSkillToolInvocation extends BaseToolInvocation<
_toolName?: string,
_toolDisplayName?: string,
) {
super(
params,
messageBus,
_toolName,
_toolDisplayName,
undefined,
undefined,
true, // ActivateSkill is always sensitive
);
super(params, messageBus, _toolName, _toolDisplayName);
}
getDescription(): string {
@@ -193,14 +185,13 @@ export class ActivateSkillTool extends BaseDeclarativeTool<
messageBus: MessageBus,
_toolName?: string,
_toolDisplayName?: string,
_isSensitive?: boolean,
): ToolInvocation<ActivateSkillToolParams, ToolResult> {
return new ActivateSkillToolInvocation(
this.config,
params,
messageBus,
_toolName,
_toolDisplayName,
_toolDisplayName ?? 'Activate Skill',
);
}
-1
View File
@@ -293,7 +293,6 @@ describe('AskUserTool', () => {
getDescription: vi.fn().mockReturnValue(''),
toolLocations: vi.fn().mockReturnValue([]),
shouldConfirmExecute: vi.fn().mockResolvedValue(false),
isSensitive: false,
};
const buildSpy = vi.spyOn(tool, 'build').mockReturnValue(mockInvocation);
@@ -10,25 +10,115 @@
*/
// ============================================================================
// TOOL NAMES
// SHARED PARAMETER NAMES (used by multiple tools)
// ============================================================================
export const PARAM_FILE_PATH = 'file_path';
export const PARAM_DIR_PATH = 'dir_path';
export const PARAM_PATTERN = 'pattern';
export const PARAM_CASE_SENSITIVE = 'case_sensitive';
export const PARAM_RESPECT_GIT_IGNORE = 'respect_git_ignore';
export const PARAM_RESPECT_GEMINI_IGNORE = 'respect_gemini_ignore';
export const PARAM_FILE_FILTERING_OPTIONS = 'file_filtering_options';
export const PARAM_DESCRIPTION = 'description';
// ============================================================================
// TOOL NAMES & TOOL-SPECIFIC PARAMETER NAMES
// ============================================================================
// -- glob --
export const GLOB_TOOL_NAME = 'glob';
// -- grep_search --
export const GREP_TOOL_NAME = 'grep_search';
export const GREP_PARAM_INCLUDE_PATTERN = 'include_pattern';
export const GREP_PARAM_EXCLUDE_PATTERN = 'exclude_pattern';
export const GREP_PARAM_NAMES_ONLY = 'names_only';
export const GREP_PARAM_MAX_MATCHES_PER_FILE = 'max_matches_per_file';
export const GREP_PARAM_TOTAL_MAX_MATCHES = 'total_max_matches';
// ripgrep only
export const GREP_PARAM_FIXED_STRINGS = 'fixed_strings';
export const GREP_PARAM_CONTEXT = 'context';
export const GREP_PARAM_AFTER = 'after';
export const GREP_PARAM_BEFORE = 'before';
export const GREP_PARAM_NO_IGNORE = 'no_ignore';
// -- list_directory --
export const LS_TOOL_NAME = 'list_directory';
export const LS_PARAM_IGNORE = 'ignore';
// -- read_file --
export const READ_FILE_TOOL_NAME = 'read_file';
export const READ_FILE_PARAM_START_LINE = 'start_line';
export const READ_FILE_PARAM_END_LINE = 'end_line';
// -- run_shell_command --
export const SHELL_TOOL_NAME = 'run_shell_command';
export const SHELL_PARAM_COMMAND = 'command';
export const SHELL_PARAM_IS_BACKGROUND = 'is_background';
// -- write_file --
export const WRITE_FILE_TOOL_NAME = 'write_file';
export const WRITE_FILE_PARAM_CONTENT = 'content';
// -- replace (edit) --
export const EDIT_TOOL_NAME = 'replace';
export const EDIT_PARAM_INSTRUCTION = 'instruction';
export const EDIT_PARAM_OLD_STRING = 'old_string';
export const EDIT_PARAM_NEW_STRING = 'new_string';
export const EDIT_PARAM_ALLOW_MULTIPLE = 'allow_multiple';
// -- google_web_search --
export const WEB_SEARCH_TOOL_NAME = 'google_web_search';
export const WEB_SEARCH_PARAM_QUERY = 'query';
// -- write_todos --
export const WRITE_TODOS_TOOL_NAME = 'write_todos';
export const WEB_FETCH_TOOL_NAME = 'web_fetch';
export const READ_MANY_FILES_TOOL_NAME = 'read_many_files';
export const TODOS_PARAM_TODOS = 'todos';
export const TODOS_ITEM_PARAM_DESCRIPTION = 'description';
export const TODOS_ITEM_PARAM_STATUS = 'status';
// -- web_fetch --
export const WEB_FETCH_TOOL_NAME = 'web_fetch';
export const WEB_FETCH_PARAM_PROMPT = 'prompt';
// -- read_many_files --
export const READ_MANY_FILES_TOOL_NAME = 'read_many_files';
export const READ_MANY_PARAM_INCLUDE = 'include';
export const READ_MANY_PARAM_EXCLUDE = 'exclude';
export const READ_MANY_PARAM_RECURSIVE = 'recursive';
export const READ_MANY_PARAM_USE_DEFAULT_EXCLUDES = 'useDefaultExcludes';
// -- save_memory --
export const MEMORY_TOOL_NAME = 'save_memory';
export const MEMORY_PARAM_FACT = 'fact';
// -- get_internal_docs --
export const GET_INTERNAL_DOCS_TOOL_NAME = 'get_internal_docs';
export const DOCS_PARAM_PATH = 'path';
// -- activate_skill --
export const ACTIVATE_SKILL_TOOL_NAME = 'activate_skill';
export const SKILL_PARAM_NAME = 'name';
// -- ask_user --
export const ASK_USER_TOOL_NAME = 'ask_user';
export const ASK_USER_PARAM_QUESTIONS = 'questions';
// ask_user question item params
export const ASK_USER_QUESTION_PARAM_QUESTION = 'question';
export const ASK_USER_QUESTION_PARAM_HEADER = 'header';
export const ASK_USER_QUESTION_PARAM_TYPE = 'type';
export const ASK_USER_QUESTION_PARAM_OPTIONS = 'options';
export const ASK_USER_QUESTION_PARAM_MULTI_SELECT = 'multiSelect';
export const ASK_USER_QUESTION_PARAM_PLACEHOLDER = 'placeholder';
// ask_user option item params
export const ASK_USER_OPTION_PARAM_LABEL = 'label';
export const ASK_USER_OPTION_PARAM_DESCRIPTION = 'description';
// -- exit_plan_mode --
export const EXIT_PLAN_MODE_TOOL_NAME = 'exit_plan_mode';
export const EXIT_PLAN_PARAM_PLAN_PATH = 'plan_path';
// -- enter_plan_mode --
export const ENTER_PLAN_MODE_TOOL_NAME = 'enter_plan_mode';
export const PLAN_MODE_PARAM_REASON = 'reason';
@@ -38,6 +38,59 @@ export {
ASK_USER_TOOL_NAME,
EXIT_PLAN_MODE_TOOL_NAME,
ENTER_PLAN_MODE_TOOL_NAME,
// Shared parameter names
PARAM_FILE_PATH,
PARAM_DIR_PATH,
PARAM_PATTERN,
PARAM_CASE_SENSITIVE,
PARAM_RESPECT_GIT_IGNORE,
PARAM_RESPECT_GEMINI_IGNORE,
PARAM_FILE_FILTERING_OPTIONS,
PARAM_DESCRIPTION,
// Tool-specific parameter names
READ_FILE_PARAM_START_LINE,
READ_FILE_PARAM_END_LINE,
WRITE_FILE_PARAM_CONTENT,
GREP_PARAM_INCLUDE_PATTERN,
GREP_PARAM_EXCLUDE_PATTERN,
GREP_PARAM_NAMES_ONLY,
GREP_PARAM_MAX_MATCHES_PER_FILE,
GREP_PARAM_TOTAL_MAX_MATCHES,
GREP_PARAM_FIXED_STRINGS,
GREP_PARAM_CONTEXT,
GREP_PARAM_AFTER,
GREP_PARAM_BEFORE,
GREP_PARAM_NO_IGNORE,
EDIT_PARAM_INSTRUCTION,
EDIT_PARAM_OLD_STRING,
EDIT_PARAM_NEW_STRING,
EDIT_PARAM_ALLOW_MULTIPLE,
LS_PARAM_IGNORE,
SHELL_PARAM_COMMAND,
SHELL_PARAM_IS_BACKGROUND,
WEB_SEARCH_PARAM_QUERY,
WEB_FETCH_PARAM_PROMPT,
READ_MANY_PARAM_INCLUDE,
READ_MANY_PARAM_EXCLUDE,
READ_MANY_PARAM_RECURSIVE,
READ_MANY_PARAM_USE_DEFAULT_EXCLUDES,
MEMORY_PARAM_FACT,
TODOS_PARAM_TODOS,
TODOS_ITEM_PARAM_DESCRIPTION,
TODOS_ITEM_PARAM_STATUS,
DOCS_PARAM_PATH,
ASK_USER_PARAM_QUESTIONS,
ASK_USER_QUESTION_PARAM_QUESTION,
ASK_USER_QUESTION_PARAM_HEADER,
ASK_USER_QUESTION_PARAM_TYPE,
ASK_USER_QUESTION_PARAM_OPTIONS,
ASK_USER_QUESTION_PARAM_MULTI_SELECT,
ASK_USER_QUESTION_PARAM_PLACEHOLDER,
ASK_USER_OPTION_PARAM_LABEL,
ASK_USER_OPTION_PARAM_DESCRIPTION,
PLAN_MODE_PARAM_REASON,
EXIT_PLAN_PARAM_PLAN_PATH,
SKILL_PARAM_NAME,
} from './base-declarations.js';
// Re-export sets for compatibility
@@ -17,6 +17,12 @@ import {
SHELL_TOOL_NAME,
EXIT_PLAN_MODE_TOOL_NAME,
ACTIVATE_SKILL_TOOL_NAME,
SHELL_PARAM_COMMAND,
PARAM_DESCRIPTION,
PARAM_DIR_PATH,
SHELL_PARAM_IS_BACKGROUND,
EXIT_PLAN_PARAM_PLAN_PATH,
SKILL_PARAM_NAME,
} from './base-declarations.js';
/**
@@ -47,12 +53,12 @@ export function getShellToolDescription(
if (os.platform() === 'win32') {
const backgroundInstructions = enableInteractiveShell
? 'To run a command in the background, set the `is_background` parameter to true. Do NOT use PowerShell background constructs.'
? `To run a command in the background, set the \`${SHELL_PARAM_IS_BACKGROUND}\` parameter to true. Do NOT use PowerShell background constructs.`
: 'Command can start background processes using PowerShell constructs such as `Start-Process -NoNewWindow` or `Start-Job`.';
return `This tool executes a given shell command as \`powershell.exe -NoProfile -Command <command>\`. ${backgroundInstructions}${efficiencyGuidelines}${returnedInfo}`;
} else {
const backgroundInstructions = enableInteractiveShell
? 'To run a command in the background, set the `is_background` parameter to true. Do NOT use `&` to background commands.'
? `To run a command in the background, set the \`${SHELL_PARAM_IS_BACKGROUND}\` parameter to true. Do NOT use \`&\` to background commands.`
: 'Command can start background processes using `&`.';
return `This tool executes a given shell command as \`bash -c <command>\`. ${backgroundInstructions} Command is executed as a subprocess that leads its own process group. Command process group can be terminated as \`kill -- -PGID\` or signaled as \`kill -s SIGNAL -- -PGID\`.${efficiencyGuidelines}${returnedInfo}`;
}
@@ -84,27 +90,27 @@ export function getShellDeclaration(
parametersJsonSchema: {
type: 'object',
properties: {
command: {
[SHELL_PARAM_COMMAND]: {
type: 'string',
description: getCommandDescription(),
},
description: {
[PARAM_DESCRIPTION]: {
type: 'string',
description:
'Brief description of the command for the user. Be specific and concise. Ideally a single sentence. Can be up to 3 sentences for clarity. No line breaks.',
},
dir_path: {
[PARAM_DIR_PATH]: {
type: 'string',
description:
'(OPTIONAL) The path of the directory to run the command in. If not provided, the project root directory is used. Must be a directory within the workspace and must already exist.',
},
is_background: {
[SHELL_PARAM_IS_BACKGROUND]: {
type: 'boolean',
description:
'Set to true if this command should be run in the background (e.g. for long-running servers or watchers). The command will be started, allowed to run for a brief moment to check for immediate errors, and then moved to the background.',
},
},
required: ['command'],
required: [SHELL_PARAM_COMMAND],
},
};
}
@@ -121,9 +127,9 @@ export function getExitPlanModeDeclaration(
'Finalizes the planning phase and transitions to implementation by presenting the plan for user approval. This tool MUST be used to exit Plan Mode before any source code edits can be performed. Call this whenever a plan is ready or the user requests implementation.',
parametersJsonSchema: {
type: 'object',
required: ['plan_path'],
required: [EXIT_PLAN_PARAM_PLAN_PATH],
properties: {
plan_path: {
[EXIT_PLAN_PARAM_PLAN_PATH]: {
type: 'string',
description: `The file path to the finalized plan (e.g., "${plansDir}/feature-x.md"). This path MUST be within the designated plans directory: ${plansDir}/`,
},
@@ -146,11 +152,13 @@ export function getActivateSkillDeclaration(
let schema: z.ZodTypeAny;
if (skillNames.length === 0) {
schema = z.object({
name: z.string().describe('No skills are currently available.'),
[SKILL_PARAM_NAME]: z
.string()
.describe('No skills are currently available.'),
});
} else {
schema = z.object({
name: z
[SKILL_PARAM_NAME]: z
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
.enum(skillNames as [string, ...string[]])
.describe('The name of the skill to activate.'),
@@ -25,6 +25,54 @@ import {
GET_INTERNAL_DOCS_TOOL_NAME,
ASK_USER_TOOL_NAME,
ENTER_PLAN_MODE_TOOL_NAME,
// Shared parameter names
PARAM_FILE_PATH,
PARAM_DIR_PATH,
PARAM_PATTERN,
PARAM_CASE_SENSITIVE,
PARAM_RESPECT_GIT_IGNORE,
PARAM_RESPECT_GEMINI_IGNORE,
PARAM_FILE_FILTERING_OPTIONS,
// Tool-specific parameter names
READ_FILE_PARAM_START_LINE,
READ_FILE_PARAM_END_LINE,
WRITE_FILE_PARAM_CONTENT,
GREP_PARAM_INCLUDE_PATTERN,
GREP_PARAM_EXCLUDE_PATTERN,
GREP_PARAM_NAMES_ONLY,
GREP_PARAM_MAX_MATCHES_PER_FILE,
GREP_PARAM_TOTAL_MAX_MATCHES,
GREP_PARAM_FIXED_STRINGS,
GREP_PARAM_CONTEXT,
GREP_PARAM_AFTER,
GREP_PARAM_BEFORE,
GREP_PARAM_NO_IGNORE,
EDIT_PARAM_INSTRUCTION,
EDIT_PARAM_OLD_STRING,
EDIT_PARAM_NEW_STRING,
EDIT_PARAM_ALLOW_MULTIPLE,
LS_PARAM_IGNORE,
WEB_SEARCH_PARAM_QUERY,
WEB_FETCH_PARAM_PROMPT,
READ_MANY_PARAM_INCLUDE,
READ_MANY_PARAM_EXCLUDE,
READ_MANY_PARAM_RECURSIVE,
READ_MANY_PARAM_USE_DEFAULT_EXCLUDES,
MEMORY_PARAM_FACT,
TODOS_PARAM_TODOS,
TODOS_ITEM_PARAM_DESCRIPTION,
TODOS_ITEM_PARAM_STATUS,
DOCS_PARAM_PATH,
ASK_USER_PARAM_QUESTIONS,
ASK_USER_QUESTION_PARAM_QUESTION,
ASK_USER_QUESTION_PARAM_HEADER,
ASK_USER_QUESTION_PARAM_TYPE,
ASK_USER_QUESTION_PARAM_OPTIONS,
ASK_USER_QUESTION_PARAM_MULTI_SELECT,
ASK_USER_QUESTION_PARAM_PLACEHOLDER,
ASK_USER_OPTION_PARAM_LABEL,
ASK_USER_OPTION_PARAM_DESCRIPTION,
PLAN_MODE_PARAM_REASON,
} from '../base-declarations.js';
import {
getShellDeclaration,
@@ -39,22 +87,22 @@ export const DEFAULT_LEGACY_SET: CoreToolSet = {
parametersJsonSchema: {
type: 'object',
properties: {
file_path: {
[PARAM_FILE_PATH]: {
description: 'The path to the file to read.',
type: 'string',
},
start_line: {
[READ_FILE_PARAM_START_LINE]: {
description:
'Optional: The 1-based line number to start reading from.',
type: 'number',
},
end_line: {
[READ_FILE_PARAM_END_LINE]: {
description:
'Optional: The 1-based line number to end reading at (inclusive).',
type: 'number',
},
},
required: ['file_path'],
required: [PARAM_FILE_PATH],
},
},
@@ -66,17 +114,17 @@ export const DEFAULT_LEGACY_SET: CoreToolSet = {
parametersJsonSchema: {
type: 'object',
properties: {
file_path: {
[PARAM_FILE_PATH]: {
description: 'The path to the file to write to.',
type: 'string',
},
content: {
[WRITE_FILE_PARAM_CONTENT]: {
description:
"The content to write to the file. Do not use omission placeholders like '(rest of methods ...)', '...', or 'unchanged code'; provide complete literal content.",
type: 'string',
},
},
required: ['file_path', 'content'],
required: [PARAM_FILE_PATH, WRITE_FILE_PARAM_CONTENT],
},
},
@@ -87,43 +135,43 @@ export const DEFAULT_LEGACY_SET: CoreToolSet = {
parametersJsonSchema: {
type: 'object',
properties: {
pattern: {
[PARAM_PATTERN]: {
description: `The regular expression (regex) pattern to search for within file contents (e.g., 'function\\s+myFunction', 'import\\s+\\{.*\\}\\s+from\\s+.*').`,
type: 'string',
},
dir_path: {
[PARAM_DIR_PATH]: {
description:
'Optional: The absolute path to the directory to search within. If omitted, searches the current working directory.',
type: 'string',
},
include_pattern: {
[GREP_PARAM_INCLUDE_PATTERN]: {
description: `Optional: A glob pattern to filter which files are searched (e.g., '*.js', '*.{ts,tsx}', 'src/**'). If omitted, searches all files (respecting potential global ignores).`,
type: 'string',
},
exclude_pattern: {
[GREP_PARAM_EXCLUDE_PATTERN]: {
description:
'Optional: A regular expression pattern to exclude from the search results. If a line matches both the pattern and the exclude_pattern, it will be omitted.',
type: 'string',
},
names_only: {
[GREP_PARAM_NAMES_ONLY]: {
description:
'Optional: If true, only the file paths of the matches will be returned, without the line content or line numbers. This is useful for gathering a list of files.',
type: 'boolean',
},
max_matches_per_file: {
[GREP_PARAM_MAX_MATCHES_PER_FILE]: {
description:
'Optional: Maximum number of matches to return per file. Use this to prevent being overwhelmed by repetitive matches in large files.',
type: 'integer',
minimum: 1,
},
total_max_matches: {
[GREP_PARAM_TOTAL_MAX_MATCHES]: {
description:
'Optional: Maximum number of total matches to return. Use this to limit the overall size of the response. Defaults to 100 if omitted.',
type: 'integer',
minimum: 1,
},
},
required: ['pattern'],
required: [PARAM_PATTERN],
},
},
@@ -134,76 +182,76 @@ export const DEFAULT_LEGACY_SET: CoreToolSet = {
parametersJsonSchema: {
type: 'object',
properties: {
pattern: {
[PARAM_PATTERN]: {
description: `The pattern to search for. By default, treated as a Rust-flavored regular expression. Use '\\b' for precise symbol matching (e.g., '\\bMatchMe\\b').`,
type: 'string',
},
dir_path: {
[PARAM_DIR_PATH]: {
description:
"Directory or file to search. Directories are searched recursively. Relative paths are resolved against current working directory. Defaults to current working directory ('.') if omitted.",
type: 'string',
},
include_pattern: {
[GREP_PARAM_INCLUDE_PATTERN]: {
description:
"Glob pattern to filter files (e.g., '*.ts', 'src/**'). Recommended for large repositories to reduce noise. Defaults to all files if omitted.",
type: 'string',
},
exclude_pattern: {
[GREP_PARAM_EXCLUDE_PATTERN]: {
description:
'Optional: A regular expression pattern to exclude from the search results. If a line matches both the pattern and the exclude_pattern, it will be omitted.',
type: 'string',
},
names_only: {
[GREP_PARAM_NAMES_ONLY]: {
description:
'Optional: If true, only the file paths of the matches will be returned, without the line content or line numbers. This is useful for gathering a list of files.',
type: 'boolean',
},
case_sensitive: {
[PARAM_CASE_SENSITIVE]: {
description:
'If true, search is case-sensitive. Defaults to false (ignore case) if omitted.',
type: 'boolean',
},
fixed_strings: {
[GREP_PARAM_FIXED_STRINGS]: {
description:
'If true, treats the `pattern` as a literal string instead of a regular expression. Defaults to false (basic regex) if omitted.',
type: 'boolean',
},
context: {
[GREP_PARAM_CONTEXT]: {
description:
'Show this many lines of context around each match (equivalent to grep -C). Defaults to 0 if omitted.',
type: 'integer',
},
after: {
[GREP_PARAM_AFTER]: {
description:
'Show this many lines after each match (equivalent to grep -A). Defaults to 0 if omitted.',
type: 'integer',
minimum: 0,
},
before: {
[GREP_PARAM_BEFORE]: {
description:
'Show this many lines before each match (equivalent to grep -B). Defaults to 0 if omitted.',
type: 'integer',
minimum: 0,
},
no_ignore: {
[GREP_PARAM_NO_IGNORE]: {
description:
'If true, searches all files including those usually ignored (like in .gitignore, build/, dist/, etc). Defaults to false if omitted.',
type: 'boolean',
},
max_matches_per_file: {
[GREP_PARAM_MAX_MATCHES_PER_FILE]: {
description:
'Optional: Maximum number of matches to return per file. Use this to prevent being overwhelmed by repetitive matches in large files.',
type: 'integer',
minimum: 1,
},
total_max_matches: {
[GREP_PARAM_TOTAL_MAX_MATCHES]: {
description:
'Optional: Maximum number of total matches to return. Use this to limit the overall size of the response. Defaults to 100 if omitted.',
type: 'integer',
minimum: 1,
},
},
required: ['pattern'],
required: [PARAM_PATTERN],
},
},
@@ -214,33 +262,33 @@ export const DEFAULT_LEGACY_SET: CoreToolSet = {
parametersJsonSchema: {
type: 'object',
properties: {
pattern: {
[PARAM_PATTERN]: {
description:
"The glob pattern to match against (e.g., '**/*.py', 'docs/*.md').",
type: 'string',
},
dir_path: {
[PARAM_DIR_PATH]: {
description:
'Optional: The absolute path to the directory to search within. If omitted, searches the root directory.',
type: 'string',
},
case_sensitive: {
[PARAM_CASE_SENSITIVE]: {
description:
'Optional: Whether the search should be case-sensitive. Defaults to false.',
type: 'boolean',
},
respect_git_ignore: {
[PARAM_RESPECT_GIT_IGNORE]: {
description:
'Optional: Whether to respect .gitignore patterns when finding files. Only available in git repositories. Defaults to true.',
type: 'boolean',
},
respect_gemini_ignore: {
[PARAM_RESPECT_GEMINI_IGNORE]: {
description:
'Optional: Whether to respect .geminiignore patterns when finding files. Defaults to true.',
type: 'boolean',
},
},
required: ['pattern'],
required: [PARAM_PATTERN],
},
},
@@ -251,28 +299,28 @@ export const DEFAULT_LEGACY_SET: CoreToolSet = {
parametersJsonSchema: {
type: 'object',
properties: {
dir_path: {
[PARAM_DIR_PATH]: {
description: 'The path to the directory to list',
type: 'string',
},
ignore: {
[LS_PARAM_IGNORE]: {
description: 'List of glob patterns to ignore',
items: {
type: 'string',
},
type: 'array',
},
file_filtering_options: {
[PARAM_FILE_FILTERING_OPTIONS]: {
description:
'Optional: Whether to respect ignore patterns from .gitignore or .geminiignore',
type: 'object',
properties: {
respect_git_ignore: {
[PARAM_RESPECT_GIT_IGNORE]: {
description:
'Optional: Whether to respect .gitignore patterns when listing files. Only available in git repositories. Defaults to true.',
type: 'boolean',
},
respect_gemini_ignore: {
[PARAM_RESPECT_GEMINI_IGNORE]: {
description:
'Optional: Whether to respect .geminiignore patterns when listing files. Defaults to true.',
type: 'boolean',
@@ -280,7 +328,7 @@ export const DEFAULT_LEGACY_SET: CoreToolSet = {
},
},
},
required: ['dir_path'],
required: [PARAM_DIR_PATH],
},
},
@@ -304,11 +352,11 @@ export const DEFAULT_LEGACY_SET: CoreToolSet = {
parametersJsonSchema: {
type: 'object',
properties: {
file_path: {
[PARAM_FILE_PATH]: {
description: 'The path to the file to modify.',
type: 'string',
},
instruction: {
[EDIT_PARAM_INSTRUCTION]: {
description: `A clear, semantic instruction for the code change, acting as a high-quality prompt for an expert LLM assistant. It must be self-contained and explain the goal of the change.
A good instruction should concisely answer:
@@ -326,23 +374,28 @@ A good instruction should concisely answer:
`,
type: 'string',
},
old_string: {
[EDIT_PARAM_OLD_STRING]: {
description:
'The exact literal text to replace, preferably unescaped. For single replacements (default), include at least 3 lines of context BEFORE and AFTER the target text, matching whitespace and indentation precisely. If this string is not the exact literal text (i.e. you escaped it) or does not match exactly, the tool will fail.',
type: 'string',
},
new_string: {
[EDIT_PARAM_NEW_STRING]: {
description:
"The exact literal text to replace `old_string` with, preferably unescaped. Provide the EXACT text. Ensure the resulting code is correct and idiomatic. Do not use omission placeholders like '(rest of methods ...)', '...', or 'unchanged code'; provide exact literal code.",
type: 'string',
},
allow_multiple: {
[EDIT_PARAM_ALLOW_MULTIPLE]: {
type: 'boolean',
description:
'If true, the tool will replace all occurrences of `old_string`. If false (default), it will only succeed if exactly one occurrence is found.',
},
},
required: ['file_path', 'instruction', 'old_string', 'new_string'],
required: [
PARAM_FILE_PATH,
EDIT_PARAM_INSTRUCTION,
EDIT_PARAM_OLD_STRING,
EDIT_PARAM_NEW_STRING,
],
},
},
@@ -353,12 +406,12 @@ A good instruction should concisely answer:
parametersJsonSchema: {
type: 'object',
properties: {
query: {
[WEB_SEARCH_PARAM_QUERY]: {
type: 'string',
description: 'The search query to find information on the web.',
},
},
required: ['query'],
required: [WEB_SEARCH_PARAM_QUERY],
},
},
@@ -369,13 +422,13 @@ A good instruction should concisely answer:
parametersJsonSchema: {
type: 'object',
properties: {
prompt: {
[WEB_FETCH_PARAM_PROMPT]: {
description:
'A comprehensive prompt that includes the URL(s) (up to 20) to fetch and specific instructions on how to process their content (e.g., "Summarize https://example.com/article and extract key points from https://another.com/data"). All URLs to be fetched must be valid and complete, starting with "http://" or "https://", and be fully-formed with a valid hostname (e.g., a domain name like "example.com" or an IP address). For example, "https://example.com" is valid, but "example.com" is not.',
type: 'string',
},
},
required: ['prompt'],
required: [WEB_FETCH_PARAM_PROMPT],
},
},
@@ -394,7 +447,7 @@ Use this tool when the user's query implies needing the content of several files
parametersJsonSchema: {
type: 'object',
properties: {
include: {
[READ_MANY_PARAM_INCLUDE]: {
type: 'array',
items: {
type: 'string',
@@ -404,7 +457,7 @@ Use this tool when the user's query implies needing the content of several files
description:
'An array of glob patterns or paths. Examples: ["src/**/*.ts"], ["README.md", "docs/"]',
},
exclude: {
[READ_MANY_PARAM_EXCLUDE]: {
type: 'array',
items: {
type: 'string',
@@ -414,30 +467,30 @@ Use this tool when the user's query implies needing the content of several files
'Optional. Glob patterns for files/directories to exclude. Added to default excludes if useDefaultExcludes is true. Example: "**/*.log", "temp/"',
default: [],
},
recursive: {
[READ_MANY_PARAM_RECURSIVE]: {
type: 'boolean',
description:
'Optional. Whether to search recursively (primarily controlled by `**` in glob patterns). Defaults to true.',
default: true,
},
useDefaultExcludes: {
[READ_MANY_PARAM_USE_DEFAULT_EXCLUDES]: {
type: 'boolean',
description:
'Optional. Whether to apply a list of default exclusion patterns (e.g., node_modules, .git, binary files). Defaults to true.',
default: true,
},
file_filtering_options: {
[PARAM_FILE_FILTERING_OPTIONS]: {
description:
'Whether to respect ignore patterns from .gitignore or .geminiignore',
type: 'object',
properties: {
respect_git_ignore: {
[PARAM_RESPECT_GIT_IGNORE]: {
description:
'Optional: Whether to respect .gitignore patterns when listing files. Only available in git repositories. Defaults to true.',
type: 'boolean',
},
respect_gemini_ignore: {
[PARAM_RESPECT_GEMINI_IGNORE]: {
description:
'Optional: Whether to respect .geminiignore patterns when listing files. Defaults to true.',
type: 'boolean',
@@ -445,7 +498,7 @@ Use this tool when the user's query implies needing the content of several files
},
},
},
required: ['include'],
required: [READ_MANY_PARAM_INCLUDE],
},
},
@@ -462,13 +515,13 @@ NEVER save workspace-specific context, local paths, or commands (e.g. "The entry
parametersJsonSchema: {
type: 'object',
properties: {
fact: {
[MEMORY_PARAM_FACT]: {
type: 'string',
description:
'The specific fact or piece of information to remember. Should be a clear, self-contained statement.',
},
},
required: ['fact'],
required: [MEMORY_PARAM_FACT],
additionalProperties: false,
},
},
@@ -541,7 +594,7 @@ The agent did not use the todo list because this task could be completed by a ti
parametersJsonSchema: {
type: 'object',
properties: {
todos: {
[TODOS_PARAM_TODOS]: {
type: 'array',
description:
'The complete list of todo items. This will replace the existing list.',
@@ -549,22 +602,22 @@ The agent did not use the todo list because this task could be completed by a ti
type: 'object',
description: 'A single todo item.',
properties: {
description: {
[TODOS_ITEM_PARAM_DESCRIPTION]: {
type: 'string',
description: 'The description of the task.',
},
status: {
[TODOS_ITEM_PARAM_STATUS]: {
type: 'string',
description: 'The current status of the task.',
enum: ['pending', 'in_progress', 'completed', 'cancelled'],
},
},
required: ['description', 'status'],
required: [TODOS_ITEM_PARAM_DESCRIPTION, TODOS_ITEM_PARAM_STATUS],
additionalProperties: false,
},
},
},
required: ['todos'],
required: [TODOS_PARAM_TODOS],
additionalProperties: false,
},
},
@@ -576,7 +629,7 @@ The agent did not use the todo list because this task could be completed by a ti
parametersJsonSchema: {
type: 'object',
properties: {
path: {
[DOCS_PARAM_PATH]: {
description:
"The relative path to the documentation file (e.g., 'cli/commands.md'). If omitted, lists all available documentation.",
type: 'string',
@@ -591,47 +644,54 @@ The agent did not use the todo list because this task could be completed by a ti
'Ask the user one or more questions to gather preferences, clarify requirements, or make decisions.',
parametersJsonSchema: {
type: 'object',
required: ['questions'],
required: [ASK_USER_PARAM_QUESTIONS],
properties: {
questions: {
[ASK_USER_PARAM_QUESTIONS]: {
type: 'array',
minItems: 1,
maxItems: 4,
items: {
type: 'object',
required: ['question', 'header', 'type'],
required: [
ASK_USER_QUESTION_PARAM_QUESTION,
ASK_USER_QUESTION_PARAM_HEADER,
ASK_USER_QUESTION_PARAM_TYPE,
],
properties: {
question: {
[ASK_USER_QUESTION_PARAM_QUESTION]: {
type: 'string',
description:
'The complete question to ask the user. Should be clear, specific, and end with a question mark.',
},
header: {
[ASK_USER_QUESTION_PARAM_HEADER]: {
type: 'string',
description:
'Very short label displayed as a chip/tag. Use abbreviations: "Auth" not "Authentication", "Config" not "Configuration". Examples: "Auth method", "Library", "Approach", "Database".',
},
type: {
[ASK_USER_QUESTION_PARAM_TYPE]: {
type: 'string',
enum: ['choice', 'text', 'yesno'],
default: 'choice',
description:
"Question type: 'choice' (default) for multiple-choice with options, 'text' for free-form input, 'yesno' for Yes/No confirmation.",
},
options: {
[ASK_USER_QUESTION_PARAM_OPTIONS]: {
type: 'array',
description:
"The selectable choices for 'choice' type questions. Provide 2-4 options. An 'Other' option is automatically added. Not needed for 'text' or 'yesno' types.",
items: {
type: 'object',
required: ['label', 'description'],
required: [
ASK_USER_OPTION_PARAM_LABEL,
ASK_USER_OPTION_PARAM_DESCRIPTION,
],
properties: {
label: {
[ASK_USER_OPTION_PARAM_LABEL]: {
type: 'string',
description:
'The display text for this option (1-5 words). Example: "OAuth 2.0"',
},
description: {
[ASK_USER_OPTION_PARAM_DESCRIPTION]: {
type: 'string',
description:
'Brief explanation of this option. Example: "Industry standard, supports SSO"',
@@ -639,12 +699,12 @@ The agent did not use the todo list because this task could be completed by a ti
},
},
},
multiSelect: {
[ASK_USER_QUESTION_PARAM_MULTI_SELECT]: {
type: 'boolean',
description:
"Only applies when type='choice'. Set to true to allow selecting multiple options.",
},
placeholder: {
[ASK_USER_QUESTION_PARAM_PLACEHOLDER]: {
type: 'string',
description:
"Hint text shown in the input field. For type='text', shown in the main input. For type='choice', shown in the 'Other' custom input.",
@@ -663,7 +723,7 @@ The agent did not use the todo list because this task could be completed by a ti
parametersJsonSchema: {
type: 'object',
properties: {
reason: {
[PLAN_MODE_PARAM_REASON]: {
type: 'string',
description:
'Short reason explaining why you are entering plan mode.',
@@ -25,6 +25,54 @@ import {
GET_INTERNAL_DOCS_TOOL_NAME,
ASK_USER_TOOL_NAME,
ENTER_PLAN_MODE_TOOL_NAME,
// Shared parameter names
PARAM_FILE_PATH,
PARAM_DIR_PATH,
PARAM_PATTERN,
PARAM_CASE_SENSITIVE,
PARAM_RESPECT_GIT_IGNORE,
PARAM_RESPECT_GEMINI_IGNORE,
PARAM_FILE_FILTERING_OPTIONS,
// Tool-specific parameter names
READ_FILE_PARAM_START_LINE,
READ_FILE_PARAM_END_LINE,
WRITE_FILE_PARAM_CONTENT,
GREP_PARAM_INCLUDE_PATTERN,
GREP_PARAM_EXCLUDE_PATTERN,
GREP_PARAM_NAMES_ONLY,
GREP_PARAM_MAX_MATCHES_PER_FILE,
GREP_PARAM_TOTAL_MAX_MATCHES,
GREP_PARAM_FIXED_STRINGS,
GREP_PARAM_CONTEXT,
GREP_PARAM_AFTER,
GREP_PARAM_BEFORE,
GREP_PARAM_NO_IGNORE,
EDIT_PARAM_INSTRUCTION,
EDIT_PARAM_OLD_STRING,
EDIT_PARAM_NEW_STRING,
EDIT_PARAM_ALLOW_MULTIPLE,
LS_PARAM_IGNORE,
WEB_SEARCH_PARAM_QUERY,
WEB_FETCH_PARAM_PROMPT,
READ_MANY_PARAM_INCLUDE,
READ_MANY_PARAM_EXCLUDE,
READ_MANY_PARAM_RECURSIVE,
READ_MANY_PARAM_USE_DEFAULT_EXCLUDES,
MEMORY_PARAM_FACT,
TODOS_PARAM_TODOS,
TODOS_ITEM_PARAM_DESCRIPTION,
TODOS_ITEM_PARAM_STATUS,
DOCS_PARAM_PATH,
ASK_USER_PARAM_QUESTIONS,
ASK_USER_QUESTION_PARAM_QUESTION,
ASK_USER_QUESTION_PARAM_HEADER,
ASK_USER_QUESTION_PARAM_TYPE,
ASK_USER_QUESTION_PARAM_OPTIONS,
ASK_USER_QUESTION_PARAM_MULTI_SELECT,
ASK_USER_QUESTION_PARAM_PLACEHOLDER,
ASK_USER_OPTION_PARAM_LABEL,
ASK_USER_OPTION_PARAM_DESCRIPTION,
PLAN_MODE_PARAM_REASON,
} from '../base-declarations.js';
import {
getShellDeclaration,
@@ -42,22 +90,22 @@ export const GEMINI_3_SET: CoreToolSet = {
parametersJsonSchema: {
type: 'object',
properties: {
file_path: {
[PARAM_FILE_PATH]: {
description: 'The path to the file to read.',
type: 'string',
},
start_line: {
[READ_FILE_PARAM_START_LINE]: {
description:
'Optional: The 1-based line number to start reading from.',
type: 'number',
},
end_line: {
[READ_FILE_PARAM_END_LINE]: {
description:
'Optional: The 1-based line number to end reading at (inclusive).',
type: 'number',
},
},
required: ['file_path'],
required: [PARAM_FILE_PATH],
},
},
@@ -67,17 +115,17 @@ export const GEMINI_3_SET: CoreToolSet = {
parametersJsonSchema: {
type: 'object',
properties: {
file_path: {
[PARAM_FILE_PATH]: {
description: 'Path to the file.',
type: 'string',
},
content: {
[WRITE_FILE_PARAM_CONTENT]: {
description:
"The complete content to write. Provide the full file; do not use placeholders like '// ... rest of code'.",
type: 'string',
},
},
required: ['file_path', 'content'],
required: [PARAM_FILE_PATH, WRITE_FILE_PARAM_CONTENT],
},
},
@@ -88,43 +136,43 @@ export const GEMINI_3_SET: CoreToolSet = {
parametersJsonSchema: {
type: 'object',
properties: {
pattern: {
[PARAM_PATTERN]: {
description: `The regular expression (regex) pattern to search for within file contents (e.g., 'function\\s+myFunction', 'import\\s+\\{.*\\}\\s+from\\s+.*').`,
type: 'string',
},
dir_path: {
[PARAM_DIR_PATH]: {
description:
'Optional: The absolute path to the directory to search within. If omitted, searches the current working directory.',
type: 'string',
},
include_pattern: {
[GREP_PARAM_INCLUDE_PATTERN]: {
description: `Optional: A glob pattern to filter which files are searched (e.g., '*.js', '*.{ts,tsx}', 'src/**'). If omitted, searches all files (respecting potential global ignores).`,
type: 'string',
},
exclude_pattern: {
[GREP_PARAM_EXCLUDE_PATTERN]: {
description:
'Optional: A regular expression pattern to exclude from the search results. If a line matches both the pattern and the exclude_pattern, it will be omitted.',
type: 'string',
},
names_only: {
[GREP_PARAM_NAMES_ONLY]: {
description:
'Optional: If true, only the file paths of the matches will be returned, without the line content or line numbers. This is useful for gathering a list of files.',
type: 'boolean',
},
max_matches_per_file: {
[GREP_PARAM_MAX_MATCHES_PER_FILE]: {
description:
'Optional: Maximum number of matches to return per file. Use this to prevent being overwhelmed by repetitive matches in large files.',
type: 'integer',
minimum: 1,
},
total_max_matches: {
[GREP_PARAM_TOTAL_MAX_MATCHES]: {
description:
'Optional: Maximum number of total matches to return. Use this to limit the overall size of the response. Defaults to 100 if omitted.',
type: 'integer',
minimum: 1,
},
},
required: ['pattern'],
required: [PARAM_PATTERN],
},
},
@@ -135,76 +183,76 @@ export const GEMINI_3_SET: CoreToolSet = {
parametersJsonSchema: {
type: 'object',
properties: {
pattern: {
[PARAM_PATTERN]: {
description: `The pattern to search for. By default, treated as a Rust-flavored regular expression. Use '\\b' for precise symbol matching (e.g., '\\bMatchMe\\b').`,
type: 'string',
},
dir_path: {
[PARAM_DIR_PATH]: {
description:
"Directory or file to search. Directories are searched recursively. Relative paths are resolved against current working directory. Defaults to current working directory ('.') if omitted.",
type: 'string',
},
include_pattern: {
[GREP_PARAM_INCLUDE_PATTERN]: {
description:
"Glob pattern to filter files (e.g., '*.ts', 'src/**'). Recommended for large repositories to reduce noise. Defaults to all files if omitted.",
type: 'string',
},
exclude_pattern: {
[GREP_PARAM_EXCLUDE_PATTERN]: {
description:
'Optional: A regular expression pattern to exclude from the search results. If a line matches both the pattern and the exclude_pattern, it will be omitted.',
type: 'string',
},
names_only: {
[GREP_PARAM_NAMES_ONLY]: {
description:
'Optional: If true, only the file paths of the matches will be returned, without the line content or line numbers. This is useful for gathering a list of files.',
type: 'boolean',
},
case_sensitive: {
[PARAM_CASE_SENSITIVE]: {
description:
'If true, search is case-sensitive. Defaults to false (ignore case) if omitted.',
type: 'boolean',
},
fixed_strings: {
[GREP_PARAM_FIXED_STRINGS]: {
description:
'If true, treats the `pattern` as a literal string instead of a regular expression. Defaults to false (basic regex) if omitted.',
type: 'boolean',
},
context: {
[GREP_PARAM_CONTEXT]: {
description:
'Show this many lines of context around each match (equivalent to grep -C). Defaults to 0 if omitted.',
type: 'integer',
},
after: {
[GREP_PARAM_AFTER]: {
description:
'Show this many lines after each match (equivalent to grep -A). Defaults to 0 if omitted.',
type: 'integer',
minimum: 0,
},
before: {
[GREP_PARAM_BEFORE]: {
description:
'Show this many lines before each match (equivalent to grep -B). Defaults to 0 if omitted.',
type: 'integer',
minimum: 0,
},
no_ignore: {
[GREP_PARAM_NO_IGNORE]: {
description:
'If true, searches all files including those usually ignored (like in .gitignore, build/, dist/, etc). Defaults to false if omitted.',
type: 'boolean',
},
max_matches_per_file: {
[GREP_PARAM_MAX_MATCHES_PER_FILE]: {
description:
'Optional: Maximum number of matches to return per file. Use this to prevent being overwhelmed by repetitive matches in large files.',
type: 'integer',
minimum: 1,
},
total_max_matches: {
[GREP_PARAM_TOTAL_MAX_MATCHES]: {
description:
'Optional: Maximum number of total matches to return. Use this to limit the overall size of the response. Defaults to 100 if omitted.',
type: 'integer',
minimum: 1,
},
},
required: ['pattern'],
required: [PARAM_PATTERN],
},
},
@@ -215,33 +263,33 @@ export const GEMINI_3_SET: CoreToolSet = {
parametersJsonSchema: {
type: 'object',
properties: {
pattern: {
[PARAM_PATTERN]: {
description:
"The glob pattern to match against (e.g., '**/*.py', 'docs/*.md').",
type: 'string',
},
dir_path: {
[PARAM_DIR_PATH]: {
description:
'Optional: The absolute path to the directory to search within. If omitted, searches the root directory.',
type: 'string',
},
case_sensitive: {
[PARAM_CASE_SENSITIVE]: {
description:
'Optional: Whether the search should be case-sensitive. Defaults to false.',
type: 'boolean',
},
respect_git_ignore: {
[PARAM_RESPECT_GIT_IGNORE]: {
description:
'Optional: Whether to respect .gitignore patterns when finding files. Only available in git repositories. Defaults to true.',
type: 'boolean',
},
respect_gemini_ignore: {
[PARAM_RESPECT_GEMINI_IGNORE]: {
description:
'Optional: Whether to respect .geminiignore patterns when finding files. Defaults to true.',
type: 'boolean',
},
},
required: ['pattern'],
required: [PARAM_PATTERN],
},
},
@@ -252,28 +300,28 @@ export const GEMINI_3_SET: CoreToolSet = {
parametersJsonSchema: {
type: 'object',
properties: {
dir_path: {
[PARAM_DIR_PATH]: {
description: 'The path to the directory to list',
type: 'string',
},
ignore: {
[LS_PARAM_IGNORE]: {
description: 'List of glob patterns to ignore',
items: {
type: 'string',
},
type: 'array',
},
file_filtering_options: {
[PARAM_FILE_FILTERING_OPTIONS]: {
description:
'Optional: Whether to respect ignore patterns from .gitignore or .geminiignore',
type: 'object',
properties: {
respect_git_ignore: {
[PARAM_RESPECT_GIT_IGNORE]: {
description:
'Optional: Whether to respect .gitignore patterns when listing files. Only available in git repositories. Defaults to true.',
type: 'boolean',
},
respect_gemini_ignore: {
[PARAM_RESPECT_GEMINI_IGNORE]: {
description:
'Optional: Whether to respect .geminiignore patterns when listing files. Defaults to true.',
type: 'boolean',
@@ -281,7 +329,7 @@ export const GEMINI_3_SET: CoreToolSet = {
},
},
},
required: ['dir_path'],
required: [PARAM_DIR_PATH],
},
},
@@ -295,31 +343,36 @@ The user has the ability to modify the \`new_string\` content. If modified, this
parametersJsonSchema: {
type: 'object',
properties: {
file_path: {
[PARAM_FILE_PATH]: {
description: 'The path to the file to modify.',
type: 'string',
},
instruction: {
[EDIT_PARAM_INSTRUCTION]: {
description: `A clear, semantic instruction for the code change, acting as a high-quality prompt for an expert LLM assistant. It must be self-contained and explain the goal of the change.`,
type: 'string',
},
old_string: {
[EDIT_PARAM_OLD_STRING]: {
description:
'The exact literal text to replace, unescaped. If this string is not the exact literal text (i.e. you escaped it) or does not match exactly, the tool will fail.',
type: 'string',
},
new_string: {
[EDIT_PARAM_NEW_STRING]: {
description:
"The exact literal text to replace `old_string` with, unescaped. Provide the EXACT text. Ensure the resulting code is correct and idiomatic. Do not use omission placeholders like '(rest of methods ...)', '...', or 'unchanged code'; provide exact literal code.",
type: 'string',
},
allow_multiple: {
[EDIT_PARAM_ALLOW_MULTIPLE]: {
type: 'boolean',
description:
'If true, the tool will replace all occurrences of `old_string`. If false (default), it will only succeed if exactly one occurrence is found.',
},
},
required: ['file_path', 'instruction', 'old_string', 'new_string'],
required: [
PARAM_FILE_PATH,
EDIT_PARAM_INSTRUCTION,
EDIT_PARAM_OLD_STRING,
EDIT_PARAM_NEW_STRING,
],
},
},
@@ -329,13 +382,13 @@ The user has the ability to modify the \`new_string\` content. If modified, this
parametersJsonSchema: {
type: 'object',
properties: {
query: {
[WEB_SEARCH_PARAM_QUERY]: {
type: 'string',
description:
"The search query. Supports natural language questions (e.g., 'Latest breaking changes in React 19') or specific technical queries.",
},
},
required: ['query'],
required: [WEB_SEARCH_PARAM_QUERY],
},
},
@@ -346,13 +399,13 @@ The user has the ability to modify the \`new_string\` content. If modified, this
parametersJsonSchema: {
type: 'object',
properties: {
prompt: {
[WEB_FETCH_PARAM_PROMPT]: {
description:
'A string containing the URL(s) and your specific analysis instructions. Be clear about what information you want to find or summarize. Supports up to 20 URLs.',
type: 'string',
},
},
required: ['prompt'],
required: [WEB_FETCH_PARAM_PROMPT],
},
},
@@ -371,7 +424,7 @@ Use this tool when the user's query implies needing the content of several files
parametersJsonSchema: {
type: 'object',
properties: {
include: {
[READ_MANY_PARAM_INCLUDE]: {
type: 'array',
items: {
type: 'string',
@@ -381,7 +434,7 @@ Use this tool when the user's query implies needing the content of several files
description:
'An array of glob patterns or paths. Examples: ["src/**/*.ts"], ["README.md", "docs/"]',
},
exclude: {
[READ_MANY_PARAM_EXCLUDE]: {
type: 'array',
items: {
type: 'string',
@@ -391,30 +444,30 @@ Use this tool when the user's query implies needing the content of several files
'Optional. Glob patterns for files/directories to exclude. Added to default excludes if useDefaultExcludes is true. Example: "**/*.log", "temp/"',
default: [],
},
recursive: {
[READ_MANY_PARAM_RECURSIVE]: {
type: 'boolean',
description:
'Optional. Whether to search recursively (primarily controlled by `**` in glob patterns). Defaults to true.',
default: true,
},
useDefaultExcludes: {
[READ_MANY_PARAM_USE_DEFAULT_EXCLUDES]: {
type: 'boolean',
description:
'Optional. Whether to apply a list of default exclusion patterns (e.g., node_modules, .git, binary files). Defaults to true.',
default: true,
},
file_filtering_options: {
[PARAM_FILE_FILTERING_OPTIONS]: {
description:
'Whether to respect ignore patterns from .gitignore or .geminiignore',
type: 'object',
properties: {
respect_git_ignore: {
[PARAM_RESPECT_GIT_IGNORE]: {
description:
'Optional: Whether to respect .gitignore patterns when listing files. Only available in git repositories. Defaults to true.',
type: 'boolean',
},
respect_gemini_ignore: {
[PARAM_RESPECT_GEMINI_IGNORE]: {
description:
'Optional: Whether to respect .geminiignore patterns when listing files. Defaults to true.',
type: 'boolean',
@@ -422,7 +475,7 @@ Use this tool when the user's query implies needing the content of several files
},
},
},
required: ['include'],
required: [READ_MANY_PARAM_INCLUDE],
},
},
@@ -432,13 +485,13 @@ Use this tool when the user's query implies needing the content of several files
parametersJsonSchema: {
type: 'object',
properties: {
fact: {
[MEMORY_PARAM_FACT]: {
type: 'string',
description:
"A concise, global fact or preference (e.g., 'I prefer using tabs'). Do not include local paths or project-specific names.",
},
},
required: ['fact'],
required: [MEMORY_PARAM_FACT],
additionalProperties: false,
},
},
@@ -511,7 +564,7 @@ The agent did not use the todo list because this task could be completed by a ti
parametersJsonSchema: {
type: 'object',
properties: {
todos: {
[TODOS_PARAM_TODOS]: {
type: 'array',
description:
'The complete list of todo items. This will replace the existing list.',
@@ -519,22 +572,22 @@ The agent did not use the todo list because this task could be completed by a ti
type: 'object',
description: 'A single todo item.',
properties: {
description: {
[TODOS_ITEM_PARAM_DESCRIPTION]: {
type: 'string',
description: 'The description of the task.',
},
status: {
[TODOS_ITEM_PARAM_STATUS]: {
type: 'string',
description: 'The current status of the task.',
enum: ['pending', 'in_progress', 'completed', 'cancelled'],
},
},
required: ['description', 'status'],
required: [TODOS_ITEM_PARAM_DESCRIPTION, TODOS_ITEM_PARAM_STATUS],
additionalProperties: false,
},
},
},
required: ['todos'],
required: [TODOS_PARAM_TODOS],
additionalProperties: false,
},
},
@@ -546,7 +599,7 @@ The agent did not use the todo list because this task could be completed by a ti
parametersJsonSchema: {
type: 'object',
properties: {
path: {
[DOCS_PARAM_PATH]: {
description:
"The relative path to the documentation file (e.g., 'cli/commands.md'). If omitted, lists all available documentation.",
type: 'string',
@@ -561,47 +614,54 @@ The agent did not use the todo list because this task could be completed by a ti
'Ask the user one or more questions to gather preferences, clarify requirements, or make decisions. When using this tool, prefer providing multiple-choice options with detailed descriptions and enable multi-select where appropriate to provide maximum flexibility.',
parametersJsonSchema: {
type: 'object',
required: ['questions'],
required: [ASK_USER_PARAM_QUESTIONS],
properties: {
questions: {
[ASK_USER_PARAM_QUESTIONS]: {
type: 'array',
minItems: 1,
maxItems: 4,
items: {
type: 'object',
required: ['question', 'header', 'type'],
required: [
ASK_USER_QUESTION_PARAM_QUESTION,
ASK_USER_QUESTION_PARAM_HEADER,
ASK_USER_QUESTION_PARAM_TYPE,
],
properties: {
question: {
[ASK_USER_QUESTION_PARAM_QUESTION]: {
type: 'string',
description:
'The complete question to ask the user. Should be clear, specific, and end with a question mark.',
},
header: {
[ASK_USER_QUESTION_PARAM_HEADER]: {
type: 'string',
description:
'Very short label displayed as a chip/tag. Use abbreviations: "Auth" not "Authentication", "Config" not "Configuration". Examples: "Auth method", "Library", "Approach", "Database".',
},
type: {
[ASK_USER_QUESTION_PARAM_TYPE]: {
type: 'string',
enum: ['choice', 'text', 'yesno'],
default: 'choice',
description:
"Question type: 'choice' (default) for multiple-choice with options, 'text' for free-form input, 'yesno' for Yes/No confirmation.",
},
options: {
[ASK_USER_QUESTION_PARAM_OPTIONS]: {
type: 'array',
description:
"The selectable choices for 'choice' type questions. Provide 2-4 options. An 'Other' option is automatically added. Not needed for 'text' or 'yesno' types.",
items: {
type: 'object',
required: ['label', 'description'],
required: [
ASK_USER_OPTION_PARAM_LABEL,
ASK_USER_OPTION_PARAM_DESCRIPTION,
],
properties: {
label: {
[ASK_USER_OPTION_PARAM_LABEL]: {
type: 'string',
description:
'The display text for this option (1-5 words). Example: "OAuth 2.0"',
},
description: {
[ASK_USER_OPTION_PARAM_DESCRIPTION]: {
type: 'string',
description:
'Brief explanation of this option. Example: "Industry standard, supports SSO"',
@@ -609,12 +669,12 @@ The agent did not use the todo list because this task could be completed by a ti
},
},
},
multiSelect: {
[ASK_USER_QUESTION_PARAM_MULTI_SELECT]: {
type: 'boolean',
description:
"Only applies when type='choice'. Set to true to allow selecting multiple options.",
},
placeholder: {
[ASK_USER_QUESTION_PARAM_PLACEHOLDER]: {
type: 'string',
description:
"Hint text shown in the input field. For type='text', shown in the main input. For type='choice', shown in the 'Other' custom input.",
@@ -633,7 +693,7 @@ The agent did not use the todo list because this task could be completed by a ti
parametersJsonSchema: {
type: 'object',
properties: {
reason: {
[PLAN_MODE_PARAM_REASON]: {
type: 'string',
description:
'Short reason explaining why you are entering plan mode.',
-15
View File
@@ -202,21 +202,6 @@ describe('EditTool', () => {
fs.rmSync(tempDir, { recursive: true, force: true });
});
it('should be marked as sensitive and pass the flag to its invocations', () => {
// Check the tool definition itself
expect(tool.isSensitive).toBe(true);
// Build an invocation and check the instance
const params: EditToolParams = {
file_path: path.join(rootDir, 'test.txt'),
instruction: 'An instruction',
old_string: 'old',
new_string: 'new',
};
const invocation = tool.build(params);
expect(invocation.isSensitive).toBe(true);
});
describe('applyReplacement', () => {
it('should return newString if isNewFile is true', () => {
expect(applyReplacement(null, 'old', 'new', true)).toBe('new');
+1 -17
View File
@@ -434,17 +434,8 @@ class EditToolInvocation
messageBus: MessageBus,
toolName?: string,
displayName?: string,
isSensitive?: boolean,
) {
super(
params,
messageBus,
toolName,
displayName,
undefined,
undefined,
isSensitive,
);
super(params, messageBus, toolName, displayName);
}
override toolLocations(): ToolLocation[] {
@@ -965,9 +956,6 @@ export class EditTool
messageBus,
true, // isOutputMarkdown
false, // canUpdateOutput
undefined,
undefined,
true,
);
}
@@ -1013,9 +1001,6 @@ export class EditTool
protected createInvocation(
params: EditToolParams,
messageBus: MessageBus,
_toolName?: string,
_toolDisplayName?: string,
isSensitive?: boolean,
): ToolInvocation<EditToolParams, ToolResult> {
return new EditToolInvocation(
this.config,
@@ -1023,7 +1008,6 @@ export class EditTool
messageBus,
this.name,
this.displayName,
isSensitive,
);
}
+1 -15
View File
@@ -79,17 +79,8 @@ class GetInternalDocsInvocation extends BaseToolInvocation<
messageBus: MessageBus,
_toolName?: string,
_toolDisplayName?: string,
isSensitive?: boolean,
) {
super(
params,
messageBus,
_toolName,
_toolDisplayName,
undefined,
undefined,
isSensitive,
);
super(params, messageBus, _toolName, _toolDisplayName);
}
override async shouldConfirmExecute(
@@ -174,9 +165,6 @@ export class GetInternalDocsTool extends BaseDeclarativeTool<
messageBus,
/* isOutputMarkdown */ true,
/* canUpdateOutput */ false,
undefined,
undefined,
true,
);
}
@@ -185,14 +173,12 @@ export class GetInternalDocsTool extends BaseDeclarativeTool<
messageBus: MessageBus,
_toolName?: string,
_toolDisplayName?: string,
isSensitive?: boolean,
): ToolInvocation<GetInternalDocsParams, ToolResult> {
return new GetInternalDocsInvocation(
params,
messageBus,
_toolName ?? GetInternalDocsTool.Name,
_toolDisplayName,
isSensitive,
);
}
+1 -15
View File
@@ -96,17 +96,8 @@ class GlobToolInvocation extends BaseToolInvocation<
messageBus: MessageBus,
_toolName?: string,
_toolDisplayName?: string,
isSensitive?: boolean,
) {
super(
params,
messageBus,
_toolName,
_toolDisplayName,
undefined,
undefined,
isSensitive,
);
super(params, messageBus, _toolName, _toolDisplayName);
}
getDescription(): string {
@@ -287,9 +278,6 @@ export class GlobTool extends BaseDeclarativeTool<GlobToolParams, ToolResult> {
messageBus,
true,
false,
undefined,
undefined,
true,
);
}
@@ -340,7 +328,6 @@ export class GlobTool extends BaseDeclarativeTool<GlobToolParams, ToolResult> {
messageBus: MessageBus,
_toolName?: string,
_toolDisplayName?: string,
isSensitive?: boolean,
): ToolInvocation<GlobToolParams, ToolResult> {
return new GlobToolInvocation(
this.config,
@@ -348,7 +335,6 @@ export class GlobTool extends BaseDeclarativeTool<GlobToolParams, ToolResult> {
messageBus,
_toolName,
_toolDisplayName,
isSensitive,
);
}
+1 -15
View File
@@ -83,17 +83,8 @@ class GrepToolInvocation extends BaseToolInvocation<
messageBus: MessageBus,
_toolName?: string,
_toolDisplayName?: string,
isSensitive?: boolean,
) {
super(
params,
messageBus,
_toolName,
_toolDisplayName,
undefined,
undefined,
isSensitive,
);
super(params, messageBus, _toolName, _toolDisplayName);
this.fileExclusions = config.getFileExclusions();
}
@@ -610,9 +601,6 @@ export class GrepTool extends BaseDeclarativeTool<GrepToolParams, ToolResult> {
messageBus,
true,
false,
undefined,
undefined,
true,
);
}
@@ -688,7 +676,6 @@ export class GrepTool extends BaseDeclarativeTool<GrepToolParams, ToolResult> {
messageBus: MessageBus,
_toolName?: string,
_toolDisplayName?: string,
isSensitive?: boolean,
): ToolInvocation<GrepToolParams, ToolResult> {
return new GrepToolInvocation(
this.config,
@@ -696,7 +683,6 @@ export class GrepTool extends BaseDeclarativeTool<GrepToolParams, ToolResult> {
messageBus,
_toolName,
_toolDisplayName,
isSensitive,
);
}
+2 -16
View File
@@ -78,17 +78,8 @@ class LSToolInvocation extends BaseToolInvocation<LSToolParams, ToolResult> {
messageBus: MessageBus,
_toolName?: string,
_toolDisplayName?: string,
isSensitive?: boolean,
) {
super(
params,
messageBus,
_toolName,
_toolDisplayName,
undefined,
undefined,
isSensitive,
);
super(params, messageBus, _toolName, _toolDisplayName);
}
/**
@@ -302,9 +293,6 @@ export class LSTool extends BaseDeclarativeTool<LSToolParams, ToolResult> {
messageBus,
true,
false,
undefined,
undefined,
true,
);
}
@@ -328,15 +316,13 @@ export class LSTool extends BaseDeclarativeTool<LSToolParams, ToolResult> {
messageBus: MessageBus,
_toolName?: string,
_toolDisplayName?: string,
isSensitive?: boolean,
): ToolInvocation<LSToolParams, ToolResult> {
return new LSToolInvocation(
this.config,
params,
messageBus,
messageBus ?? this.messageBus,
_toolName,
_toolDisplayName,
isSensitive,
);
}
-5
View File
@@ -93,7 +93,6 @@ export class DiscoveredMCPToolInvocation extends BaseToolInvocation<
private readonly toolDescription?: string,
private readonly toolParameterSchema?: unknown,
toolAnnotationsData?: Record<string, unknown>,
isSensitive: boolean = false,
) {
// Use composite format for policy checks: serverName__toolName
// This enables server wildcards (e.g., "google-workspace__*")
@@ -106,7 +105,6 @@ export class DiscoveredMCPToolInvocation extends BaseToolInvocation<
displayName,
serverName,
toolAnnotationsData,
isSensitive,
);
}
@@ -285,7 +283,6 @@ export class DiscoveredMCPTool extends BaseDeclarativeTool<
false, // canUpdateOutput,
extensionName,
extensionId,
true, // isSensitive
);
this._isReadOnly = isReadOnly;
}
@@ -334,7 +331,6 @@ export class DiscoveredMCPTool extends BaseDeclarativeTool<
messageBus: MessageBus,
_toolName?: string,
_displayName?: string,
isSensitive?: boolean,
): ToolInvocation<ToolParams, ToolResult> {
return new DiscoveredMCPToolInvocation(
this.mcpTool,
@@ -348,7 +344,6 @@ export class DiscoveredMCPTool extends BaseDeclarativeTool<
this.description,
this.parameterSchema,
this._toolAnnotations,
isSensitive,
);
}
}
-3
View File
@@ -285,9 +285,6 @@ export class MemoryTool
messageBus,
true,
false,
undefined,
undefined,
true,
);
}
+1 -15
View File
@@ -114,17 +114,8 @@ class ReadManyFilesToolInvocation extends BaseToolInvocation<
messageBus: MessageBus,
_toolName?: string,
_toolDisplayName?: string,
isSensitive?: boolean,
) {
super(
params,
messageBus,
_toolName,
_toolDisplayName,
undefined,
undefined,
isSensitive,
);
super(params, messageBus, _toolName, _toolDisplayName);
}
getDescription(): string {
@@ -483,9 +474,6 @@ export class ReadManyFilesTool extends BaseDeclarativeTool<
messageBus,
true, // isOutputMarkdown
false, // canUpdateOutput
undefined,
undefined,
true,
);
}
@@ -494,7 +482,6 @@ export class ReadManyFilesTool extends BaseDeclarativeTool<
messageBus: MessageBus,
_toolName?: string,
_toolDisplayName?: string,
isSensitive?: boolean,
): ToolInvocation<ReadManyFilesParams, ToolResult> {
return new ReadManyFilesToolInvocation(
this.config,
@@ -502,7 +489,6 @@ export class ReadManyFilesTool extends BaseDeclarativeTool<
messageBus,
_toolName,
_toolDisplayName,
isSensitive,
);
}
+2 -16
View File
@@ -167,17 +167,8 @@ class GrepToolInvocation extends BaseToolInvocation<
messageBus: MessageBus,
_toolName?: string,
_toolDisplayName?: string,
isSensitive?: boolean,
) {
super(
params,
messageBus,
_toolName,
_toolDisplayName,
undefined,
undefined,
isSensitive,
);
super(params, messageBus, _toolName, _toolDisplayName);
}
async execute(signal: AbortSignal): Promise<ToolResult> {
@@ -593,9 +584,6 @@ export class RipGrepTool extends BaseDeclarativeTool<
messageBus,
true, // isOutputMarkdown
false, // canUpdateOutput
undefined,
undefined,
true,
);
this.fileDiscoveryService = new FileDiscoveryService(
config.getTargetDir(),
@@ -677,16 +665,14 @@ export class RipGrepTool extends BaseDeclarativeTool<
messageBus: MessageBus,
_toolName?: string,
_toolDisplayName?: string,
isSensitive?: boolean,
): ToolInvocation<RipGrepToolParams, ToolResult> {
return new GrepToolInvocation(
this.config,
this.fileDiscoveryService,
params,
messageBus,
messageBus ?? this.messageBus,
_toolName,
_toolDisplayName,
isSensitive,
);
}
+1 -15
View File
@@ -68,17 +68,8 @@ export class ShellToolInvocation extends BaseToolInvocation<
messageBus: MessageBus,
_toolName?: string,
_toolDisplayName?: string,
isSensitive?: boolean,
) {
super(
params,
messageBus,
_toolName,
_toolDisplayName,
undefined,
undefined,
isSensitive,
);
super(params, messageBus, _toolName, _toolDisplayName);
}
getDescription(): string {
@@ -488,9 +479,6 @@ export class ShellTool extends BaseDeclarativeTool<
messageBus,
false, // output is not markdown
true, // output can be updated
undefined,
undefined,
true,
);
}
@@ -516,7 +504,6 @@ export class ShellTool extends BaseDeclarativeTool<
messageBus: MessageBus,
_toolName?: string,
_toolDisplayName?: string,
isSensitive?: boolean,
): ToolInvocation<ShellToolParams, ToolResult> {
return new ShellToolInvocation(
this.config,
@@ -524,7 +511,6 @@ export class ShellTool extends BaseDeclarativeTool<
messageBus,
_toolName,
_toolDisplayName,
isSensitive,
);
}
+106
View File
@@ -22,6 +22,59 @@ import {
ASK_USER_TOOL_NAME,
EXIT_PLAN_MODE_TOOL_NAME,
ENTER_PLAN_MODE_TOOL_NAME,
// Shared parameter names
PARAM_FILE_PATH,
PARAM_DIR_PATH,
PARAM_PATTERN,
PARAM_CASE_SENSITIVE,
PARAM_RESPECT_GIT_IGNORE,
PARAM_RESPECT_GEMINI_IGNORE,
PARAM_FILE_FILTERING_OPTIONS,
PARAM_DESCRIPTION,
// Tool-specific parameter names
READ_FILE_PARAM_START_LINE,
READ_FILE_PARAM_END_LINE,
WRITE_FILE_PARAM_CONTENT,
GREP_PARAM_INCLUDE_PATTERN,
GREP_PARAM_EXCLUDE_PATTERN,
GREP_PARAM_NAMES_ONLY,
GREP_PARAM_MAX_MATCHES_PER_FILE,
GREP_PARAM_TOTAL_MAX_MATCHES,
GREP_PARAM_FIXED_STRINGS,
GREP_PARAM_CONTEXT,
GREP_PARAM_AFTER,
GREP_PARAM_BEFORE,
GREP_PARAM_NO_IGNORE,
EDIT_PARAM_INSTRUCTION,
EDIT_PARAM_OLD_STRING,
EDIT_PARAM_NEW_STRING,
EDIT_PARAM_ALLOW_MULTIPLE,
LS_PARAM_IGNORE,
SHELL_PARAM_COMMAND,
SHELL_PARAM_IS_BACKGROUND,
WEB_SEARCH_PARAM_QUERY,
WEB_FETCH_PARAM_PROMPT,
READ_MANY_PARAM_INCLUDE,
READ_MANY_PARAM_EXCLUDE,
READ_MANY_PARAM_RECURSIVE,
READ_MANY_PARAM_USE_DEFAULT_EXCLUDES,
MEMORY_PARAM_FACT,
TODOS_PARAM_TODOS,
TODOS_ITEM_PARAM_DESCRIPTION,
TODOS_ITEM_PARAM_STATUS,
DOCS_PARAM_PATH,
ASK_USER_PARAM_QUESTIONS,
ASK_USER_QUESTION_PARAM_QUESTION,
ASK_USER_QUESTION_PARAM_HEADER,
ASK_USER_QUESTION_PARAM_TYPE,
ASK_USER_QUESTION_PARAM_OPTIONS,
ASK_USER_QUESTION_PARAM_MULTI_SELECT,
ASK_USER_QUESTION_PARAM_PLACEHOLDER,
ASK_USER_OPTION_PARAM_LABEL,
ASK_USER_OPTION_PARAM_DESCRIPTION,
PLAN_MODE_PARAM_REASON,
EXIT_PLAN_PARAM_PLAN_PATH,
SKILL_PARAM_NAME,
} from './definitions/coreTools.js';
export {
@@ -42,6 +95,59 @@ export {
ASK_USER_TOOL_NAME,
EXIT_PLAN_MODE_TOOL_NAME,
ENTER_PLAN_MODE_TOOL_NAME,
// Shared parameter names
PARAM_FILE_PATH,
PARAM_DIR_PATH,
PARAM_PATTERN,
PARAM_CASE_SENSITIVE,
PARAM_RESPECT_GIT_IGNORE,
PARAM_RESPECT_GEMINI_IGNORE,
PARAM_FILE_FILTERING_OPTIONS,
PARAM_DESCRIPTION,
// Tool-specific parameter names
READ_FILE_PARAM_START_LINE,
READ_FILE_PARAM_END_LINE,
WRITE_FILE_PARAM_CONTENT,
GREP_PARAM_INCLUDE_PATTERN,
GREP_PARAM_EXCLUDE_PATTERN,
GREP_PARAM_NAMES_ONLY,
GREP_PARAM_MAX_MATCHES_PER_FILE,
GREP_PARAM_TOTAL_MAX_MATCHES,
GREP_PARAM_FIXED_STRINGS,
GREP_PARAM_CONTEXT,
GREP_PARAM_AFTER,
GREP_PARAM_BEFORE,
GREP_PARAM_NO_IGNORE,
EDIT_PARAM_INSTRUCTION,
EDIT_PARAM_OLD_STRING,
EDIT_PARAM_NEW_STRING,
EDIT_PARAM_ALLOW_MULTIPLE,
LS_PARAM_IGNORE,
SHELL_PARAM_COMMAND,
SHELL_PARAM_IS_BACKGROUND,
WEB_SEARCH_PARAM_QUERY,
WEB_FETCH_PARAM_PROMPT,
READ_MANY_PARAM_INCLUDE,
READ_MANY_PARAM_EXCLUDE,
READ_MANY_PARAM_RECURSIVE,
READ_MANY_PARAM_USE_DEFAULT_EXCLUDES,
MEMORY_PARAM_FACT,
TODOS_PARAM_TODOS,
TODOS_ITEM_PARAM_DESCRIPTION,
TODOS_ITEM_PARAM_STATUS,
DOCS_PARAM_PATH,
ASK_USER_PARAM_QUESTIONS,
ASK_USER_QUESTION_PARAM_QUESTION,
ASK_USER_QUESTION_PARAM_HEADER,
ASK_USER_QUESTION_PARAM_TYPE,
ASK_USER_QUESTION_PARAM_OPTIONS,
ASK_USER_QUESTION_PARAM_MULTI_SELECT,
ASK_USER_QUESTION_PARAM_PLACEHOLDER,
ASK_USER_OPTION_PARAM_LABEL,
ASK_USER_OPTION_PARAM_DESCRIPTION,
PLAN_MODE_PARAM_REASON,
EXIT_PLAN_PARAM_PLAN_PATH,
SKILL_PARAM_NAME,
};
export const LS_TOOL_NAME_LEGACY = 'list_directory'; // Just to be safe if anything used the old exported name directly
-1
View File
@@ -16,7 +16,6 @@ class TestToolInvocation implements ToolInvocation<object, ToolResult> {
constructor(
readonly params: object,
private readonly executeFn: () => Promise<ToolResult>,
readonly isSensitive: boolean = false,
) {}
getDescription(): string {
-46
View File
@@ -33,11 +33,6 @@ export interface ToolInvocation<
*/
params: TParams;
/**
* Whether the tool is sensitive and requires specific policy approvals.
*/
isSensitive: boolean;
/**
* Gets a pre-execution description of the tool operation.
*
@@ -80,7 +75,6 @@ export interface ToolInvocation<
export interface PolicyUpdateOptions {
commandPrefix?: string | string[];
mcpName?: string;
argsPattern?: string;
}
/**
@@ -98,7 +92,6 @@ export abstract class BaseToolInvocation<
readonly _toolDisplayName?: string,
readonly _serverName?: string,
readonly _toolAnnotations?: Record<string, unknown>,
readonly isSensitive: boolean = false,
) {}
abstract getDescription(): string;
@@ -159,7 +152,6 @@ export abstract class BaseToolInvocation<
type: MessageBusType.UPDATE_POLICY,
toolName: this._toolName,
persist: outcome === ToolConfirmationOutcome.ProceedAlwaysAndSave,
isSensitive: this.isSensitive,
...options,
});
}
@@ -348,11 +340,6 @@ export interface ToolBuilder<
*/
isReadOnly: boolean;
/**
* Whether the tool is sensitive and requires specific policy approvals.
*/
isSensitive: boolean;
/**
* Validates raw parameters and builds a ready-to-execute invocation.
* @param params The raw, untrusted parameters from the model.
@@ -381,7 +368,6 @@ export abstract class DeclarativeTool<
readonly canUpdateOutput: boolean = false,
readonly extensionName?: string,
readonly extensionId?: string,
readonly isSensitive: boolean = false,
) {}
get isReadOnly(): boolean {
@@ -512,34 +498,6 @@ export abstract class BaseDeclarativeTool<
TParams extends object,
TResult extends ToolResult,
> extends DeclarativeTool<TParams, TResult> {
constructor(
name: string,
displayName: string,
description: string,
kind: Kind,
parameterSchema: unknown,
messageBus: MessageBus,
isOutputMarkdown: boolean = true,
canUpdateOutput: boolean = false,
extensionName?: string,
extensionId?: string,
isSensitive: boolean = false,
) {
super(
name,
displayName,
description,
kind,
parameterSchema,
messageBus,
isOutputMarkdown,
canUpdateOutput,
extensionName,
extensionId,
isSensitive,
);
}
build(params: TParams): ToolInvocation<TParams, TResult> {
const validationError = this.validateToolParams(params);
if (validationError) {
@@ -550,7 +508,6 @@ export abstract class BaseDeclarativeTool<
this.messageBus,
this.name,
this.displayName,
this.isSensitive,
);
}
@@ -576,7 +533,6 @@ export abstract class BaseDeclarativeTool<
messageBus: MessageBus,
_toolName?: string,
_toolDisplayName?: string,
isSensitive?: boolean,
): ToolInvocation<TParams, TResult>;
}
@@ -870,7 +826,6 @@ export enum ToolConfirmationOutcome {
export enum Kind {
Read = 'read',
Write = 'write',
Edit = 'edit',
Delete = 'delete',
Move = 'move',
@@ -887,7 +842,6 @@ export enum Kind {
// Function kinds that have side effects
export const MUTATOR_KINDS: Kind[] = [
Kind.Write,
Kind.Edit,
Kind.Delete,
Kind.Move,
+1 -15
View File
@@ -178,17 +178,8 @@ class WebFetchToolInvocation extends BaseToolInvocation<
messageBus: MessageBus,
_toolName?: string,
_toolDisplayName?: string,
isSensitive?: boolean,
) {
super(
params,
messageBus,
_toolName,
_toolDisplayName,
undefined,
undefined,
isSensitive,
);
super(params, messageBus, _toolName, _toolDisplayName);
}
private async executeFallback(signal: AbortSignal): Promise<ToolResult> {
@@ -698,9 +689,6 @@ export class WebFetchTool extends BaseDeclarativeTool<
messageBus,
true, // isOutputMarkdown
false, // canUpdateOutput
undefined,
undefined,
true,
);
}
@@ -741,7 +729,6 @@ export class WebFetchTool extends BaseDeclarativeTool<
messageBus: MessageBus,
_toolName?: string,
_toolDisplayName?: string,
isSensitive?: boolean,
): ToolInvocation<WebFetchToolParams, ToolResult> {
return new WebFetchToolInvocation(
this.config,
@@ -749,7 +736,6 @@ export class WebFetchTool extends BaseDeclarativeTool<
messageBus,
_toolName,
_toolDisplayName,
isSensitive,
);
}
+1 -15
View File
@@ -71,17 +71,8 @@ class WebSearchToolInvocation extends BaseToolInvocation<
messageBus: MessageBus,
_toolName?: string,
_toolDisplayName?: string,
isSensitive?: boolean,
) {
super(
params,
messageBus,
_toolName,
_toolDisplayName,
undefined,
undefined,
isSensitive,
);
super(params, messageBus, _toolName, _toolDisplayName);
}
override getDescription(): string {
@@ -217,9 +208,6 @@ export class WebSearchTool extends BaseDeclarativeTool<
messageBus,
true, // isOutputMarkdown
false, // canUpdateOutput
undefined,
undefined,
true,
);
}
@@ -242,7 +230,6 @@ export class WebSearchTool extends BaseDeclarativeTool<
messageBus: MessageBus,
_toolName?: string,
_toolDisplayName?: string,
isSensitive?: boolean,
): ToolInvocation<WebSearchToolParams, WebSearchToolResult> {
return new WebSearchToolInvocation(
this.config,
@@ -250,7 +237,6 @@ export class WebSearchTool extends BaseDeclarativeTool<
messageBus ?? this.messageBus,
_toolName,
_toolDisplayName,
isSensitive,
);
}
+2 -18
View File
@@ -136,17 +136,8 @@ class WriteFileToolInvocation extends BaseToolInvocation<
messageBus: MessageBus,
toolName?: string,
displayName?: string,
isSensitive?: boolean,
) {
super(
params,
messageBus,
toolName,
displayName,
undefined,
undefined,
isSensitive,
);
super(params, messageBus, toolName, displayName);
this.resolvedPath = path.resolve(
this.config.getTargetDir(),
this.params.file_path,
@@ -438,14 +429,11 @@ export class WriteFileTool
WriteFileTool.Name,
WRITE_FILE_DISPLAY_NAME,
WRITE_FILE_DEFINITION.base.description!,
Kind.Write,
Kind.Edit,
WRITE_FILE_DEFINITION.base.parametersJsonSchema,
messageBus,
true,
false,
undefined,
undefined,
true,
);
}
@@ -489,9 +477,6 @@ export class WriteFileTool
protected createInvocation(
params: WriteFileToolParams,
messageBus: MessageBus,
_toolName?: string,
_toolDisplayName?: string,
isSensitive?: boolean,
): ToolInvocation<WriteFileToolParams, ToolResult> {
return new WriteFileToolInvocation(
this.config,
@@ -499,7 +484,6 @@ export class WriteFileTool
messageBus ?? this.messageBus,
this.name,
this.displayName,
isSensitive,
);
}
+1 -15
View File
@@ -34,17 +34,8 @@ class WriteTodosToolInvocation extends BaseToolInvocation<
messageBus: MessageBus,
_toolName?: string,
_toolDisplayName?: string,
isSensitive?: boolean,
) {
super(
params,
messageBus,
_toolName,
_toolDisplayName,
undefined,
undefined,
isSensitive,
);
super(params, messageBus, _toolName, _toolDisplayName);
}
getDescription(): string {
@@ -94,9 +85,6 @@ export class WriteTodosTool extends BaseDeclarativeTool<
messageBus,
true, // isOutputMarkdown
false, // canUpdateOutput
undefined,
undefined,
true,
);
}
@@ -140,14 +128,12 @@ export class WriteTodosTool extends BaseDeclarativeTool<
messageBus: MessageBus,
_toolName?: string,
_displayName?: string,
isSensitive?: boolean,
): ToolInvocation<WriteTodosToolParams, ToolResult> {
return new WriteTodosToolInvocation(
params,
messageBus,
_toolName,
_displayName,
isSensitive,
);
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli-devtools",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.33.0-nightly.20260228.1ca5c05d0",
"license": "Apache-2.0",
"type": "module",
"main": "dist/src/index.js",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli-sdk",
"version": "0.29.0-nightly.20260203.71f46f116",
"version": "0.33.0-nightly.20260228.1ca5c05d0",
"description": "Gemini CLI SDK",
"license": "Apache-2.0",
"repository": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli-test-utils",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.33.0-nightly.20260228.1ca5c05d0",
"private": true,
"main": "src/index.ts",
"license": "Apache-2.0",
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "gemini-cli-vscode-ide-companion",
"displayName": "Gemini CLI Companion",
"description": "Enable Gemini CLI with direct access to your IDE workspace.",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.33.0-nightly.20260228.1ca5c05d0",
"publisher": "google",
"icon": "assets/icon.png",
"repository": {