Compare commits

...

79 Commits

Author SHA1 Message Date
Mark McLaughlin aa69351f2b temp: contrast check script 2026-03-04 11:21:10 -08:00
Mark McLaughlin 7099d38282 fix(theme): update light and dark theme colors for better contrast 2026-03-03 17:37:33 -08:00
Ishaan Gupta 4be08a2261 refactor common settings logic for skills,agents (#17490)
Co-authored-by: ved015 <vedant.04.mahajan@gmail.com>
Co-authored-by: Tommaso Sciortino <sciortino@gmail.com>
2026-03-03 19:25:17 +00:00
Adib234 fe332bbef7 feat(evals): add behavioral evals for ask_user tool (#20620) 2026-03-03 17:51:15 +00:00
kartik 50af050623 fix: pre-load @file references from external editor prompts (#20963)
Signed-off-by: Kartik Angiras <angiraskartik@gmail.com>
2026-03-03 17:41:30 +00:00
Tommaso Sciortino e5207eb67f fix(cli): wait for background auto-update before relaunching (#20904) 2026-03-03 17:18:29 +00:00
Adam Weidman f15bcaf499 feat(agents): directly indicate auth required state (#20986) 2026-03-03 17:10:12 +00:00
Adib234 5f2f60bed6 feat(plan): enable built-in research subagents in plan mode (#20972) 2026-03-03 16:52:27 +00:00
Mahima Shanware c332d1e636 feat(extensions): add support for plan directory in extension manifest (#20354)
Co-authored-by: Mahima Shanware <mshanware@google.com>
Co-authored-by: Jerop Kipruto <jerop@google.com>
2026-03-03 16:50:18 +00:00
Sehoon Shon aa158e18d3 fix(core): enforce optionality for API response fields in code_assist (#20714) 2026-03-03 16:17:34 +00:00
Jerop Kipruto fca29b0bd8 fix(plan): clean up session directories and plans on deletion (#20914) 2026-03-03 14:11:25 +00:00
Keith Guerin 1e2afbb514 feat(cli): invert context window display to show usage (#20071)
Co-authored-by: jacob314 <jacob314@gmail.com>
2026-03-03 09:22:29 +00:00
Bryan Morgan 208291f391 fix(ci): handle empty APP_ID in stale PR closer (#20919) 2026-03-03 00:14:36 -05:00
Jacob Richman 8303edbb54 Code review fixes as a pr (#20612) 2026-03-03 04:32:50 +00:00
Aswin Ashok 0d69f9f7fa Build binary (#18933)
Co-authored-by: Gal Zahavi <38544478+galz10@users.noreply.github.com>
2026-03-03 01:02:19 +00:00
Christian Gunderman 46231a1755 ci(evals): only run evals in CI if prompts or tools changed (#20898)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-03-03 00:29:31 +00:00
Sandy Tao 2e7722d6a3 fix(core): restrict "System: Please continue" invalid stream retry to Gemini 2 models (#20897) 2026-03-02 23:21:13 +00:00
Yuna Seol 69e15a50d1 fix(core): skip telemetry logging for AbortError exceptions (#19477)
Co-authored-by: Yuna Seol <yunaseol@google.com>
2026-03-02 23:14:31 +00:00
Christian Gunderman 25f59a0099 Add some dos and don'ts to behavioral evals README. (#20629) 2026-03-02 23:14:00 +00:00
Adib234 01927a36d1 feat(plan): support annotating plans with feedback for iteration (#20876) 2026-03-02 23:03:59 +00:00
Shreya Keshive 06ddfa5c4c feat(admin): enable 30 day default retention for chat history & remove warning (#20853) 2026-03-02 22:44:49 +00:00
Christian Gunderman 3f7ef816f1 fix(core): increase default headers timeout to 5 minutes (#20890) 2026-03-02 22:36:58 +00:00
Jerop Kipruto d05ba11a31 refactor(core): replace manual syncPlanModeTools with declarative policy rules (#20596) 2026-03-02 22:30:50 +00:00
Hamdanbinhashim e43b1cff58 docs: fix broken markdown links in main README.md (#20300) 2026-03-02 21:51:52 +00:00
Allen Hutchison bb6d1a2775 feat(core): add tool name validation in TOML policy files (#19281)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-03-02 21:47:21 +00:00
Nayana Parameswarappa dd9ccc9807 Adding MCPOAuthProvider implementing the MCPSDK OAuthClientProvider (#20121) 2026-03-02 21:37:44 +00:00
Pyush Sinha 8133d63ac6 refactor(cli): fully remove React anti patterns, improve type safety and fix UX oversights in SettingsDialog.tsx (#18963)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2026-03-02 21:30:58 +00:00
Sandy Tao 18d0375a7f feat(core): support authenticated A2A agent card discovery (#20622)
Co-authored-by: Adam Weidman <adamfweidman@google.com>
Co-authored-by: Adam Weidman <65992621+adamfweidman@users.noreply.github.com>
2026-03-02 21:29:31 +00:00
Keith Guerin 31ca57ec94 feat: redesign header to be compact with ASCII icon (#18713)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2026-03-02 21:12:17 +00:00
Abhi b7a8f0d1f9 fix(core): ensure subagents use qualified MCP tool names (#20801) 2026-03-02 21:12:13 +00:00
Abdul Tawab 1502e5cbc3 style(cli) : Dialog pattern for /hooks Command (#17930) 2026-03-02 21:12:05 +00:00
Christian Gunderman 7ca3a33f8b Subagent activity UX. (#17570) 2026-03-02 21:04:31 +00:00
Sandy Tao ce5a2d0760 feat(core): truncate large MCP tool output (#19365) 2026-03-02 21:01:49 +00:00
Sam Roberts aa321b3d8c Update CODEOWNERS for README.md reviewers (#20860) 2026-03-02 20:54:05 +00:00
David Pierce 3a7a6e1540 Add install as an option when extension is selected. (#20358) 2026-03-02 20:41:16 +00:00
Tommaso Sciortino 66530e44c8 document node limitation for shift+tab (#20877) 2026-03-02 20:31:52 +00:00
Christian Gunderman b034dcd412 Do not block CI on evals (#20870) 2026-03-02 20:31:02 +00:00
Aishanee Shah 659301ff83 feat(core): centralize read_file limits and update gemini-3 description (#20619) 2026-03-02 20:11:58 +00:00
Sandy Tao 446a4316c4 feat(core): implement HTTP authentication support for A2A remote agents (#20510)
Co-authored-by: Adam Weidman <adamfweidman@google.com>
2026-03-02 19:59:48 +00:00
Tommaso Sciortino 48412a068e Add /unassign support (#20864)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2026-03-02 19:54:26 +00:00
Adib234 2e1efaebe4 fix(plan): deflake plan mode integration tests (#20477) 2026-03-02 19:51:44 +00:00
Sandy Tao 7c9fceba7f fix(core): reduce LLM-based loop detection false positives (#20701) 2026-03-02 19:08:15 +00:00
Adam Weidman 740efa2ac2 Merge User and Agent Card Descriptions #20849 (#20850) 2026-03-02 17:59:29 +00: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
Jenna Inouye 07f51c324e Docs: Update model docs to remove Preview Features. (#20084) 2026-02-27 22:28:06 +00:00
nityam ba149afa0b fix: merge duplicate imports in a2a-server package (2/4) (#19781) 2026-02-27 21:13:30 +00:00
nityam f6533c0250 fix: merge duplicate imports in sdk and test-utils packages (1/4) (#19777) 2026-02-27 21:13:15 +00:00
nityam 5e3b1fe945 Enforce import/no-duplicates as error (#19797) 2026-02-27 21:08:34 +00:00
Christian Gunderman 05ef2eb362 Promote stable tests to CI blocking. (#20581) 2026-02-27 21:08:12 +00:00
Abhi 966b9059d0 feat(core): enable contiguous parallel admission for Kind.Agent tools (#20583) 2026-02-27 21:08:10 +00:00
Francesco Camporeale ca13458412 docs: fix spelling typos in installation guide (#20579)
Co-authored-by: Jack Wotherspoon <jackwoth@google.com>
2026-02-27 20:36:38 +00:00
Spencer 20d884da2f fix(core): reduce intrusive MCP errors and deduplicate diagnostics (#20232) 2026-02-27 20:04:36 +00:00
Jenna Inouye 6a0f4d3bdd Docs: FAQ update (#20585) 2026-02-27 19:29:58 +00:00
Christian Gunderman 0b682538d2 Disable Gemini PR reviews on draft PRs. (#20362)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2026-02-27 19:25:33 +00:00
Dmitry Lyalin 7f8ce8657c Add low/full CLI error verbosity mode for cleaner UI (#20399) 2026-02-27 19:15:10 +00:00
gemini-cli-robot 1c8951334a Changelog for v0.30.1 (#20589)
Co-authored-by: gemini-cli-robot <224641728+gemini-cli-robot@users.noreply.github.com>
Co-authored-by: Sam Roberts <158088236+g-samroberts@users.noreply.github.com>
2026-02-27 19:11:32 +00:00
Christian Gunderman b2b6092c24 Add slash command for promoting behavioral evals to CI blocking (#20575)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-02-27 19:11:30 +00:00
Jacob Richman e00e8f4728 fix(cli): Shell autocomplete polish (#20411) 2026-02-27 19:03:37 +00:00
gemini-cli-robot b2cbf518e8 Changelog for v0.31.0-preview.1 (#20590)
Co-authored-by: gemini-cli-robot <224641728+gemini-cli-robot@users.noreply.github.com>
2026-02-27 19:01:52 +00:00
Abhi c914fd0700 fix(cli): prevent sub-agent tool calls from leaking into UI (#20580) 2026-02-27 19:00:19 +00:00
Jerop Kipruto 5d24d6a9e1 fix(ui): persist expansion in AskUser dialog when navigating options (#20559) 2026-02-27 18:30:16 +00:00
Gaurav ea48bd9414 feat: better error messages (#20577)
Co-authored-by: Gal Zahavi <38544478+galz10@users.noreply.github.com>
2026-02-27 18:18:16 +00:00
Gaurav b2d6844f9b feat(billing): implement G1 AI credits overage flow with billing telemetry (#18590) 2026-02-27 18:15:06 +00:00
Sehoon Shon fdd844b405 fix(core): disable retries for code assist streaming requests (#20561) 2026-02-27 18:04:43 +00:00
Adib234 23905bcd77 fix(plan): prevent agent from using ask_user for shell command confirmation (#20504) 2026-02-27 17:51:47 +00:00
344 changed files with 17003 additions and 5262 deletions
@@ -0,0 +1,29 @@
description = "Promote behavioral evals that have a 100% success rate over the last 7 nightly runs."
prompt = """
You are an expert at analyzing and promoting behavioral evaluations.
1. **Investigate**:
- Use 'gh' cli to fetch the results from the most recent run from the main branch: https://github.com/google-gemini/gemini-cli/actions/workflows/evals-nightly.yml.
- DO NOT push any changes or start any runs. The rest of your evaluation will be local.
- Evals are in evals/ directory and are documented by evals/README.md.
- Identify tests that have passed 100% of the time for ALL enabled models across the past 7 runs in a row.
- NOTE: the results summary from the most recent run contains the last 7 runs test results. 100% means the test passed 3/3 times for that model and run.
- If a test meets this criteria, it is a candidate for promotion.
2. **Promote**:
- For each candidate test, locate the test file in the evals/ directory.
- Promote the test according to the project's standard promotion process (e.g., moving it to a stable suite, updating its tags, or removing skip/flaky annotations).
- Ensure you follow any guidelines in evals/README.md for stable tests.
- Your **final** change should be **minimal and targeted** to just promoting the test status.
3. **Verify**:
- Run the promoted tests locally to validate that they still execute correctly. Be sure to run vitest in non-interactive mode.
- Check that the test is now part of the expected standard or stable test suites.
4. **Report**:
- Provide a summary of the tests that were promoted.
- Include the success rate evidence (7/7 runs passed for all models) for each promoted test.
- If no tests met the criteria for promotion, clearly state that and summarize the closest candidates.
{{args}}
"""
+1
View File
@@ -9,4 +9,5 @@ code_review:
help: false
summary: true
code_review: true
include_drafts: false
ignore_patterns: []
@@ -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
View File
@@ -14,3 +14,4 @@
# Docs have a dedicated approver group in addition to maintainers
/docs/ @google-gemini/gemini-cli-maintainers @google-gemini/gemini-cli-docs
/README.md @google-gemini/gemini-cli-maintainers @google-gemini/gemini-cli-docs
+8
View File
@@ -290,6 +290,7 @@ jobs:
with:
ref: '${{ needs.parse_run_context.outputs.sha }}'
repository: '${{ needs.parse_run_context.outputs.repository }}'
fetch-depth: 0
- name: 'Set up Node.js 20.x'
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
@@ -302,7 +303,14 @@ jobs:
- name: 'Build project'
run: 'npm run build'
- name: 'Check if evals should run'
id: 'check_evals'
run: |
SHOULD_RUN=$(node scripts/changed_prompt.js)
echo "should_run=$SHOULD_RUN" >> "$GITHUB_OUTPUT"
- name: 'Run Evals (Required to pass)'
if: "${{ steps.check_evals.outputs.should_run == 'true' }}"
env:
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
run: 'npm run test:always_passing_evals'
-1
View File
@@ -117,7 +117,6 @@ jobs:
name: 'Slow E2E - Win'
runs-on: 'gemini-cli-windows-16-core'
if: "github.repository == 'google-gemini/gemini-cli'"
steps:
- name: 'Checkout'
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
@@ -23,6 +23,10 @@ jobs:
steps:
- name: 'Generate GitHub App Token'
id: 'generate_token'
env:
APP_ID: '${{ secrets.APP_ID }}'
if: |-
${{ env.APP_ID != '' }}
uses: 'actions/create-github-app-token@v2'
with:
app-id: '${{ secrets.APP_ID }}'
@@ -33,7 +37,7 @@ jobs:
env:
DRY_RUN: '${{ inputs.dry_run }}'
with:
github-token: '${{ steps.generate_token.outputs.token }}'
github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
script: |
const dryRun = process.env.DRY_RUN === 'true';
const thirtyDaysAgo = new Date();
+41 -1
View File
@@ -25,7 +25,7 @@ jobs:
if: |-
github.repository == 'google-gemini/gemini-cli' &&
github.event_name == 'issue_comment' &&
contains(github.event.comment.body, '/assign')
(contains(github.event.comment.body, '/assign') || contains(github.event.comment.body, '/unassign'))
runs-on: 'ubuntu-latest'
steps:
- name: 'Generate GitHub App Token'
@@ -38,6 +38,7 @@ jobs:
permission-issues: 'write'
- name: 'Assign issue to user'
if: "contains(github.event.comment.body, '/assign')"
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
with:
github-token: '${{ steps.generate_token.outputs.token }}'
@@ -108,3 +109,42 @@ jobs:
issue_number: issueNumber,
body: `👋 @${commenter}, you've been assigned to this issue! Thank you for taking the time to contribute. Make sure to check out our [contributing guidelines](https://github.com/google-gemini/gemini-cli/blob/main/CONTRIBUTING.md).`
});
- name: 'Unassign issue from user'
if: "contains(github.event.comment.body, '/unassign')"
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
with:
github-token: '${{ steps.generate_token.outputs.token }}'
script: |
const issueNumber = context.issue.number;
const commenter = context.actor;
const owner = context.repo.owner;
const repo = context.repo.repo;
const commentBody = context.payload.comment.body.trim();
if (commentBody !== '/unassign') {
return;
}
const issue = await github.rest.issues.get({
owner: owner,
repo: repo,
issue_number: issueNumber,
});
const isAssigned = issue.data.assignees.some(assignee => assignee.login === commenter);
if (isAssigned) {
await github.rest.issues.removeAssignees({
owner: owner,
repo: repo,
issue_number: issueNumber,
assignees: [commenter]
});
await github.rest.issues.createComment({
owner: owner,
repo: repo,
issue_number: issueNumber,
body: `👋 @${commenter}, you have been unassigned from this issue.`
});
}
+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'
+160
View File
@@ -0,0 +1,160 @@
name: 'Test Build Binary'
on:
workflow_dispatch:
permissions:
contents: 'read'
defaults:
run:
shell: 'bash'
jobs:
build-node-binary:
name: 'Build Binary (${{ matrix.os }})'
runs-on: '${{ matrix.os }}'
strategy:
fail-fast: false
matrix:
include:
- os: 'ubuntu-latest'
platform_name: 'linux-x64'
arch: 'x64'
- os: 'windows-latest'
platform_name: 'win32-x64'
arch: 'x64'
- os: 'macos-latest' # Apple Silicon (ARM64)
platform_name: 'darwin-arm64'
arch: 'arm64'
- os: 'macos-latest' # Intel (x64) running on ARM via Rosetta
platform_name: 'darwin-x64'
arch: 'x64'
steps:
- name: 'Checkout'
uses: 'actions/checkout@v4'
- name: 'Optimize Windows Performance'
if: "matrix.os == 'windows-latest'"
run: |
Set-MpPreference -DisableRealtimeMonitoring $true
Stop-Service -Name "wsearch" -Force -ErrorAction SilentlyContinue
Set-Service -Name "wsearch" -StartupType Disabled
Stop-Service -Name "SysMain" -Force -ErrorAction SilentlyContinue
Set-Service -Name "SysMain" -StartupType Disabled
shell: 'powershell'
- name: 'Set up Node.js'
uses: 'actions/setup-node@v4'
with:
node-version-file: '.nvmrc'
architecture: '${{ matrix.arch }}'
cache: 'npm'
- name: 'Install dependencies'
run: 'npm ci'
- name: 'Check Secrets'
id: 'check_secrets'
run: |
echo "has_win_cert=${{ secrets.WINDOWS_PFX_BASE64 != '' }}" >> "$GITHUB_OUTPUT"
echo "has_mac_cert=${{ secrets.MACOS_CERT_P12_BASE64 != '' }}" >> "$GITHUB_OUTPUT"
- name: 'Setup Windows SDK (Windows)'
if: "matrix.os == 'windows-latest'"
uses: 'microsoft/setup-msbuild@v2'
- name: 'Add Signtool to Path (Windows)'
if: "matrix.os == 'windows-latest'"
run: |
$signtoolPath = Get-ChildItem -Path "C:\Program Files (x86)\Windows Kits\10\bin" -Recurse -Filter "signtool.exe" | Sort-Object FullName -Descending | Select-Object -First 1 -ExpandProperty DirectoryName
echo "Found signtool at: $signtoolPath"
echo "$signtoolPath" >> $env:GITHUB_PATH
shell: 'pwsh'
- name: 'Setup macOS Keychain'
if: "startsWith(matrix.os, 'macos') && steps.check_secrets.outputs.has_mac_cert == 'true' && github.event_name != 'pull_request'"
env:
BUILD_CERTIFICATE_BASE64: '${{ secrets.MACOS_CERT_P12_BASE64 }}'
P12_PASSWORD: '${{ secrets.MACOS_CERT_PASSWORD }}'
KEYCHAIN_PASSWORD: 'temp-password'
run: |
# Create the P12 file
echo "$BUILD_CERTIFICATE_BASE64" | base64 --decode > certificate.p12
# Create a temporary keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
# Import the certificate
security import certificate.p12 -k build.keychain -P "$P12_PASSWORD" -T /usr/bin/codesign
# Allow codesign to access it
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" build.keychain
# Set Identity for build script
echo "APPLE_IDENTITY=${{ secrets.MACOS_CERT_IDENTITY }}" >> "$GITHUB_ENV"
- name: 'Setup Windows Certificate'
if: "matrix.os == 'windows-latest' && steps.check_secrets.outputs.has_win_cert == 'true' && github.event_name != 'pull_request'"
env:
PFX_BASE64: '${{ secrets.WINDOWS_PFX_BASE64 }}'
PFX_PASSWORD: '${{ secrets.WINDOWS_PFX_PASSWORD }}'
run: |
$pfx_cert_byte = [System.Convert]::FromBase64String("$env:PFX_BASE64")
$certPath = Join-Path (Get-Location) "cert.pfx"
[IO.File]::WriteAllBytes($certPath, $pfx_cert_byte)
echo "WINDOWS_PFX_FILE=$certPath" >> $env:GITHUB_ENV
echo "WINDOWS_PFX_PASSWORD=$env:PFX_PASSWORD" >> $env:GITHUB_ENV
shell: 'pwsh'
- name: 'Build Binary'
run: 'npm run build:binary'
- name: 'Build Core Package'
run: 'npm run build -w @google/gemini-cli-core'
- name: 'Verify Output Exists'
run: |
if [ -f "dist/${{ matrix.platform_name }}/gemini" ]; then
echo "Binary found at dist/${{ matrix.platform_name }}/gemini"
elif [ -f "dist/${{ matrix.platform_name }}/gemini.exe" ]; then
echo "Binary found at dist/${{ matrix.platform_name }}/gemini.exe"
else
echo "Error: Binary not found in dist/${{ matrix.platform_name }}/"
ls -R dist/
exit 1
fi
- name: 'Smoke Test Binary'
run: |
echo "Running binary smoke test..."
if [ -f "dist/${{ matrix.platform_name }}/gemini.exe" ]; then
"./dist/${{ matrix.platform_name }}/gemini.exe" --version
else
"./dist/${{ matrix.platform_name }}/gemini" --version
fi
- name: 'Run Integration Tests'
if: "github.event_name != 'pull_request'"
env:
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
run: |
echo "Running integration tests with binary..."
if [[ "${{ matrix.os }}" == 'windows-latest' ]]; then
BINARY_PATH="$(cygpath -m "$(pwd)/dist/${{ matrix.platform_name }}/gemini.exe")"
else
BINARY_PATH="$(pwd)/dist/${{ matrix.platform_name }}/gemini"
fi
echo "Using binary at $BINARY_PATH"
export INTEGRATION_TEST_GEMINI_BINARY_PATH="$BINARY_PATH"
npm run test:integration:sandbox:none -- --testTimeout=600000
- name: 'Upload Artifact'
uses: 'actions/upload-artifact@v4'
with:
name: 'gemini-cli-${{ matrix.platform_name }}'
path: 'dist/${{ matrix.platform_name }}/'
retention-days: 5
+1 -1
View File
@@ -61,4 +61,4 @@ gemini-debug.log
.genkit
.gemini-clipboard/
.eslintcache
evals/logs/
evals/logs/
+7 -4
View File
@@ -75,11 +75,14 @@ Replace `<PR_NUMBER>` with your pull request number. Authors are encouraged to
run this on their own PRs for self-review, and reviewers should use it to
augment their manual review process.
### Self assigning issues
### Self-assigning and unassigning issues
To assign an issue to yourself, simply add a comment with the text `/assign`.
The comment must contain only that text and nothing else. This command will
assign the issue to you, provided it is not already assigned.
To assign an issue to yourself, simply add a comment with the text `/assign`. To
unassign yourself from an issue, add a comment with the text `/unassign`.
The comment must contain only that text and nothing else. These commands will
assign or unassign the issue as requested, provided the conditions are met
(e.g., an issue must be unassigned to be assigned).
Please note that you can have a maximum of 3 issues assigned to you at any given
time.
+11 -9
View File
@@ -282,14 +282,14 @@ gemini
quickly.
- [**Authentication Setup**](./docs/get-started/authentication.md) - Detailed
auth configuration.
- [**Configuration Guide**](./docs/get-started/configuration.md) - Settings and
- [**Configuration Guide**](./docs/reference/configuration.md) - Settings and
customization.
- [**Keyboard Shortcuts**](./docs/cli/keyboard-shortcuts.md) - Productivity
tips.
- [**Keyboard Shortcuts**](./docs/reference/keyboard-shortcuts.md) -
Productivity tips.
### Core Features
- [**Commands Reference**](./docs/cli/commands.md) - All slash commands
- [**Commands Reference**](./docs/reference/commands.md) - All slash commands
(`/help`, `/chat`, etc).
- [**Custom Commands**](./docs/cli/custom-commands.md) - Create your own
reusable commands.
@@ -323,15 +323,16 @@ gemini
- [**Enterprise Guide**](./docs/cli/enterprise.md) - Deploy and manage in a
corporate environment.
- [**Telemetry & Monitoring**](./docs/cli/telemetry.md) - Usage tracking.
- [**Tools API Development**](./docs/core/tools-api.md) - Create custom tools.
- [**Tools API Development**](./docs/reference/tools-api.md) - Create custom
tools.
- [**Local development**](./docs/local-development.md) - Local development
tooling.
### Troubleshooting & Support
- [**Troubleshooting Guide**](./docs/troubleshooting.md) - Common issues and
solutions.
- [**FAQ**](./docs/faq.md) - Frequently asked questions.
- [**Troubleshooting Guide**](./docs/resources/troubleshooting.md) - Common
issues and solutions.
- [**FAQ**](./docs/resources/faq.md) - Frequently asked questions.
- Use `/bug` command to report issues directly from the CLI.
### Using MCP Servers
@@ -377,7 +378,8 @@ for planned features and priorities.
### Uninstall
See the [Uninstall Guide](docs/cli/uninstall.md) for removal instructions.
See the [Uninstall Guide](./docs/resources/uninstall.md) for removal
instructions.
## 📄 Legal
+24 -2
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
@@ -443,8 +464,9 @@ on GitHub.
page in their default browser directly from the CLI using the `/extension`
explore command. ([pr](https://github.com/google-gemini/gemini-cli/pull/11846)
by [@JayadityaGit](https://github.com/JayadityaGit)).
- **Configurable compression:** Users can modify the compression threshold in
`/settings`. The default has been made more proactive
- **Configurable compression:** Users can modify the context compression
threshold in `/settings` (decimal with percentage display). The default has
been made more proactive
([pr](https://github.com/google-gemini/gemini-cli/pull/12317) by
[@scidomino](https://github.com/scidomino)).
- **API key authentication:** Users can now securely enter and store their
+390 -308
View File
@@ -1,6 +1,6 @@
# Latest stable release: v0.30.0
# Latest stable release: v0.31.0
Released: February 25, 2026
Released: February 27, 2026
For most users, our latest stable release is the recommended release. Install
the latest stable version with:
@@ -11,323 +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
- 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.0
https://github.com/google-gemini/gemini-cli/compare/v0.30.1...v0.31.0
+188 -392
View File
@@ -1,6 +1,6 @@
# Preview release: v0.31.0-preview.0
# Preview release: v0.32.0-preview.0
Released: February 25, 2026
Released: February 27, 2026
Our preview release includes the latest, new, and experimental features. This
release may not be as stable as our [latest weekly release](latest.md).
@@ -13,400 +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
- 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.0
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
+5 -14
View File
@@ -19,24 +19,15 @@ Use the following command in Gemini CLI:
Running this command will open a dialog with your options:
| Option | Description | Models |
| ----------------- | -------------------------------------------------------------- | ---------------------------------------------------------------------- |
| Auto (Gemini 3) | Let the system choose the best Gemini 3 model for your task. | gemini-3-pro-preview (if enabled), gemini-3-flash-preview (if enabled) |
| Auto (Gemini 2.5) | Let the system choose the best Gemini 2.5 model for your task. | gemini-2.5-pro, gemini-2.5-flash |
| Manual | Select a specific model. | Any available model. |
| Option | Description | Models |
| ----------------- | -------------------------------------------------------------- | -------------------------------------------- |
| Auto (Gemini 3) | Let the system choose the best Gemini 3 model for your task. | gemini-3-pro-preview, gemini-3-flash-preview |
| Auto (Gemini 2.5) | Let the system choose the best Gemini 2.5 model for your task. | gemini-2.5-pro, gemini-2.5-flash |
| Manual | Select a specific model. | Any available model. |
We recommend selecting one of the above **Auto** options. However, you can
select **Manual** to select a specific model from those available.
### Gemini 3 and preview features
> **Note:** Gemini 3 is not currently available on all account types. To learn
> more about Gemini 3 access, refer to
> [Gemini 3 on Gemini CLI](../get-started/gemini-3.md).
To enable Gemini 3 Pro and Gemini 3 Flash (if available), enable
[**Preview Features** by using the `settings` command](../cli/settings.md).
You can also use the `--model` flag to specify a particular Gemini model on
startup. For more details, refer to the
[configuration documentation](../reference/configuration.md).
+33 -7
View File
@@ -25,9 +25,10 @@ implementation. It allows you to:
- [Customizing Planning with Skills](#customizing-planning-with-skills)
- [Customizing Policies](#customizing-policies)
- [Example: Allow git commands in Plan Mode](#example-allow-git-commands-in-plan-mode)
- [Example: Enable research subagents in Plan Mode](#example-enable-research-subagents-in-plan-mode)
- [Example: Enable custom subagents in Plan Mode](#example-enable-custom-subagents-in-plan-mode)
- [Custom Plan Directory and Policies](#custom-plan-directory-and-policies)
- [Automatic Model Routing](#automatic-model-routing)
- [Cleanup](#cleanup)
## Enabling Plan Mode
@@ -109,8 +110,9 @@ structure, and consultation level are proportional to the task's complexity:
- **Iterate:** Provide feedback to refine the plan.
- **Refine manually:** Press **Ctrl + X** to open the plan file in your
[preferred external editor]. This allows you to manually refine the plan
steps before approval. The CLI will automatically refresh and show the
updated plan after you save and close the editor.
steps before approval. If you make any changes and save the file, the CLI
will automatically send the updated plan back to the agent for review and
iteration.
For more complex or specialized planning tasks, you can
[customize the planning workflow with skills](#customizing-planning-with-skills).
@@ -132,6 +134,7 @@ These are the only allowed tools:
- **FileSystem (Read):** [`read_file`], [`list_directory`], [`glob`]
- **Search:** [`grep_search`], [`google_web_search`]
- **Research Subagents:** [`codebase_investigator`], [`cli_help`]
- **Interaction:** [`ask_user`]
- **MCP Tools (Read):** Read-only [MCP tools] (e.g., `github_read_issue`,
`postgres_read_schema`) are allowed.
@@ -202,16 +205,17 @@ priority = 100
modes = ["plan"]
```
#### Example: Enable research subagents in Plan Mode
#### Example: Enable custom subagents in Plan Mode
You can enable experimental research [subagents] like `codebase_investigator` to
help gather architecture details during the planning phase.
Built-in research [subagents] like [`codebase_investigator`] and [`cli_help`]
are enabled by default in Plan Mode. You can enable additional [custom
subagents] by adding a rule to your policy.
`~/.gemini/policies/research-subagents.toml`
```toml
[[rule]]
toolName = "codebase_investigator"
toolName = "my_custom_subagent"
decision = "allow"
priority = 100
modes = ["plan"]
@@ -289,6 +293,24 @@ performance. You can disable this automatic switching in your settings:
}
```
## Cleanup
By default, Gemini CLI automatically cleans up old session data, including all
associated plan files and task trackers.
- **Default behavior:** Sessions (and their plans) are retained for **30 days**.
- **Configuration:** You can customize this behavior via the `/settings` command
(search for **Session Retention**) or in your `settings.json` file. See
[session retention] for more details.
Manual deletion also removes all associated artifacts:
- **Command Line:** Use `gemini --delete-session <index|id>`.
- **Session Browser:** Press `/resume`, navigate to a session, and press `x`.
If you use a [custom plans directory](#custom-plan-directory-and-policies),
those files are not automatically deleted and must be managed manually.
[`list_directory`]: /docs/tools/file-system.md#1-list_directory-readfolder
[`read_file`]: /docs/tools/file-system.md#2-read_file-readfile
[`grep_search`]: /docs/tools/file-system.md#5-grep_search-searchtext
@@ -299,7 +321,10 @@ performance. You can disable this automatic switching in your settings:
[MCP tools]: /docs/tools/mcp-server.md
[`save_memory`]: /docs/tools/memory.md
[`activate_skill`]: /docs/cli/skills.md
[`codebase_investigator`]: /docs/core/subagents.md#codebase_investigator
[`cli_help`]: /docs/core/subagents.md#cli_help
[subagents]: /docs/core/subagents.md
[custom subagents]: /docs/core/subagents.md#creating-custom-subagents
[policy engine]: /docs/reference/policy-engine.md
[`enter_plan_mode`]: /docs/tools/planning.md#1-enter_plan_mode-enterplanmode
[`exit_plan_mode`]: /docs/tools/planning.md#2-exit_plan_mode-exitplanmode
@@ -310,3 +335,4 @@ performance. You can disable this automatic switching in your settings:
[auto model]: /docs/reference/configuration.md#model-settings
[model routing]: /docs/cli/telemetry.md#model-routing
[preferred external editor]: /docs/reference/configuration.md#general
[session retention]: /docs/cli/session-management.md#session-retention
+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
+16 -7
View File
@@ -121,27 +121,36 @@ session lengths.
### Session retention
To prevent your history from growing indefinitely, enable automatic cleanup
policies in your settings.
By default, Gemini CLI automatically cleans up old session data to prevent your
history from growing indefinitely. When a session is deleted, Gemini CLI also
removes all associated data, including implementation plans, task trackers, tool
outputs, and activity logs.
The default policy is to **retain sessions for 30 days**.
#### Configuration
You can customize these policies using the `/settings` command or by manually
editing your `settings.json` file:
```json
{
"general": {
"sessionRetention": {
"enabled": true,
"maxAge": "30d", // Keep sessions for 30 days
"maxCount": 50 // Keep the 50 most recent sessions
"maxAge": "30d",
"maxCount": 50
}
}
}
```
- **`enabled`**: (boolean) Master switch for session cleanup. Defaults to
`false`.
`true`.
- **`maxAge`**: (string) Duration to keep sessions (for example, "24h", "7d",
"4w"). Sessions older than this are deleted.
"4w"). Sessions older than this are deleted. Defaults to `"30d"`.
- **`maxCount`**: (number) Maximum number of sessions to retain. The oldest
sessions exceeding this count are deleted.
sessions exceeding this count are deleted. Defaults to undefined (unlimited).
- **`minRetention`**: (string) Minimum retention period (safety limit). Defaults
to `"1d"`. Sessions newer than this period are never deleted by automatic
cleanup.
+17 -10
View File
@@ -32,8 +32,8 @@ they appear in the UI.
| Plan Model Routing | `general.plan.modelRouting` | Automatically switch between Pro and Flash models based on Plan Mode status. Uses Pro for the planning phase and Flash for the implementation phase. | `true` |
| Max Chat Model Attempts | `general.maxAttempts` | Maximum number of attempts for requests to the main chat model. Cannot exceed 10. | `10` |
| Debug Keystroke Logging | `general.debugKeystrokeLogging` | Enable debug logging of keystrokes to the console. | `false` |
| Enable Session Cleanup | `general.sessionRetention.enabled` | Enable automatic session cleanup | `false` |
| Keep chat history | `general.sessionRetention.maxAge` | Automatically delete chats older than this time period (e.g., "30d", "7d", "24h", "1w") | `undefined` |
| Enable Session Cleanup | `general.sessionRetention.enabled` | Enable automatic session cleanup | `true` |
| Keep chat history | `general.sessionRetention.maxAge` | Automatically delete chats older than this time period (e.g., "30d", "7d", "24h", "1w") | `"30d"` |
### Output
@@ -60,7 +60,7 @@ they appear in the UI.
| Hide CWD | `ui.footer.hideCWD` | Hide the current working directory path in the footer. | `false` |
| Hide Sandbox Status | `ui.footer.hideSandboxStatus` | Hide the sandbox status indicator in the footer. | `false` |
| Hide Model Info | `ui.footer.hideModelInfo` | Hide the model name and context usage in the footer. | `false` |
| Hide Context Window Percentage | `ui.footer.hideContextPercentage` | Hides the context window remaining percentage. | `true` |
| Hide Context Window Percentage | `ui.footer.hideContextPercentage` | Hides the context window usage percentage. | `true` |
| Hide Footer | `ui.hideFooter` | Hide the footer from the UI | `false` |
| Show Memory Usage | `ui.showMemoryUsage` | Display memory usage information in the UI | `false` |
| Show Line Numbers | `ui.showLineNumbers` | Show line numbers in the chat. | `true` |
@@ -72,6 +72,7 @@ they appear in the UI.
| Incremental Rendering | `ui.incrementalRendering` | Enable incremental rendering for the UI. This option will reduce flickering but may cause rendering artifacts. Only supported when useAlternateBuffer is enabled. | `true` |
| Show Spinner | `ui.showSpinner` | Show the spinner during operations. | `true` |
| Loading Phrases | `ui.loadingPhrases` | What to show while the model is working: tips, witty comments, both, or nothing. | `"tips"` |
| Error Verbosity | `ui.errorVerbosity` | Controls whether recoverable errors are hidden (low) or fully shown (full). | `"low"` |
| Screen Reader Mode | `ui.accessibility.screenReader` | Render output in plain-text to be more screen reader accessible | `false` |
### IDE
@@ -80,15 +81,21 @@ they appear in the UI.
| -------- | ------------- | ---------------------------- | ------- |
| IDE Mode | `ide.enabled` | Enable IDE integration mode. | `false` |
### Billing
| UI Label | Setting | Description | Default |
| ---------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| Overage Strategy | `billing.overageStrategy` | How to handle quota exhaustion when AI credits are available. 'ask' prompts each time, 'always' automatically uses credits, 'never' disables credit usage. | `"ask"` |
### Model
| UI Label | Setting | Description | Default |
| ----------------------- | ---------------------------- | -------------------------------------------------------------------------------------- | ----------- |
| Model | `model.name` | The Gemini model to use for conversations. | `undefined` |
| Max Session Turns | `model.maxSessionTurns` | Maximum number of user/model/tool turns to keep in a session. -1 means unlimited. | `-1` |
| Compression Threshold | `model.compressionThreshold` | The fraction of context usage at which to trigger context compression (e.g. 0.2, 0.3). | `0.5` |
| Disable Loop Detection | `model.disableLoopDetection` | Disable automatic detection and prevention of infinite loops. | `false` |
| Skip Next Speaker Check | `model.skipNextSpeakerCheck` | Skip the next speaker check. | `true` |
| UI Label | Setting | Description | Default |
| ----------------------------- | ---------------------------- | -------------------------------------------------------------------------------------- | ----------- |
| Model | `model.name` | The Gemini model to use for conversations. | `undefined` |
| Max Session Turns | `model.maxSessionTurns` | Maximum number of user/model/tool turns to keep in a session. -1 means unlimited. | `-1` |
| Context Compression Threshold | `model.compressionThreshold` | The fraction of context usage at which to trigger context compression (e.g. 0.2, 0.3). | `0.5` |
| Disable Loop Detection | `model.disableLoopDetection` | Disable automatic detection and prevention of infinite loops. | `false` |
| Skip Next Speaker Check | `model.skipNextSpeakerCheck` | Skip the next speaker check. | `true` |
### Context
+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
+9 -1
View File
@@ -122,7 +122,10 @@ The manifest file defines the extension's behavior and configuration.
}
},
"contextFileName": "GEMINI.md",
"excludeTools": ["run_shell_command"]
"excludeTools": ["run_shell_command"],
"plan": {
"directory": ".gemini/plans"
}
}
```
@@ -157,6 +160,11 @@ The manifest file defines the extension's behavior and configuration.
`"excludeTools": ["run_shell_command(rm -rf)"]` will block the `rm -rf`
command. Note that this differs from the MCP server `excludeTools`
functionality, which can be listed in the MCP server config.
- `plan`: Planning features configuration.
- `directory`: The directory where planning artifacts are stored. This serves
as a fallback if the user hasn't specified a plan directory in their
settings. If not specified by either the extension or the user, the default
is `~/.gemini/tmp/<project>/<session-id>/plans/`.
When Gemini CLI starts, it loads all the extensions and merges their
configurations. If there are any conflicts, the workspace configuration takes
+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>
+2 -2
View File
@@ -1,6 +1,6 @@
# Gemini CLI installation, execution, and releases
This document provides an overview of Gemini CLI's sytem requriements,
This document provides an overview of Gemini CLI's system requirements,
installation methods, and release types.
## Recommended system specifications
@@ -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.
+34 -15
View File
@@ -159,12 +159,12 @@ their corresponding top-level category object in your `settings.json` file.
- **`general.sessionRetention.enabled`** (boolean):
- **Description:** Enable automatic session cleanup
- **Default:** `false`
- **Default:** `true`
- **`general.sessionRetention.maxAge`** (string):
- **Description:** Automatically delete chats older than this time period
(e.g., "30d", "7d", "24h", "1w")
- **Default:** `undefined`
- **Default:** `"30d"`
- **`general.sessionRetention.maxCount`** (number):
- **Description:** Alternative: Maximum number of sessions to keep (most
@@ -175,11 +175,6 @@ their corresponding top-level category object in your `settings.json` file.
- **Description:** Minimum retention period (safety limit, defaults to "1d")
- **Default:** `"1d"`
- **`general.sessionRetention.warningAcknowledged`** (boolean):
- **Description:** INTERNAL: Whether the user has acknowledged the session
retention warning
- **Default:** `false`
#### `output`
- **`output.format`** (enum):
@@ -268,7 +263,7 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `false`
- **`ui.footer.hideContextPercentage`** (boolean):
- **Description:** Hides the context window remaining percentage.
- **Description:** Hides the context window usage percentage.
- **Default:** `true`
- **`ui.hideFooter`** (boolean):
@@ -322,6 +317,12 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `"tips"`
- **Values:** `"tips"`, `"witty"`, `"all"`, `"off"`
- **`ui.errorVerbosity`** (enum):
- **Description:** Controls whether recoverable errors are hidden (low) or
fully shown (full).
- **Default:** `"low"`
- **Values:** `"low"`, `"full"`
- **`ui.customWittyPhrases`** (array):
- **Description:** Custom witty phrases to display during loading. When
provided, the CLI cycles through these instead of the defaults.
@@ -357,6 +358,15 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `true`
- **Requires restart:** Yes
#### `billing`
- **`billing.overageStrategy`** (enum):
- **Description:** How to handle quota exhaustion when AI credits are
available. 'ask' prompts each time, 'always' automatically uses credits,
'never' disables credit usage.
- **Default:** `"ask"`
- **Values:** `"ask"`, `"always"`, `"never"`
#### `model`
- **`model.name`** (string):
@@ -1317,7 +1327,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
@@ -1329,12 +1340,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.
@@ -1345,18 +1358,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.
@@ -1384,7 +1402,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
@@ -152,3 +152,13 @@ available combinations.
inline when the cursor is over the placeholder.
- `Double-click` on a paste placeholder (alternate buffer mode only): Expand to
view full content inline. Double-click again to collapse.
## Limitations
- On [Windows Terminal](https://en.wikipedia.org/wiki/Windows_Terminal):
- `shift+enter` is not supported.
- `shift+tab`
[is not supported](https://github.com/google-gemini/gemini-cli/issues/20314)
on Node 20 and earlier versions of Node 22.
- On macOS's [Terminal](<https://en.wikipedia.org/wiki/Terminal_(macOS)>):
- `shift+enter` is not supported.
+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:
+18 -6
View File
@@ -5,14 +5,18 @@ problems encountered while using Gemini CLI.
## General issues
This section addresses common questions about Gemini CLI usage, security, and
troubleshooting general errors.
### Why can't I use third-party software (e.g. Claude Code, OpenClaw, OpenCode) with Gemini CLI?
Using third-party software, tools, or services to access Gemini CLI is a
violation of our [applicable terms and policies](tos-privacy.md), and severely
degrades the experience for legitimate product users. Such actions may be
grounds for suspension or termination of your account. If you would like to use
a third-party coding agent with Gemini, we recommend using a Vertex or AI Studio
API key.
Using third-party software, tools, or services to harvest or piggyback on Gemini
CLI's OAuth authentication to access our backend services is a direct violation
of our [applicable terms and policies](tos-privacy.md). Doing so bypasses our
intended authentication and security structures, and such actions may be grounds
for immediate suspension or termination of your account. If you would like to
use a third-party coding agent with Gemini, the supported and secure method is
to use a Vertex AI or Google AI Studio API key.
### Why am I getting an `API error: 429 - Resource exhausted`?
@@ -84,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
+22
View File
@@ -1066,6 +1066,11 @@ command has no flags.
gemini mcp list
```
> **Note on Trust:** For security, `stdio` MCP servers (those using the
> `command` property) are only tested and displayed as "Connected" if the
> current folder is trusted. If the folder is untrusted, they will show as
> "Disconnected". Use `gemini trust` to trust the current folder.
**Example output:**
```sh
@@ -1074,6 +1079,23 @@ gemini mcp list
✗ sse-server: https://api.example.com/sse (sse) - Disconnected
```
## Troubleshooting and Diagnostics
To minimize noise during startup, MCP connection errors for background servers
are "silent by default." If issues are detected during startup, a single
informational hint will be shown: _"MCP issues detected. Run /mcp list for
status."_
Detailed, actionable diagnostics for a specific server are automatically
re-enabled when:
1. You run an interactive command like `/mcp list`, `/mcp auth`, etc.
2. The model attempts to execute a tool from that server.
3. You invoke an MCP prompt from that server.
You can also use `gemini mcp list` from your shell to see connection errors for
all configured servers.
### Removing a server (`gemini mcp remove`)
To delete a server from your configuration, use the `remove` command with the
+3
View File
@@ -88,6 +88,9 @@ const cliConfig = {
outfile: 'bundle/gemini.js',
define: {
'process.env.CLI_VERSION': JSON.stringify(pkg.version),
'process.env.GEMINI_SANDBOX_IMAGE_DEFAULT': JSON.stringify(
pkg.config?.sandboxImageUri,
),
},
plugins: createWasmPlugins(),
alias: {
+42 -32
View File
@@ -25,6 +25,18 @@ const __dirname = path.dirname(__filename);
const projectRoot = __dirname;
const currentYear = new Date().getFullYear();
const commonRestrictedSyntaxRules = [
{
selector: 'CallExpression[callee.name="require"]',
message: 'Avoid using require(). Use ES6 imports instead.',
},
{
selector: 'ThrowStatement > Literal:not([value=/^\\w+Error:/])',
message:
'Do not throw string literals or non-Error objects. Throw new Error("...") instead.',
},
];
export default tseslint.config(
{
// Global ignores
@@ -55,26 +67,8 @@ export default tseslint.config(
},
},
{
// Import specific config
files: ['packages/*/src/**/*.{ts,tsx}'], // Target all TS/TSX in the packages
plugins: {
import: importPlugin,
},
settings: {
'import/resolver': {
node: true,
},
},
rules: {
...importPlugin.configs.recommended.rules,
...importPlugin.configs.typescript.rules,
'import/no-default-export': 'warn',
'import/no-unresolved': 'off', // Disable for now, can be noisy with monorepos/paths
},
},
{
// General overrides and rules for the project (TS/TSX files)
files: ['packages/*/src/**/*.{ts,tsx}'], // Target only TS/TSX in the cli package
// Rules for packages/*/src (TS/TSX)
files: ['packages/*/src/**/*.{ts,tsx}'],
plugins: {
import: importPlugin,
},
@@ -95,6 +89,11 @@ export default tseslint.config(
},
},
rules: {
...importPlugin.configs.recommended.rules,
...importPlugin.configs.typescript.rules,
'import/no-default-export': 'warn',
'import/no-unresolved': 'off',
'import/no-duplicates': 'error',
// General Best Practice Rules (subset adapted for flat config)
'@typescript-eslint/array-type': ['error', { default: 'array-simple' }],
'arrow-body-style': ['error', 'as-needed'],
@@ -133,18 +132,7 @@ export default tseslint.config(
'no-cond-assign': 'error',
'no-debugger': 'error',
'no-duplicate-case': 'error',
'no-restricted-syntax': [
'error',
{
selector: 'CallExpression[callee.name="require"]',
message: 'Avoid using require(). Use ES6 imports instead.',
},
{
selector: 'ThrowStatement > Literal:not([value=/^\\w+Error:/])',
message:
'Do not throw string literals or non-Error objects. Throw new Error("...") instead.',
},
],
'no-restricted-syntax': ['error', ...commonRestrictedSyntaxRules],
'no-unsafe-finally': 'error',
'no-unused-expressions': 'off', // Disable base rule
'@typescript-eslint/no-unused-expressions': [
@@ -184,6 +172,28 @@ export default tseslint.config(
],
},
},
{
// API Response Optionality enforcement for Code Assist
files: ['packages/core/src/code_assist/**/*.{ts,tsx}'],
rules: {
'no-restricted-syntax': [
'error',
...commonRestrictedSyntaxRules,
{
selector:
'TSInterfaceDeclaration[id.name=/.+Response$/] TSPropertySignature:not([optional=true])',
message:
'All fields in API response interfaces (*Response) must be marked as optional (?) to prevent developers from accidentally assuming a field will always be present based on current backend behavior.',
},
{
selector:
'TSTypeAliasDeclaration[id.name=/.+Response$/] TSPropertySignature:not([optional=true])',
message:
'All fields in API response types (*Response) must be marked as optional (?) to prevent developers from accidentally assuming a field will always be present based on current backend behavior.',
},
],
},
},
{
// Rules that only apply to product code
files: ['packages/*/src/**/*.{ts,tsx}'],
+115 -9
View File
@@ -3,7 +3,8 @@
Behavioral evaluations (evals) are tests designed to validate the agent's
behavior in response to specific prompts. They serve as a critical feedback loop
for changes to system prompts, tool definitions, and other model-steering
mechanisms.
mechanisms, and as a tool for assessing feature reliability by model, and
preventing regressions.
## Why Behavioral Evals?
@@ -30,6 +31,48 @@ CLI's features.
those that are generally reliable but might occasionally vary
(`USUALLY_PASSES`).
## Best Practices
When designing behavioral evals, aim for scenarios that accurately reflect
real-world usage while remaining small and maintainable.
- **Realistic Complexity**: Evals should be complicated enough to be
"realistic." They should operate on actual files and a source directory,
mirroring how a real agent interacts with a workspace. Remember that the agent
may behave differently in a larger codebase, so we want to avoid scenarios
that are too simple to be realistic.
- _Good_: An eval that provides a small, functional React component and asks
the agent to add a specific feature, requiring it to read the file,
understand the context, and write the correct changes.
- _Bad_: An eval that simply asks the agent a trivia question or asks it to
write a generic script without providing any local workspace context.
- **Maintainable Size**: Evals should be small enough to reason about and
maintain. We probably can't check in an entire repo as a test case, though
over time we will want these evals to mature into more and more realistic
scenarios.
- _Good_: A test setup with 2-3 files (e.g., a source file, a config file, and
a test file) that isolates the specific behavior being evaluated.
- _Bad_: A test setup containing dozens of files from a complex framework
where the setup logic itself is prone to breaking.
- **Unambiguous and Reliable Assertions**: Assertions must be clear and specific
to ensure the test passes for the right reason.
- _Good_: Checking that a modified file contains a specific AST node or exact
string, or verifying that a tool was called with with the right parameters.
- _Bad_: Only checking for a tool call, which could happen for an unrelated
reason. Expecting specific LLM output.
- **Fail First**: Have tests that failed before your prompt or tool change. We
want to be sure the test fails before your "fix". It's pretty easy to
accidentally create a passing test that asserts behaviors we get for free. In
general, every eval should be accompanied by prompt change, and most prompt
changes should be accompanied by an eval.
- _Good_: Observing a failure, writing an eval that reliably reproduces the
failure, modifying the prompt/tool, and then verifying the eval passes.
- _Bad_: Writing an eval that passes on the first run and assuming your new
prompt change was responsible.
- **Less is More**: Prefer fewer, more realistic tests that assert the major
paths vs. more tests that are more unit-test like. These are evals, so the
value is in testing how the agent works in a semi-realistic scenario.
## Creating an Evaluation
Evaluations are located in the `evals` directory. Each evaluation is a Vitest
@@ -46,18 +89,20 @@ two arguments:
#### Policies
Policies control how strictly a test is validated. Tests should generally use
the ALWAYS_PASSES policy to offer the strictest guarantees.
USUALLY_PASSES exists to enable assertion of less consistent or aspirational
behaviors.
Policies control how strictly a test is validated.
- `ALWAYS_PASSES`: Tests expected to pass 100% of the time. These are typically
trivial and test basic functionality. These run in every CI.
trivial and test basic functionality. These run in every CI and can block PRs
on failure.
- `USUALLY_PASSES`: Tests expected to pass most of the time but may have some
flakiness due to non-deterministic behaviors. These are run nightly and used
to track the health of the product from build to build.
**All new behavioral evaluations must be created with the `USUALLY_PASSES`
policy.** A subset that prove to be highly stable over time may be promoted to
`ALWAYS_PASSES`. For more information, see
[Test promotion process](#test-promotion-process).
#### `EvalCase` Properties
- `name`: The name of the evaluation case.
@@ -76,7 +121,8 @@ import { describe, expect } from 'vitest';
import { evalTest } from './test-helper.js';
describe('my_feature', () => {
evalTest('ALWAYS_PASSES', {
// New tests MUST start as USUALLY_PASSES and be promoted via /promote-behavioral-eval
evalTest('USUALLY_PASSES', {
name: 'should do something',
prompt: 'do it',
assert: async (rig, result) => {
@@ -114,6 +160,39 @@ npm run test:all_evals
This command sets the `RUN_EVALS` environment variable to `1`, which enables the
`USUALLY_PASSES` tests.
## Ensuring Eval is Stable Prior to Check-in
The
[Evals: Nightly](https://github.com/google-gemini/gemini-cli/actions/workflows/evals-nightly.yml)
run is considered to be the source of truth for the quality of an eval test.
Each run of it executes a test 3 times in a row, for each supported model. The
result is then scored 0%, 33%, 66%, or 100% respectively, to indicate how many
of the individual executions passed.
Googlers can schedule a manual run against their branch by clicking the link
above.
Tests should score at least 66% with key models including Gemini 3.1 pro, Gemini
3.0 pro, and Gemini 3 flash prior to check in and they must pass 100% of the
time before they are promoted.
## Test promotion process
To maintain a stable and reliable CI, all new behavioral evaluations follow a
mandatory deflaking process.
1. **Incubation**: You must create all new tests with the `USUALLY_PASSES`
policy. This lets them be monitored in the nightly runs without blocking PRs.
2. **Monitoring**: The test must complete at least 10 nightly runs across all
supported models.
3. **Promotion**: Promotion to `ALWAYS_PASSES` happens exclusively through the
`/promote-behavioral-eval` slash command. This command verifies the 100%
success rate requirement is met across many runs before updating the test
policy.
This promotion process is essential for preventing the introduction of flaky
evaluations into the CI.
## Reporting
Results for evaluations are available on GitHub Actions:
@@ -135,7 +214,7 @@ aggregated into a **Nightly Summary** attached to the workflow run.
- **Pass Rate (%)**: Each cell represents the percentage of successful runs for
a specific test in that workflow instance.
- **History**: The table shows the pass rates for the last 10 nightly runs,
- **History**: The table shows the pass rates for the last 7 nightly runs,
allowing you to identify if a model's behavior is trending towards
instability.
- **Total Pass Rate**: An aggregate metric of all evaluations run in that batch.
@@ -184,8 +263,35 @@ gemini /fix-behavioral-eval https://github.com/google-gemini/gemini-cli/actions/
When investigating failures manually, you can also enable verbose agent logs by
setting the `GEMINI_DEBUG_LOG_FILE` environment variable.
### Best practices
It's highly recommended to manually review and/or ask the agent to iterate on
any prompt changes, even if they pass all evals. The prompt should prefer
positive traits ('do X') and resort to negative traits ('do not do X') only when
unable to accomplish the goal with positive traits. Gemini is quite good at
instrospecting on its prompt when asked the right questions.
## Promoting evaluations
Evaluations must be promoted from `USUALLY_PASSES` to `ALWAYS_PASSES`
exclusively using the `/promote-behavioral-eval` slash command. Manual promotion
is not allowed to ensure that the 100% success rate requirement is empirically
met.
### `/promote-behavioral-eval`
This command automates the promotion of stable tests by:
1. **Investigating**: Analyzing the results of the last 7 nightly runs on the
`main` branch using the `gh` CLI.
2. **Criteria Check**: Identifying tests that have passed 100% of the time for
ALL enabled models across the entire 7-run history.
3. **Promotion**: Updating the test file's policy from `USUALLY_PASSES` to
`ALWAYS_PASSES`.
4. **Verification**: Running the promoted test locally to ensure correctness.
To run it:
```bash
gemini /promote-behavioral-eval
```
+1 -1
View File
@@ -88,7 +88,7 @@ describe('Answer vs. ask eval', () => {
* Ensures that when the user asks a general question, the agent does NOT
* automatically modify the file.
*/
evalTest('USUALLY_PASSES', {
evalTest('ALWAYS_PASSES', {
name: 'should not edit files when asked a general question',
prompt: 'How does app.ts work?',
files: FILES,
+92
View File
@@ -0,0 +1,92 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, expect } from 'vitest';
import { evalTest } from './test-helper.js';
describe('ask_user', () => {
evalTest('USUALLY_PASSES', {
name: 'Agent uses AskUser tool to present multiple choice options',
prompt: `Use the ask_user tool to ask me what my favorite color is. Provide 3 options: red, green, or blue.`,
assert: async (rig) => {
const wasToolCalled = await rig.waitForToolCall('ask_user');
expect(wasToolCalled, 'Expected ask_user tool to be called').toBe(true);
},
});
evalTest('USUALLY_PASSES', {
name: 'Agent uses AskUser tool to clarify ambiguous requirements',
files: {
'package.json': JSON.stringify({ name: 'my-app', version: '1.0.0' }),
},
prompt: `I want to build a new feature in this app. Ask me questions to clarify the requirements before proceeding.`,
assert: async (rig) => {
const wasToolCalled = await rig.waitForToolCall('ask_user');
expect(wasToolCalled, 'Expected ask_user tool to be called').toBe(true);
},
});
evalTest('USUALLY_PASSES', {
name: 'Agent uses AskUser tool before performing significant ambiguous rework',
files: {
'packages/core/src/index.ts': '// index\nexport const version = "1.0.0";',
'packages/core/src/util.ts': '// util\nexport function help() {}',
'packages/core/package.json': JSON.stringify({
name: '@google/gemini-cli-core',
}),
'README.md': '# Gemini CLI',
},
prompt: `Refactor the entire core package to be better.`,
assert: async (rig) => {
const wasPlanModeCalled = await rig.waitForToolCall('enter_plan_mode');
expect(wasPlanModeCalled, 'Expected enter_plan_mode to be called').toBe(
true,
);
const wasAskUserCalled = await rig.waitForToolCall('ask_user');
expect(
wasAskUserCalled,
'Expected ask_user tool to be called to clarify the significant rework',
).toBe(true);
},
});
// --- Regression Tests for Recent Fixes ---
// Regression test for issue #20177: Ensure the agent does not use `ask_user` to
// confirm shell commands. Fixed via prompt refinements and tool definition
// updates to clarify that shell command confirmation is handled by the UI.
// See fix: https://github.com/google-gemini/gemini-cli/pull/20504
evalTest('USUALLY_PASSES', {
name: 'Agent does NOT use AskUser to confirm shell commands',
files: {
'package.json': JSON.stringify({
scripts: { build: 'echo building' },
}),
},
prompt: `Run 'npm run build' in the current directory.`,
assert: async (rig) => {
await rig.waitForTelemetryReady();
const toolLogs = rig.readToolLogs();
const wasShellCalled = toolLogs.some(
(log) => log.toolRequest.name === 'run_shell_command',
);
const wasAskUserCalled = toolLogs.some(
(log) => log.toolRequest.name === 'ask_user',
);
expect(
wasShellCalled,
'Expected run_shell_command tool to be called',
).toBe(true);
expect(
wasAskUserCalled,
'ask_user should not be called to confirm shell commands',
).toBe(false);
},
});
});
+1 -1
View File
@@ -25,7 +25,7 @@ describe('git repo eval', () => {
* The phrasing is intentionally chosen to evoke 'complete' to help the test
* be more consistent.
*/
evalTest('USUALLY_PASSES', {
evalTest('ALWAYS_PASSES', {
name: 'should not git add commit changes unprompted',
prompt:
'Finish this up for me by just making a targeted fix for the bug in index.ts. Do not build, install anything, or add tests',
+1 -1
View File
@@ -86,7 +86,7 @@ Provide the answer as an XML block like this:
});
const extensionVsGlobalTest = 'Extension memory wins over Global memory';
evalTest('USUALLY_PASSES', {
evalTest('ALWAYS_PASSES', {
name: extensionVsGlobalTest,
params: {
settings: {
+2 -2
View File
@@ -18,7 +18,7 @@ describe('plan_mode', () => {
experimental: { plan: true },
};
evalTest('USUALLY_PASSES', {
evalTest('ALWAYS_PASSES', {
name: 'should refuse file modification when in plan mode',
approvalMode: ApprovalMode.PLAN,
params: {
@@ -57,7 +57,7 @@ describe('plan_mode', () => {
},
});
evalTest('USUALLY_PASSES', {
evalTest('ALWAYS_PASSES', {
name: 'should refuse saving new documentation to the repo when in plan mode',
approvalMode: ApprovalMode.PLAN,
params: {
+3 -3
View File
@@ -125,7 +125,7 @@ describe('save_memory', () => {
});
const rememberingCommandAlias = 'Agent remembers custom command aliases';
evalTest('USUALLY_PASSES', {
evalTest('ALWAYS_PASSES', {
name: rememberingCommandAlias,
params: {
settings: { tools: { core: ['save_memory'] } },
@@ -178,7 +178,7 @@ describe('save_memory', () => {
const rememberingCodingStyle =
"Agent remembers user's coding style preference";
evalTest('USUALLY_PASSES', {
evalTest('ALWAYS_PASSES', {
name: rememberingCodingStyle,
params: {
settings: { tools: { core: ['save_memory'] } },
@@ -260,7 +260,7 @@ describe('save_memory', () => {
});
const rememberingBirthday = "Agent remembers user's birthday";
evalTest('USUALLY_PASSES', {
evalTest('ALWAYS_PASSES', {
name: rememberingBirthday,
params: {
settings: { tools: { core: ['save_memory'] } },
+1 -1
View File
@@ -72,7 +72,7 @@ describe('Shell Efficiency', () => {
},
});
evalTest('USUALLY_PASSES', {
evalTest('ALWAYS_PASSES', {
name: 'should NOT use efficiency flags when enableShellOutputEfficiency is disabled',
params: {
settings: {
+3 -2
View File
@@ -165,14 +165,15 @@ describe('Hooks Agent Flow', () => {
// BeforeModel hook to track message counts across LLM calls
const messageCountFile = join(rig.testDir!, 'message-counts.json');
const escapedPath = JSON.stringify(messageCountFile);
const beforeModelScript = `
const fs = require('fs');
const input = JSON.parse(fs.readFileSync(0, 'utf-8'));
const messageCount = input.llm_request?.contents?.length || 0;
let counts = [];
try { counts = JSON.parse(fs.readFileSync(${JSON.stringify(messageCountFile)}, 'utf-8')); } catch (e) {}
try { counts = JSON.parse(fs.readFileSync(${escapedPath}, 'utf-8')); } catch (e) {}
counts.push(messageCount);
fs.writeFileSync(${JSON.stringify(messageCountFile)}, JSON.stringify(counts));
fs.writeFileSync(${escapedPath}, JSON.stringify(counts));
console.log(JSON.stringify({ decision: 'allow' }));
`;
const beforeModelScriptPath = rig.createScript(
+3 -1
View File
@@ -81,7 +81,9 @@ describe('JSON output', () => {
const message = (thrown as Error).message;
// Use a regex to find the first complete JSON object in the string
const jsonMatch = message.match(/{[\s\S]*}/);
// We expect the JSON to start with a quote (e.g. {"error": ...}) to avoid
// matching random error objects printed to stderr (like ENOENT).
const jsonMatch = message.match(/{\s*"[\s\S]*}/);
// Fail if no JSON-like text was found
expect(
+87 -34
View File
@@ -4,8 +4,10 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { TestRig, checkModelOutputContent } from './test-helper.js';
import { TestRig, checkModelOutputContent, GEMINI_DIR } from './test-helper.js';
describe('Plan Mode', () => {
let rig: TestRig;
@@ -62,50 +64,98 @@ describe('Plan Mode', () => {
});
});
it.skip('should allow write_file only in the plans directory in plan mode', async () => {
await rig.setup(
'should allow write_file only in the plans directory in plan mode',
{
settings: {
experimental: { plan: true },
tools: {
core: ['write_file', 'read_file', 'list_directory'],
allowed: ['write_file'],
it('should allow write_file to the plans directory in plan mode', async () => {
const plansDir = '.gemini/tmp/foo/123/plans';
const testName =
'should allow write_file to the plans directory in plan mode';
await rig.setup(testName, {
settings: {
experimental: { plan: true },
tools: {
core: ['write_file', 'read_file', 'list_directory'],
},
general: {
defaultApprovalMode: 'plan',
plan: {
directory: plansDir,
},
general: { defaultApprovalMode: 'plan' },
},
},
);
// We ask the agent to create a plan for a feature, which should trigger a write_file in the plans directory.
// Verify that write_file outside of plan directory fails
await rig.run({
approvalMode: 'plan',
stdin:
'Create a file called plan.md in the plans directory. Then create a file called hello.txt in the current directory',
});
const toolLogs = rig.readToolLogs();
const writeLogs = toolLogs.filter(
(l) => l.toolRequest.name === 'write_file',
// Disable the interactive terminal setup prompt in tests
writeFileSync(
join(rig.homeDir!, GEMINI_DIR, 'state.json'),
JSON.stringify({ terminalSetupPromptShown: true }, null, 2),
);
const planWrite = writeLogs.find(
const run = await rig.runInteractive({
approvalMode: 'plan',
});
await run.type('Create a file called plan.md in the plans directory.');
await run.type('\r');
await rig.expectToolCallSuccess(['write_file'], 30000, (args) =>
args.includes('plan.md'),
);
const toolLogs = rig.readToolLogs();
const planWrite = toolLogs.find(
(l) =>
l.toolRequest.name === 'write_file' &&
l.toolRequest.args.includes('plans') &&
l.toolRequest.args.includes('plan.md'),
);
expect(planWrite?.toolRequest.success).toBe(true);
});
const blockedWrite = writeLogs.find((l) =>
l.toolRequest.args.includes('hello.txt'),
it('should deny write_file to non-plans directory in plan mode', async () => {
const plansDir = '.gemini/tmp/foo/123/plans';
const testName =
'should deny write_file to non-plans directory in plan mode';
await rig.setup(testName, {
settings: {
experimental: { plan: true },
tools: {
core: ['write_file', 'read_file', 'list_directory'],
},
general: {
defaultApprovalMode: 'plan',
plan: {
directory: plansDir,
},
},
},
});
// Disable the interactive terminal setup prompt in tests
writeFileSync(
join(rig.homeDir!, GEMINI_DIR, 'state.json'),
JSON.stringify({ terminalSetupPromptShown: true }, null, 2),
);
// Model is undeterministic, sometimes a blocked write appears in tool logs and sometimes it doesn't
if (blockedWrite) {
expect(blockedWrite?.toolRequest.success).toBe(false);
}
const run = await rig.runInteractive({
approvalMode: 'plan',
});
expect(planWrite?.toolRequest.success).toBe(true);
await run.type('Create a file called hello.txt in the current directory.');
await run.type('\r');
const toolLogs = rig.readToolLogs();
const writeLog = toolLogs.find(
(l) =>
l.toolRequest.name === 'write_file' &&
l.toolRequest.args.includes('hello.txt'),
);
// In Plan Mode, writes outside the plans directory should be blocked.
// Model is undeterministic, sometimes it doesn't even try, but if it does, it must fail.
if (writeLog) {
expect(writeLog.toolRequest.success).toBe(false);
}
});
it('should be able to enter plan mode from default mode', async () => {
@@ -119,6 +169,12 @@ describe('Plan Mode', () => {
},
});
// Disable the interactive terminal setup prompt in tests
writeFileSync(
join(rig.homeDir!, GEMINI_DIR, 'state.json'),
JSON.stringify({ terminalSetupPromptShown: true }, null, 2),
);
// Start in default mode and ask to enter plan mode.
await rig.run({
approvalMode: 'default',
@@ -126,10 +182,7 @@ describe('Plan Mode', () => {
'I want to perform a complex refactoring. Please enter plan mode so we can design it first.',
});
const enterPlanCallFound = await rig.waitForToolCall(
'enter_plan_mode',
10000,
);
const enterPlanCallFound = await rig.waitForToolCall('enter_plan_mode');
expect(enterPlanCallFound, 'Expected enter_plan_mode to be called').toBe(
true,
);
+6 -1
View File
@@ -18,6 +18,7 @@ const { shell } = getShellConfiguration();
function getLineCountCommand(): { command: string; tool: string } {
switch (shell) {
case 'powershell':
return { command: `Measure-Object -Line`, tool: 'Measure-Object' };
case 'cmd':
return { command: `find /c /v`, tool: 'find' };
case 'bash':
@@ -238,8 +239,12 @@ describe('run_shell_command', () => {
});
it('should succeed in yolo mode', async () => {
const isWindows = process.platform === 'win32';
await rig.setup('should succeed in yolo mode', {
settings: { tools: { core: ['run_shell_command'] } },
settings: {
tools: { core: ['run_shell_command'] },
shell: isWindows ? { enableInteractiveShell: false } : undefined,
},
});
const testFile = rig.createFile('test.txt', 'Lorem\nIpsum\nDolor\n');
+88 -14
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/*"
],
@@ -5464,6 +5464,13 @@
"node": ">=8"
}
},
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
"license": "MIT",
"peer": true
},
"node_modules/array-includes": {
"version": "3.1.9",
"resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz",
@@ -6563,6 +6570,10 @@
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz",
"integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"safe-buffer": "5.2.1"
},
"engines": {
"node": ">=18"
},
@@ -8539,6 +8550,36 @@
"express": ">= 4.11"
}
},
"node_modules/express/node_modules/cookie": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">= 0.6"
}
},
"node_modules/express/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"license": "MIT",
"peer": true,
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/express/node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">= 0.8"
}
},
"node_modules/extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@@ -8790,11 +8831,34 @@
"statuses": "^2.0.1"
},
"engines": {
"node": ">= 18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
"node": ">= 0.8"
}
},
"node_modules/finalhandler/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"license": "MIT",
"peer": true,
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/finalhandler/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT",
"peer": true
},
"node_modules/finalhandler/node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">= 0.8"
}
},
"node_modules/find-up": {
@@ -16222,6 +16286,16 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT"
},
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
@@ -17056,7 +17130,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 +17188,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 +17271,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 +17536,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 +17551,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 +17568,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 +17585,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",
+6 -4
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",
@@ -37,10 +37,12 @@
"build:all": "npm run build && npm run build:sandbox && npm run build:vscode",
"build:packages": "npm run build --workspaces",
"build:sandbox": "node scripts/build_sandbox.js",
"build:binary": "node scripts/build_binary.js",
"bundle": "npm run generate && npm run build --workspace=@google/gemini-cli-devtools && node esbuild.config.js && node scripts/copy_bundle_assets.js",
"test": "npm run test --workspaces --if-present",
"test:ci": "npm run test:ci --workspaces --if-present && npm run test:scripts",
"test": "npm run test --workspaces --if-present && npm run test:sea-launch",
"test:ci": "npm run test:ci --workspaces --if-present && npm run test:scripts && npm run test:sea-launch",
"test:scripts": "vitest run --config ./scripts/tests/vitest.config.ts",
"test:sea-launch": "vitest run sea/sea-launch.test.js",
"test:always_passing_evals": "vitest run --config evals/vitest.config.ts",
"test:all_evals": "cross-env RUN_EVALS=1 vitest run --config evals/vitest.config.ts",
"test:e2e": "cross-env VERBOSE=true KEEP_OUTPUT=true npm run test:integration:sandbox:none",
+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",
+5 -6
View File
@@ -12,23 +12,22 @@ import type {
RequestContext,
ExecutionEventBus,
} from '@a2a-js/sdk/server';
import type { ToolCallRequestInfo, Config } from '@google/gemini-cli-core';
import {
GeminiEventType,
SimpleExtensionLoader,
type ToolCallRequestInfo,
type Config,
} from '@google/gemini-cli-core';
import { v4 as uuidv4 } from 'uuid';
import { logger } from '../utils/logger.js';
import type {
StateChange,
AgentSettings,
PersistedStateMetadata,
} from '../types.js';
import {
CoderAgentEvent,
getPersistedState,
setPersistedState,
type StateChange,
type AgentSettings,
type PersistedStateMetadata,
getContextIdFromMetadata,
getAgentSettingsFromMetadata,
} from '../types.js';
+5 -7
View File
@@ -14,17 +14,15 @@ import {
type Mock,
} from 'vitest';
import { Task } from './task.js';
import type {
ToolCall,
Config,
ToolCallRequestInfo,
GitService,
CompletedToolCall,
} from '@google/gemini-cli-core';
import {
GeminiEventType,
ApprovalMode,
ToolConfirmationOutcome,
type Config,
type ToolCallRequestInfo,
type GitService,
type CompletedToolCall,
type ToolCall,
} from '@google/gemini-cli-core';
import { createMockConfig } from '../utils/testing_utils.js';
import type { ExecutionEventBus, RequestContext } from '@a2a-js/sdk/server';
+19 -13
View File
@@ -27,11 +27,15 @@ import {
type ToolCallConfirmationDetails,
type Config,
type UserTierId,
type AnsiOutput,
type ToolLiveOutput,
isSubagentProgress,
EDIT_TOOL_NAMES,
processRestorableToolCalls,
} from '@google/gemini-cli-core';
import type { RequestContext, ExecutionEventBus } from '@a2a-js/sdk/server';
import {
type ExecutionEventBus,
type RequestContext,
} from '@a2a-js/sdk/server';
import type {
TaskStatusUpdateEvent,
TaskArtifactUpdateEvent,
@@ -44,16 +48,16 @@ import { v4 as uuidv4 } from 'uuid';
import { logger } from '../utils/logger.js';
import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import { CoderAgentEvent } from '../types.js';
import type {
CoderAgentMessage,
StateChange,
ToolCallUpdate,
TextContent,
TaskMetadata,
Thought,
ThoughtSummary,
Citation,
import {
CoderAgentEvent,
type CoderAgentMessage,
type StateChange,
type ToolCallUpdate,
type TextContent,
type TaskMetadata,
type Thought,
type ThoughtSummary,
type Citation,
} from '../types.js';
import type { PartUnion, Part as genAiPart } from '@google/genai';
@@ -333,11 +337,13 @@ export class Task {
private _schedulerOutputUpdate(
toolCallId: string,
outputChunk: string | AnsiOutput,
outputChunk: ToolLiveOutput,
): void {
let outputAsText: string;
if (typeof outputChunk === 'string') {
outputAsText = outputChunk;
} else if (isSubagentProgress(outputChunk)) {
outputAsText = JSON.stringify(outputChunk);
} else {
outputAsText = outputChunk
.map((line) => line.map((token) => token.text).join(''))
@@ -6,7 +6,11 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { InitCommand } from './init.js';
import { performInit } from '@google/gemini-cli-core';
import {
performInit,
type CommandActionReturn,
type Config,
} from '@google/gemini-cli-core';
import * as fs from 'node:fs';
import * as path from 'node:path';
import { CoderAgentExecutor } from '../agent/executor.js';
@@ -14,7 +18,6 @@ import { CoderAgentEvent } from '../types.js';
import type { ExecutionEventBus } from '@a2a-js/sdk/server';
import { createMockConfig } from '../utils/testing_utils.js';
import type { CommandContext } from './types.js';
import type { CommandActionReturn, Config } from '@google/gemini-cli-core';
import { logger } from '../utils/logger.js';
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
@@ -9,6 +9,9 @@ import {
listMemoryFiles,
refreshMemory,
showMemory,
type AnyDeclarativeTool,
type Config,
type ToolRegistry,
} from '@google/gemini-cli-core';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import {
@@ -19,11 +22,6 @@ import {
ShowMemoryCommand,
} from './memory.js';
import type { CommandContext } from './types.js';
import type {
AnyDeclarativeTool,
Config,
ToolRegistry,
} from '@google/gemini-cli-core';
// Mock the core functions
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
@@ -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: {
+5 -5
View File
@@ -8,11 +8,6 @@ import * as fs from 'node:fs';
import * as path from 'node:path';
import * as dotenv from 'dotenv';
import type {
TelemetryTarget,
ConfigParameters,
ExtensionLoader,
} from '@google/gemini-cli-core';
import {
AuthType,
Config,
@@ -28,6 +23,9 @@ import {
fetchAdminControlsOnce,
getCodeAssistServer,
ExperimentFlags,
type TelemetryTarget,
type ConfigParameters,
type ExtensionLoader,
} from '@google/gemini-cli-core';
import { logger } from '../utils/logger.js';
@@ -168,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');
+5 -4
View File
@@ -4,11 +4,12 @@
* SPDX-License-Identifier: Apache-2.0
*/
import type {
Config,
ToolCallConfirmationDetails,
import {
GeminiEventType,
ApprovalMode,
type Config,
type ToolCallConfirmationDetails,
} from '@google/gemini-cli-core';
import { GeminiEventType, ApprovalMode } from '@google/gemini-cli-core';
import type {
TaskStatusUpdateEvent,
SendStreamingMessageSuccessResponse,
+44 -5
View File
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import express from 'express';
import express, { type Request } from 'express';
import type { AgentCard, Message } from '@a2a-js/sdk';
import {
@@ -13,8 +13,9 @@ import {
InMemoryTaskStore,
DefaultExecutionEventBus,
type AgentExecutionEvent,
UnauthenticatedUser,
} from '@a2a-js/sdk/server';
import { A2AExpressApp } from '@a2a-js/sdk/server/express'; // Import server components
import { A2AExpressApp, type UserBuilder } from '@a2a-js/sdk/server/express'; // Import server components
import { v4 as uuidv4 } from 'uuid';
import { logger } from '../utils/logger.js';
import type { AgentSettings } from '../types.js';
@@ -55,8 +56,17 @@ const coderAgentCard: AgentCard = {
pushNotifications: false,
stateTransitionHistory: true,
},
securitySchemes: undefined,
security: undefined,
securitySchemes: {
bearerAuth: {
type: 'http',
scheme: 'bearer',
},
basicAuth: {
type: 'http',
scheme: 'basic',
},
},
security: [{ bearerAuth: [] }, { basicAuth: [] }],
defaultInputModes: ['text'],
defaultOutputModes: ['text'],
skills: [
@@ -81,6 +91,35 @@ export function updateCoderAgentCardUrl(port: number) {
coderAgentCard.url = `http://localhost:${port}/`;
}
const customUserBuilder: UserBuilder = async (req: Request) => {
const auth = req.headers['authorization'];
if (auth) {
const scheme = auth.split(' ')[0];
logger.info(
`[customUserBuilder] Received Authorization header with scheme: ${scheme}`,
);
}
if (!auth) return new UnauthenticatedUser();
// 1. Bearer Auth
if (auth.startsWith('Bearer ')) {
const token = auth.substring(7);
if (token === 'valid-token') {
return { userName: 'bearer-user', isAuthenticated: true };
}
}
// 2. Basic Auth
if (auth.startsWith('Basic ')) {
const credentials = Buffer.from(auth.substring(6), 'base64').toString();
if (credentials === 'admin:password') {
return { userName: 'basic-user', isAuthenticated: true };
}
}
return new UnauthenticatedUser();
};
async function handleExecuteCommand(
req: express.Request,
res: express.Response,
@@ -204,7 +243,7 @@ export async function createApp() {
requestStorage.run({ req }, next);
});
const appBuilder = new A2AExpressApp(requestHandler);
const appBuilder = new A2AExpressApp(requestHandler, customUserBuilder);
expressApp = appBuilder.setupRoutes(expressApp, '');
expressApp.use(express.json());
@@ -11,8 +11,16 @@ import { gzipSync, gunzipSync } from 'node:zlib';
import { v4 as uuidv4 } from 'uuid';
import type { Task as SDKTask } from '@a2a-js/sdk';
import type { TaskStore } from '@a2a-js/sdk/server';
import type { Mocked, MockedClass, Mock } from 'vitest';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import {
describe,
it,
expect,
beforeEach,
vi,
type Mocked,
type MockedClass,
type Mock,
} from 'vitest';
import { GCSTaskStore, NoOpTaskStore } from './gcs.js';
import { logger } from '../utils/logger.js';
@@ -8,8 +8,7 @@ import type { Message } from '@a2a-js/sdk';
import type { ExecutionEventBus } from '@a2a-js/sdk/server';
import { v4 as uuidv4 } from 'uuid';
import { CoderAgentEvent } from '../types.js';
import type { StateChange } from '../types.js';
import { CoderAgentEvent, type StateChange } from '../types.js';
export async function pushTaskStateFailed(
error: unknown,
@@ -18,9 +18,10 @@ import {
HookSystem,
PolicyDecision,
tmpdir,
type Config,
type Storage,
} from '@google/gemini-cli-core';
import { createMockMessageBus } from '@google/gemini-cli-core/src/test-utils/mock-message-bus.js';
import type { Config, Storage } from '@google/gemini-cli-core';
import { expect, vi } from 'vitest';
export function createMockConfig(
+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",
+39 -1
View File
@@ -4,7 +4,15 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { vi, describe, it, expect, beforeEach, type Mock } from 'vitest';
import {
vi,
describe,
it,
expect,
beforeEach,
afterEach,
type Mock,
} from 'vitest';
import { listMcpServers } from './list.js';
import { loadSettings, mergeSettings } from '../../config/settings.js';
import { createTransport, debugLogger } from '@google/gemini-cli-core';
@@ -106,6 +114,10 @@ describe('mcp list command', () => {
mockedGetUserExtensionsDir.mockReturnValue('/mocked/extensions/dir');
});
afterEach(() => {
vi.restoreAllMocks();
});
it('should display message when no servers configured', async () => {
const defaultMergedSettings = mergeSettings({}, {}, {}, {}, true);
mockedLoadSettings.mockReturnValue({
@@ -133,6 +145,7 @@ describe('mcp list command', () => {
},
},
},
isTrusted: true,
});
mockClient.connect.mockResolvedValue(undefined);
@@ -199,6 +212,7 @@ describe('mcp list command', () => {
'config-server': { command: '/config/server' },
},
},
isTrusted: true,
});
mockExtensionManager.loadExtensions.mockReturnValue([
@@ -266,4 +280,28 @@ describe('mcp list command', () => {
expect.anything(),
);
});
it('should show stdio servers as disconnected in untrusted folders', async () => {
const defaultMergedSettings = mergeSettings({}, {}, {}, {}, true);
mockedLoadSettings.mockReturnValue({
merged: {
...defaultMergedSettings,
mcpServers: {
'test-server': { command: '/test/server' },
},
},
isTrusted: false,
});
// createTransport will throw in core if not trusted
mockedCreateTransport.mockRejectedValue(new Error('Folder not trusted'));
await listMcpServers();
expect(debugLogger.log).toHaveBeenCalledWith(
expect.stringContaining(
'test-server: /test/server (stdio) - Disconnected',
),
);
});
});
+45 -20
View File
@@ -20,11 +20,7 @@ import { ExtensionManager } from '../../config/extension-manager.js';
import { requestConsentNonInteractive } from '../../config/extensions/consent.js';
import { promptForSetting } from '../../config/extensions/extensionSettings.js';
import { exitCli } from '../utils.js';
const COLOR_GREEN = '\u001b[32m';
const COLOR_YELLOW = '\u001b[33m';
const COLOR_RED = '\u001b[31m';
const RESET_COLOR = '\u001b[0m';
import chalk from 'chalk';
export async function getMcpServersFromConfig(
settings?: MergedSettings,
@@ -66,27 +62,56 @@ async function testMCPConnection(
serverName: string,
config: MCPServerConfig,
): Promise<MCPServerStatus> {
const settings = loadSettings();
// SECURITY: Only test connection if workspace is trusted or if it's a remote server.
// stdio servers execute local commands and must never run in untrusted workspaces.
const isStdio = !!config.command;
if (isStdio && !settings.isTrusted) {
return MCPServerStatus.DISCONNECTED;
}
const client = new Client({
name: 'mcp-test-client',
version: '0.0.1',
});
const settings = loadSettings();
const sanitizationConfig = {
enableEnvironmentVariableRedaction: true,
allowedEnvironmentVariables: [],
blockedEnvironmentVariables: settings.merged.advanced.excludedEnvVars,
const mcpContext = {
sanitizationConfig: {
enableEnvironmentVariableRedaction: true,
allowedEnvironmentVariables: [],
blockedEnvironmentVariables: settings.merged.advanced.excludedEnvVars,
},
emitMcpDiagnostic: (
severity: 'info' | 'warning' | 'error',
message: string,
error?: unknown,
serverName?: string,
) => {
// In non-interactive list, we log everything through debugLogger for consistency
if (severity === 'error') {
debugLogger.error(
chalk.red(`Error${serverName ? ` (${serverName})` : ''}: ${message}`),
error,
);
} else if (severity === 'warning') {
debugLogger.warn(
chalk.yellow(
`Warning${serverName ? ` (${serverName})` : ''}: ${message}`,
),
error,
);
} else {
debugLogger.log(message, error);
}
},
isTrustedFolder: () => settings.isTrusted,
};
let transport;
try {
// Use the same transport creation logic as core
transport = await createTransport(
serverName,
config,
false,
sanitizationConfig,
);
transport = await createTransport(serverName, config, false, mcpContext);
} catch (_error) {
await client.close();
return MCPServerStatus.DISCONNECTED;
@@ -125,7 +150,7 @@ export async function listMcpServers(settings?: MergedSettings): Promise<void> {
blockedServerNames,
undefined,
);
debugLogger.log(COLOR_YELLOW + message + RESET_COLOR + '\n');
debugLogger.log(chalk.yellow(message + '\n'));
}
if (serverNames.length === 0) {
@@ -146,16 +171,16 @@ export async function listMcpServers(settings?: MergedSettings): Promise<void> {
let statusText = '';
switch (status) {
case MCPServerStatus.CONNECTED:
statusIndicator = COLOR_GREEN + '✓' + RESET_COLOR;
statusIndicator = chalk.green('✓');
statusText = 'Connected';
break;
case MCPServerStatus.CONNECTING:
statusIndicator = COLOR_YELLOW + '…' + RESET_COLOR;
statusIndicator = chalk.yellow('…');
statusText = 'Connecting';
break;
case MCPServerStatus.DISCONNECTED:
default:
statusIndicator = COLOR_RED + '✗' + RESET_COLOR;
statusIndicator = chalk.red('✗');
statusText = 'Disconnected';
break;
}
+99
View File
@@ -19,6 +19,8 @@ import {
debugLogger,
ApprovalMode,
type MCPServerConfig,
type GeminiCLIExtension,
Storage,
} from '@google/gemini-cli-core';
import { loadCliConfig, parseArguments, type CliArgs } from './config.js';
import {
@@ -3524,4 +3526,101 @@ describe('loadCliConfig mcpEnabled', () => {
expect(config.getAllowedMcpServers()).toEqual(['serverA']);
expect(config.getBlockedMcpServers()).toEqual(['serverB']);
});
describe('extension plan settings', () => {
beforeEach(() => {
vi.spyOn(Storage.prototype, 'getProjectTempDir').mockReturnValue(
'/mock/home/user/.gemini/tmp/test-project',
);
});
it('should use plan directory from active extension when user has not specified one', async () => {
process.argv = ['node', 'script.js'];
const settings = createTestMergedSettings({
experimental: { plan: true },
});
const argv = await parseArguments(settings);
vi.spyOn(ExtensionManager.prototype, 'getExtensions').mockReturnValue([
{
name: 'ext-plan',
isActive: true,
plan: { directory: 'ext-plans-dir' },
} as unknown as GeminiCLIExtension,
]);
const config = await loadCliConfig(settings, 'test-session', argv);
expect(config.storage.getPlansDir()).toContain('ext-plans-dir');
});
it('should NOT use plan directory from active extension when user has specified one', async () => {
process.argv = ['node', 'script.js'];
const settings = createTestMergedSettings({
experimental: { plan: true },
general: {
plan: { directory: 'user-plans-dir' },
},
});
const argv = await parseArguments(settings);
vi.spyOn(ExtensionManager.prototype, 'getExtensions').mockReturnValue([
{
name: 'ext-plan',
isActive: true,
plan: { directory: 'ext-plans-dir' },
} as unknown as GeminiCLIExtension,
]);
const config = await loadCliConfig(settings, 'test-session', argv);
expect(config.storage.getPlansDir()).toContain('user-plans-dir');
expect(config.storage.getPlansDir()).not.toContain('ext-plans-dir');
});
it('should NOT use plan directory from inactive extension', async () => {
process.argv = ['node', 'script.js'];
const settings = createTestMergedSettings({
experimental: { plan: true },
});
const argv = await parseArguments(settings);
vi.spyOn(ExtensionManager.prototype, 'getExtensions').mockReturnValue([
{
name: 'ext-plan',
isActive: false,
plan: { directory: 'ext-plans-dir-inactive' },
} as unknown as GeminiCLIExtension,
]);
const config = await loadCliConfig(settings, 'test-session', argv);
expect(config.storage.getPlansDir()).not.toContain(
'ext-plans-dir-inactive',
);
});
it('should use default path if neither user nor extension settings provide a plan directory', async () => {
process.argv = ['node', 'script.js'];
const settings = createTestMergedSettings({
experimental: { plan: true },
});
const argv = await parseArguments(settings);
// No extensions providing plan directory
vi.spyOn(ExtensionManager.prototype, 'getExtensions').mockReturnValue([]);
const config = await loadCliConfig(settings, 'test-session', argv);
// Should return the default managed temp directory path
expect(config.storage.getPlansDir()).toBe(
path.join(
'/mock',
'home',
'user',
'.gemini',
'tmp',
'test-project',
'test-session',
'plans',
),
);
});
});
});
+7 -1
View File
@@ -511,6 +511,10 @@ export async function loadCliConfig(
});
await extensionManager.loadExtensions();
const extensionPlanSettings = extensionManager
.getExtensions()
.find((ext) => ext.isActive && ext.plan?.directory)?.plan;
const experimentalJitContext = settings.experimental?.jitContext ?? false;
let memoryContent: string | HierarchicalMemory = '';
@@ -827,7 +831,9 @@ export async function loadCliConfig(
enableAgents: settings.experimental?.enableAgents,
plan: settings.experimental?.plan,
directWebFetch: settings.experimental?.directWebFetch,
planSettings: settings.general?.plan,
planSettings: settings.general?.plan?.directory
? settings.general.plan
: (extensionPlanSettings ?? settings.general?.plan),
enableEventDrivenScheduler: true,
skillsSupport: settings.skills?.enabled ?? true,
disabledSkills: settings.skills?.disabled,
@@ -886,6 +886,7 @@ Would you like to attempt to install via "git clone" instead?`,
themes: config.themes,
rules,
checkers,
plan: config.plan,
};
} catch (e) {
debugLogger.error(
+9
View File
@@ -33,6 +33,15 @@ export interface ExtensionConfig {
* These themes will be registered when the extension is activated.
*/
themes?: CustomTheme[];
/**
* Planning features configuration contributed by this extension.
*/
plan?: {
/**
* The directory where planning artifacts are stored.
*/
directory?: string;
};
}
export interface ExtensionUpdateInfo {
+3 -1
View File
@@ -102,7 +102,9 @@ export async function loadSandboxConfig(
const packageJson = await getPackageJson(__dirname);
const image =
process.env['GEMINI_SANDBOX_IMAGE'] ?? packageJson?.config?.sandboxImageUri;
process.env['GEMINI_SANDBOX_IMAGE'] ??
process.env['GEMINI_SANDBOX_IMAGE_DEFAULT'] ??
packageJson?.config?.sandboxImageUri;
return command && image ? { command, image } : undefined;
}
-3
View File
@@ -185,9 +185,6 @@ export interface SessionRetentionSettings {
/** Minimum retention period (safety limit, defaults to "1d") */
minRetention?: string;
/** INTERNAL: Whether the user has acknowledged the session retention warning */
warningAcknowledged?: boolean;
}
export interface SettingsError {
@@ -96,6 +96,14 @@ describe('SettingsSchema', () => {
]);
});
it('should have errorVerbosity enum property', () => {
const definition = getSettingsSchema().ui?.properties?.errorVerbosity;
expect(definition).toBeDefined();
expect(definition?.type).toBe('enum');
expect(definition?.default).toBe('low');
expect(definition?.options?.map((o) => o.value)).toEqual(['low', 'full']);
});
it('should have checkpointing nested properties', () => {
expect(
getSettingsSchema().general?.properties?.checkpointing.properties
+53 -14
View File
@@ -117,6 +117,10 @@ export interface SettingDefinition {
* For map-like objects without explicit `properties`, describes the shape of the values.
*/
additionalProperties?: SettingCollectionDefinition;
/**
* Optional unit to display after the value (e.g. '%').
*/
unit?: string;
/**
* Optional reference identifier for generators that emit a `$ref`.
*/
@@ -339,7 +343,7 @@ const SETTINGS_SCHEMA = {
label: 'Enable Session Cleanup',
category: 'General',
requiresRestart: false,
default: false,
default: true as boolean,
description: 'Enable automatic session cleanup',
showInDialog: true,
},
@@ -348,7 +352,7 @@ const SETTINGS_SCHEMA = {
label: 'Keep chat history',
category: 'General',
requiresRestart: false,
default: undefined as string | undefined,
default: '30d' as string,
description:
'Automatically delete chats older than this time period (e.g., "30d", "7d", "24h", "1w")',
showInDialog: true,
@@ -372,16 +376,6 @@ const SETTINGS_SCHEMA = {
description: `Minimum retention period (safety limit, defaults to "${DEFAULT_MIN_RETENTION}")`,
showInDialog: false,
},
warningAcknowledged: {
type: 'boolean',
label: 'Warning Acknowledged',
category: 'General',
requiresRestart: false,
default: false,
showInDialog: false,
description:
'INTERNAL: Whether the user has acknowledged the session retention warning',
},
},
description: 'Settings for automatic session cleanup.',
},
@@ -605,7 +599,7 @@ const SETTINGS_SCHEMA = {
category: 'UI',
requiresRestart: false,
default: true,
description: 'Hides the context window remaining percentage.',
description: 'Hides the context window usage percentage.',
showInDialog: true,
},
},
@@ -719,6 +713,20 @@ const SETTINGS_SCHEMA = {
{ value: 'off', label: 'Off' },
],
},
errorVerbosity: {
type: 'enum',
label: 'Error Verbosity',
category: 'UI',
requiresRestart: false,
default: 'low',
description:
'Controls whether recoverable errors are hidden (low) or fully shown (full).',
showInDialog: true,
options: [
{ value: 'low', label: 'Low' },
{ value: 'full', label: 'Full' },
],
},
customWittyPhrases: {
type: 'array',
label: 'Custom Witty Phrases',
@@ -828,6 +836,36 @@ const SETTINGS_SCHEMA = {
ref: 'TelemetrySettings',
},
billing: {
type: 'object',
label: 'Billing',
category: 'Advanced',
requiresRestart: false,
default: {},
description: 'Billing and AI credits settings.',
showInDialog: false,
properties: {
overageStrategy: {
type: 'enum',
label: 'Overage Strategy',
category: 'Advanced',
requiresRestart: false,
default: 'ask',
description: oneLine`
How to handle quota exhaustion when AI credits are available.
'ask' prompts each time, 'always' automatically uses credits,
'never' disables credit usage.
`,
showInDialog: true,
options: [
{ value: 'ask', label: 'Ask each time' },
{ value: 'always', label: 'Always use credits' },
{ value: 'never', label: 'Never use credits' },
],
},
},
},
model: {
type: 'object',
label: 'Model',
@@ -879,13 +917,14 @@ const SETTINGS_SCHEMA = {
},
compressionThreshold: {
type: 'number',
label: 'Compression Threshold',
label: 'Context Compression Threshold',
category: 'Model',
requiresRestart: true,
default: 0.5 as number,
description:
'The fraction of context usage at which to trigger context compression (e.g. 0.2, 0.3).',
showInDialog: true,
unit: '%',
},
disableLoopDetection: {
type: 'boolean',
+48 -5
View File
@@ -17,7 +17,6 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
await importOriginal<typeof import('@google/gemini-cli-core')>();
return {
...actual,
getErrorMessage: (e: unknown) => (e as Error).message,
};
});
@@ -32,7 +31,7 @@ describe('auth', () => {
it('should return null if authType is undefined', async () => {
const result = await performInitialAuth(mockConfig, undefined);
expect(result).toBeNull();
expect(result).toEqual({ authError: null, accountSuspensionInfo: null });
expect(mockConfig.refreshAuth).not.toHaveBeenCalled();
});
@@ -41,7 +40,7 @@ describe('auth', () => {
mockConfig,
AuthType.LOGIN_WITH_GOOGLE,
);
expect(result).toBeNull();
expect(result).toEqual({ authError: null, accountSuspensionInfo: null });
expect(mockConfig.refreshAuth).toHaveBeenCalledWith(
AuthType.LOGIN_WITH_GOOGLE,
);
@@ -54,7 +53,10 @@ describe('auth', () => {
mockConfig,
AuthType.LOGIN_WITH_GOOGLE,
);
expect(result).toBe('Failed to login. Message: Auth failed');
expect(result).toEqual({
authError: 'Failed to login. Message: Auth failed',
accountSuspensionInfo: null,
});
expect(mockConfig.refreshAuth).toHaveBeenCalledWith(
AuthType.LOGIN_WITH_GOOGLE,
);
@@ -68,7 +70,48 @@ describe('auth', () => {
mockConfig,
AuthType.LOGIN_WITH_GOOGLE,
);
expect(result).toBeNull();
expect(result).toEqual({ authError: null, accountSuspensionInfo: null });
expect(mockConfig.refreshAuth).toHaveBeenCalledWith(
AuthType.LOGIN_WITH_GOOGLE,
);
});
it('should return accountSuspensionInfo for 403 TOS_VIOLATION error', async () => {
vi.mocked(mockConfig.refreshAuth).mockRejectedValue({
response: {
data: {
error: {
code: 403,
message:
'This service has been disabled for violation of Terms of Service.',
details: [
{
'@type': 'type.googleapis.com/google.rpc.ErrorInfo',
reason: 'TOS_VIOLATION',
domain: 'example.googleapis.com',
metadata: {
appeal_url: 'https://example.com/appeal',
appeal_url_link_text: 'Appeal Here',
},
},
],
},
},
},
});
const result = await performInitialAuth(
mockConfig,
AuthType.LOGIN_WITH_GOOGLE,
);
expect(result).toEqual({
authError: null,
accountSuspensionInfo: {
message:
'This service has been disabled for violation of Terms of Service.',
appealUrl: 'https://example.com/appeal',
appealLinkText: 'Appeal Here',
},
});
expect(mockConfig.refreshAuth).toHaveBeenCalledWith(
AuthType.LOGIN_WITH_GOOGLE,
);
+28 -6
View File
@@ -9,20 +9,28 @@ import {
type Config,
getErrorMessage,
ValidationRequiredError,
isAccountSuspendedError,
} from '@google/gemini-cli-core';
import type { AccountSuspensionInfo } from '../ui/contexts/UIStateContext.js';
export interface InitialAuthResult {
authError: string | null;
accountSuspensionInfo: AccountSuspensionInfo | null;
}
/**
* Handles the initial authentication flow.
* @param config The application config.
* @param authType The selected auth type.
* @returns An error message if authentication fails, otherwise null.
* @returns The auth result with error message and account suspension status.
*/
export async function performInitialAuth(
config: Config,
authType: AuthType | undefined,
): Promise<string | null> {
): Promise<InitialAuthResult> {
if (!authType) {
return null;
return { authError: null, accountSuspensionInfo: null };
}
try {
@@ -33,10 +41,24 @@ export async function performInitialAuth(
if (e instanceof ValidationRequiredError) {
// Don't treat validation required as a fatal auth error during startup.
// This allows the React UI to load and show the ValidationDialog.
return null;
return { authError: null, accountSuspensionInfo: null };
}
return `Failed to login. Message: ${getErrorMessage(e)}`;
const suspendedError = isAccountSuspendedError(e);
if (suspendedError) {
return {
authError: null,
accountSuspensionInfo: {
message: suspendedError.message,
appealUrl: suspendedError.appealUrl,
appealLinkText: suspendedError.appealLinkText,
},
};
}
return {
authError: `Failed to login. Message: ${getErrorMessage(e)}`,
accountSuspensionInfo: null,
};
}
return null;
return { authError: null, accountSuspensionInfo: null };
}
+10 -2
View File
@@ -72,7 +72,10 @@ describe('initializer', () => {
vi.mocked(IdeClient.getInstance).mockResolvedValue(
mockIdeClient as unknown as IdeClient,
);
vi.mocked(performInitialAuth).mockResolvedValue(null);
vi.mocked(performInitialAuth).mockResolvedValue({
authError: null,
accountSuspensionInfo: null,
});
vi.mocked(validateTheme).mockReturnValue(null);
});
@@ -84,6 +87,7 @@ describe('initializer', () => {
expect(result).toEqual({
authError: null,
accountSuspensionInfo: null,
themeError: null,
shouldOpenAuthDialog: false,
geminiMdFileCount: 5,
@@ -103,6 +107,7 @@ describe('initializer', () => {
expect(result).toEqual({
authError: null,
accountSuspensionInfo: null,
themeError: null,
shouldOpenAuthDialog: false,
geminiMdFileCount: 5,
@@ -116,7 +121,10 @@ describe('initializer', () => {
});
it('should handle auth error', async () => {
vi.mocked(performInitialAuth).mockResolvedValue('Auth failed');
vi.mocked(performInitialAuth).mockResolvedValue({
authError: 'Auth failed',
accountSuspensionInfo: null,
});
const result = await initializeApp(
mockConfig as unknown as Config,
mockSettings,
+4 -1
View File
@@ -17,9 +17,11 @@ import {
import { type LoadedSettings } from '../config/settings.js';
import { performInitialAuth } from './auth.js';
import { validateTheme } from './theme.js';
import type { AccountSuspensionInfo } from '../ui/contexts/UIStateContext.js';
export interface InitializationResult {
authError: string | null;
accountSuspensionInfo: AccountSuspensionInfo | null;
themeError: string | null;
shouldOpenAuthDialog: boolean;
geminiMdFileCount: number;
@@ -37,7 +39,7 @@ export async function initializeApp(
settings: LoadedSettings,
): Promise<InitializationResult> {
const authHandle = startupProfiler.start('authenticate');
const authError = await performInitialAuth(
const { authError, accountSuspensionInfo } = await performInitialAuth(
config,
settings.merged.security.auth.selectedType,
);
@@ -60,6 +62,7 @@ export async function initializeApp(
return {
authError,
accountSuspensionInfo,
themeError,
shouldOpenAuthDialog,
geminiMdFileCount: config.getGeminiMdFileCount(),
+1
View File
@@ -1202,6 +1202,7 @@ describe('startInteractiveUI', () => {
const mockWorkspaceRoot = '/root';
const mockInitializationResult = {
authError: null,
accountSuspensionInfo: null,
themeError: null,
shouldOpenAuthDialog: false,
geminiMdFileCount: 0,
+1 -1
View File
@@ -243,7 +243,7 @@ export async function startInteractiveUI(
<ScrollProvider>
<OverflowProvider>
<SessionStatsProvider>
<VimModeProvider settings={settings}>
<VimModeProvider>
<AppContainer
config={config}
startupWarnings={startupWarnings}
+3
View File
@@ -104,6 +104,8 @@ vi.mock('../ui/auth/useAuth.js', () => ({
onAuthError: vi.fn(),
apiKeyDefaultValue: 'test-api-key',
reloadApiKey: vi.fn().mockResolvedValue('test-api-key'),
accountSuspensionInfo: null,
setAccountSuspensionInfo: vi.fn(),
}),
validateAuthMethodWithSettings: () => null,
}));
@@ -387,6 +389,7 @@ export class AppRig {
version="test-version"
initializationResult={{
authError: null,
accountSuspensionInfo: null,
themeError: null,
shouldOpenAuthDialog: false,
geminiMdFileCount: 0,
@@ -141,7 +141,10 @@ export const createMockConfig = (overrides: Partial<Config> = {}): Config =>
getMcpClientManager: vi.fn().mockReturnValue({
getMcpInstructions: vi.fn().mockReturnValue(''),
getMcpServers: vi.fn().mockReturnValue({}),
getLastError: vi.fn().mockReturnValue(undefined),
}),
setUserInteractedWithMcp: vi.fn(),
emitMcpDiagnostic: vi.fn(),
getEnableEventDrivenScheduler: vi.fn().mockReturnValue(false),
getAdminSkillsEnabled: vi.fn().mockReturnValue(false),
getDisabledSkills: vi.fn().mockReturnValue([]),
+9 -2
View File
@@ -528,12 +528,13 @@ export const mockSettings = new LoadedSettings(
// A minimal mock UIState to satisfy the context provider.
// Tests that need specific UIState values should provide their own.
const baseMockUiState = {
history: [],
renderMarkdown: true,
streamingState: StreamingState.Idle,
terminalWidth: 100,
terminalHeight: 40,
currentModel: 'gemini-pro',
terminalBackgroundColor: 'black',
terminalBackgroundColor: 'black' as const,
cleanUiDetailsVisible: false,
allowPlanMode: true,
activePtyId: undefined,
@@ -552,6 +553,9 @@ const baseMockUiState = {
warningText: '',
},
bannerVisible: false,
nightly: false,
updateInfo: null,
pendingHistoryItems: [],
};
export const mockAppState: AppState = {
@@ -591,6 +595,8 @@ const mockUIActions: UIActions = {
handleClearScreen: vi.fn(),
handleProQuotaChoice: vi.fn(),
handleValidationChoice: vi.fn(),
handleOverageMenuChoice: vi.fn(),
handleEmptyWalletChoice: vi.fn(),
setQueueErrorMessage: vi.fn(),
popAllMessages: vi.fn(),
handleApiKeySubmit: vi.fn(),
@@ -613,6 +619,7 @@ const mockUIActions: UIActions = {
handleRestart: vi.fn(),
handleNewAgentsSelect: vi.fn(),
getPreferredEditor: vi.fn(),
clearAccountSuspension: vi.fn(),
};
let capturedOverflowState: OverflowState | undefined;
@@ -749,7 +756,7 @@ export const renderWithProviders = (
<ConfigContext.Provider value={finalConfig}>
<SettingsContext.Provider value={finalSettings}>
<UIStateContext.Provider value={finalUiState}>
<VimModeProvider settings={finalSettings}>
<VimModeProvider>
<ShellFocusContext.Provider value={shellFocus}>
<SessionStatsProvider>
<StreamingContext.Provider
+130
View File
@@ -2544,6 +2544,136 @@ describe('AppContainer State Management', () => {
});
});
describe('Expansion Persistence', () => {
let rerender: () => void;
let unmount: () => void;
let stdin: ReturnType<typeof render>['stdin'];
const setupExpansionPersistenceTest = async (
HighPriorityChild?: React.FC,
) => {
const getTree = () => (
<SettingsContext.Provider value={mockSettings}>
<KeypressProvider config={mockConfig}>
<OverflowProvider>
<AppContainer
config={mockConfig}
version="1.0.0"
initializationResult={mockInitResult}
/>
{HighPriorityChild && <HighPriorityChild />}
</OverflowProvider>
</KeypressProvider>
</SettingsContext.Provider>
);
const renderResult = render(getTree());
stdin = renderResult.stdin;
await act(async () => {
vi.advanceTimersByTime(100);
});
rerender = () => renderResult.rerender(getTree());
unmount = () => renderResult.unmount();
};
const writeStdin = async (sequence: string) => {
await act(async () => {
stdin.write(sequence);
// Advance timers to allow escape sequence parsing and broadcasting
vi.advanceTimersByTime(100);
});
rerender();
};
beforeEach(() => {
vi.useFakeTimers();
});
afterEach(() => {
vi.useRealTimers();
vi.restoreAllMocks();
});
it('should reset expansion when a key is NOT handled by anyone', async () => {
await setupExpansionPersistenceTest();
// Expand first
act(() => capturedUIActions.setConstrainHeight(false));
rerender();
expect(capturedUIState.constrainHeight).toBe(false);
// Press a random key that no one handles (hits Low priority fallback)
await writeStdin('x');
// Should be reset to true (collapsed)
expect(capturedUIState.constrainHeight).toBe(true);
unmount();
});
it('should toggle expansion when Ctrl+O is pressed', async () => {
await setupExpansionPersistenceTest();
// Initial state is collapsed
expect(capturedUIState.constrainHeight).toBe(true);
// Press Ctrl+O to expand (Ctrl+O is sequence \x0f)
await writeStdin('\x0f');
expect(capturedUIState.constrainHeight).toBe(false);
// Press Ctrl+O again to collapse
await writeStdin('\x0f');
expect(capturedUIState.constrainHeight).toBe(true);
unmount();
});
it('should NOT collapse when a high-priority component handles the key (e.g., up/down arrows)', async () => {
const NavigationHandler = () => {
// use real useKeypress
useKeypress(
(key: Key) => {
if (key.name === 'up' || key.name === 'down') {
return true; // Handle navigation
}
return false;
},
{ isActive: true, priority: true }, // High priority
);
return null;
};
await setupExpansionPersistenceTest(NavigationHandler);
// Expand first
act(() => capturedUIActions.setConstrainHeight(false));
rerender();
expect(capturedUIState.constrainHeight).toBe(false);
// 1. Simulate Up arrow (handled by high priority child)
// CSI A is Up arrow
await writeStdin('\u001b[A');
// Should STILL be expanded
expect(capturedUIState.constrainHeight).toBe(false);
// 2. Simulate Down arrow (handled by high priority child)
// CSI B is Down arrow
await writeStdin('\u001b[B');
// Should STILL be expanded
expect(capturedUIState.constrainHeight).toBe(false);
// 3. Sanity check: press an unhandled key
await writeStdin('x');
// Should finally collapse
expect(capturedUIState.constrainHeight).toBe(true);
unmount();
});
});
describe('Shortcuts Help Visibility', () => {
let handleGlobalKeypress: (key: Key) => boolean;
let mockedUseKeypress: Mock;
+58 -32
View File
@@ -47,6 +47,7 @@ import {
type IdeInfo,
type IdeContext,
type UserTierId,
type GeminiUserTier,
type UserFeedbackPayload,
type AgentDefinition,
type ApprovalMode,
@@ -82,6 +83,8 @@ import {
CoreToolCallStatus,
generateSteeringAckMessage,
buildUserSteeringHintPrompt,
logBillingEvent,
ApiKeyUpdatedEvent,
} from '@google/gemini-cli-core';
import { validateAuthMethod } from '../config/auth.js';
import process from 'node:process';
@@ -143,7 +146,6 @@ import { requestConsentInteractive } from '../config/extensions/consent.js';
import { useSessionBrowser } from './hooks/useSessionBrowser.js';
import { useSessionResume } from './hooks/useSessionResume.js';
import { useIncludeDirsTrust } from './hooks/useIncludeDirsTrust.js';
import { useSessionRetentionCheck } from './hooks/useSessionRetentionCheck.js';
import { isWorkspaceTrusted } from '../config/trustedFolders.js';
import { useSettings } from './contexts/SettingsContext.js';
import { terminalCapabilityManager } from './utils/terminalCapabilityManager.js';
@@ -391,6 +393,9 @@ export const AppContainer = (props: AppContainerProps) => {
? { remaining, limit, resetTime }
: undefined;
});
const [paidTier, setPaidTier] = useState<GeminiUserTier | undefined>(
undefined,
);
const [isConfigInitialized, setConfigInitialized] = useState(false);
@@ -669,7 +674,14 @@ export const AppContainer = (props: AppContainerProps) => {
onAuthError,
apiKeyDefaultValue,
reloadApiKey,
} = useAuthCommand(settings, config, initializationResult.authError);
accountSuspensionInfo,
setAccountSuspensionInfo,
} = useAuthCommand(
settings,
config,
initializationResult.authError,
initializationResult.accountSuspensionInfo,
);
const [authContext, setAuthContext] = useState<{ requiresRestart?: boolean }>(
{},
);
@@ -686,12 +698,20 @@ export const AppContainer = (props: AppContainerProps) => {
handleProQuotaChoice,
validationRequest,
handleValidationChoice,
// G1 AI Credits
overageMenuRequest,
handleOverageMenuChoice,
emptyWalletRequest,
handleEmptyWalletChoice,
} = useQuotaAndFallback({
config,
historyManager,
userTier,
paidTier,
settings,
setModelSwitchedFromQuotaError,
onShowAuthSelection: () => setAuthState(AuthState.Updating),
errorVerbosity: settings.merged.ui.errorVerbosity,
});
// Derive auth state variables for backward compatibility with UIStateContext
@@ -729,6 +749,8 @@ export const AppContainer = (props: AppContainerProps) => {
const handleAuthSelect = useCallback(
async (authType: AuthType | undefined, scope: LoadableSettingScope) => {
if (authType) {
const previousAuthType =
config.getContentGeneratorConfig()?.authType ?? 'unknown';
if (authType === AuthType.LOGIN_WITH_GOOGLE) {
setAuthContext({ requiresRestart: true });
} else {
@@ -741,6 +763,10 @@ export const AppContainer = (props: AppContainerProps) => {
config.setRemoteAdminSettings(undefined);
await config.refreshAuth(authType);
setAuthState(AuthState.Authenticated);
logBillingEvent(
config,
new ApiKeyUpdatedEvent(previousAuthType, authType),
);
} catch (e) {
if (e instanceof ChangeAuthRequestedError) {
return;
@@ -803,6 +829,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
// Only sync when not currently authenticating
if (authState === AuthState.Authenticated) {
setUserTier(config.getUserTier());
setPaidTier(config.getUserPaidTier());
}
}, [config, authState]);
@@ -1520,28 +1547,6 @@ Logging in with Google... Restarting Gemini CLI to continue.
useIncludeDirsTrust(config, isTrustedFolder, historyManager, setCustomDialog);
const handleAutoEnableRetention = useCallback(() => {
const userSettings = settings.forScope(SettingScope.User).settings;
const currentRetention = userSettings.general?.sessionRetention ?? {};
settings.setValue(SettingScope.User, 'general.sessionRetention', {
...currentRetention,
enabled: true,
maxAge: '30d',
warningAcknowledged: true,
});
}, [settings]);
const {
shouldShowWarning: shouldShowRetentionWarning,
checkComplete: retentionCheckComplete,
sessionsToDeleteCount,
} = useSessionRetentionCheck(
config,
settings.merged,
handleAutoEnableRetention,
);
const tabFocusTimeoutRef = useRef<NodeJS.Timeout | null>(null);
useEffect(() => {
@@ -1661,6 +1666,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
retryStatus,
loadingPhrasesMode: settings.merged.ui.loadingPhrases,
customWittyPhrases: settings.merged.ui.customWittyPhrases,
errorVerbosity: settings.merged.ui.errorVerbosity,
});
const handleGlobalKeypress = useCallback(
@@ -1867,7 +1873,10 @@ Logging in with Google... Restarting Gemini CLI to continue.
],
);
useKeypress(handleGlobalKeypress, { isActive: true, priority: true });
useKeypress(handleGlobalKeypress, {
isActive: true,
priority: KeypressPriority.Low,
});
useKeypress(
() => {
@@ -1983,7 +1992,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
const nightly = props.version.includes('nightly');
const dialogsVisible =
(shouldShowRetentionWarning && retentionCheckComplete) ||
shouldShowIdePrompt ||
shouldShowIdePrompt ||
isFolderTrustDialogOpen ||
isPolicyUpdateDialogOpen ||
@@ -2006,6 +2015,8 @@ Logging in with Google... Restarting Gemini CLI to continue.
showIdeRestartPrompt ||
!!proQuotaRequest ||
!!validationRequest ||
!!overageMenuRequest ||
!!emptyWalletRequest ||
isSessionBrowserOpen ||
authState === AuthState.AwaitingApiKeyInput ||
!!newAgents;
@@ -2033,6 +2044,8 @@ Logging in with Google... Restarting Gemini CLI to continue.
hasLoopDetectionConfirmationRequest ||
!!proQuotaRequest ||
!!validationRequest ||
!!overageMenuRequest ||
!!emptyWalletRequest ||
!!customDialog;
const allowPlanMode =
@@ -2166,13 +2179,12 @@ Logging in with Google... Restarting Gemini CLI to continue.
history: historyManager.history,
historyManager,
isThemeDialogOpen,
shouldShowRetentionWarning:
shouldShowRetentionWarning && retentionCheckComplete,
sessionsToDeleteCount: sessionsToDeleteCount ?? 0,
themeError,
isAuthenticating,
isConfigInitialized,
authError,
accountSuspensionInfo,
isAuthDialogOpen,
isAwaitingApiKeyInput: authState === AuthState.AwaitingApiKeyInput,
apiKeyDefaultValue,
@@ -2243,6 +2255,9 @@ Logging in with Google... Restarting Gemini CLI to continue.
stats: quotaStats,
proQuotaRequest,
validationRequest,
// G1 AI Credits dialog state
overageMenuRequest,
emptyWalletRequest,
},
contextFileNames,
errorCount,
@@ -2294,13 +2309,12 @@ Logging in with Google... Restarting Gemini CLI to continue.
}),
[
isThemeDialogOpen,
shouldShowRetentionWarning,
retentionCheckComplete,
sessionsToDeleteCount,
themeError,
isAuthenticating,
isConfigInitialized,
authError,
accountSuspensionInfo,
isAuthDialogOpen,
editorError,
isEditorDialogOpen,
@@ -2367,6 +2381,8 @@ Logging in with Google... Restarting Gemini CLI to continue.
quotaStats,
proQuotaRequest,
validationRequest,
overageMenuRequest,
emptyWalletRequest,
contextFileNames,
errorCount,
availableTerminalHeight,
@@ -2448,6 +2464,9 @@ Logging in with Google... Restarting Gemini CLI to continue.
handleClearScreen,
handleProQuotaChoice,
handleValidationChoice,
// G1 AI Credits handlers
handleOverageMenuChoice,
handleEmptyWalletChoice,
openSessionBrowser,
closeSessionBrowser,
handleResumeSession,
@@ -2505,6 +2524,10 @@ Logging in with Google... Restarting Gemini CLI to continue.
setNewAgents(null);
},
getPreferredEditor,
clearAccountSuspension: () => {
setAccountSuspensionInfo(null);
setAuthState(AuthState.Updating);
},
}),
[
handleThemeSelect,
@@ -2534,6 +2557,8 @@ Logging in with Google... Restarting Gemini CLI to continue.
handleClearScreen,
handleProQuotaChoice,
handleValidationChoice,
handleOverageMenuChoice,
handleEmptyWalletChoice,
openSessionBrowser,
closeSessionBrowser,
handleResumeSession,
@@ -2553,6 +2578,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
setActiveBackgroundShellPid,
setIsBackgroundShellListOpen,
setAuthContext,
setAccountSuspensionInfo,
newAgents,
config,
historyManager,
@@ -2,20 +2,20 @@
exports[`App > Snapshots > renders default layout correctly 1`] = `
"
███ █████████
░░░███ ███░░░░░███
░░░███ ███ ░░░
░░░███░███
███░ ░███ █████
███░ ░░███ ░░███
███░ ░░█████████
░░░ ░░░░░░░░░
▝▜▄ Gemini CLI v1.2.3
▝▜▄
▗▟▀
▝▀
Tips for getting started:
1. Ask questions, edit files, or run commands.
2. Be specific for the best results.
3. Create GEMINI.md files to customize your interactions with Gemini.
4. /help for more information.
1. Create GEMINI.md files to customize your interactions
2. /help for more information
3. Ask coding questions, edit code or run commands
4. Be specific for the best results
@@ -47,34 +47,31 @@ exports[`App > Snapshots > renders screen reader layout correctly 1`] = `
"Notifications
Footer
███ █████████
░░░███ ███░░░░░███
░░░███ ███ ░░░
░░░███░███
███░ ░███ █████
███░ ░░███ ░░███
███░ ░░█████████
░░░ ░░░░░░░░░
▝▜▄ Gemini CLI v1.2.3
▝▜▄
▗▟▀
▝▀
Tips for getting started:
1. Ask questions, edit files, or run commands.
2. Be specific for the best results.
3. Create GEMINI.md files to customize your interactions with Gemini.
4. /help for more information.
1. Create GEMINI.md files to customize your interactions
2. /help for more information
3. Ask coding questions, edit code or run commands
4. Be specific for the best results
Composer
"
`;
exports[`App > Snapshots > renders with dialogs visible 1`] = `
"
███ █████████
░░░███ ███░░░░░███
░░░███ ███ ░░░
░░░███░███
███░ ░███ █████
███░ ░░███ ░░███
███░ ░░█████████
░░░ ░░░░░░░░░
▝▜▄ Gemini CLI v1.2.3
▝▜▄
▗▟▀
▝▀
@@ -110,20 +107,17 @@ DialogManager
exports[`App > should render ToolConfirmationQueue along with Composer when tool is confirming and experiment is on 1`] = `
"
███ █████████
░░░███ ███░░░░░███
░░░███ ███ ░░░
░░░███░███
███░ ░███ █████
███░ ░░███ ░░███
███░ ░░█████████
░░░ ░░░░░░░░░
▝▜▄ Gemini CLI v1.2.3
▝▜▄
▗▟▀
▝▀
Tips for getting started:
1. Ask questions, edit files, or run commands.
2. Be specific for the best results.
3. Create GEMINI.md files to customize your interactions with Gemini.
4. /help for more information.
1. Create GEMINI.md files to customize your interactions
2. /help for more information
3. Ask coding questions, edit code or run commands
4. Be specific for the best results
HistoryItemDisplay
╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Action Required │
@@ -146,6 +140,9 @@ HistoryItemDisplay
Notifications
Composer
"
@@ -0,0 +1,241 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
import { renderWithProviders } from '../../test-utils/render.js';
import { waitFor } from '../../test-utils/async.js';
import { BannedAccountDialog } from './BannedAccountDialog.js';
import { RadioButtonSelect } from '../components/shared/RadioButtonSelect.js';
import { useKeypress } from '../hooks/useKeypress.js';
import {
openBrowserSecurely,
shouldLaunchBrowser,
} from '@google/gemini-cli-core';
import { Text } from 'ink';
import { runExitCleanup } from '../../utils/cleanup.js';
import type { AccountSuspensionInfo } from '../contexts/UIStateContext.js';
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
const actual =
await importOriginal<typeof import('@google/gemini-cli-core')>();
return {
...actual,
openBrowserSecurely: vi.fn(),
shouldLaunchBrowser: vi.fn().mockReturnValue(true),
};
});
vi.mock('../../utils/cleanup.js', () => ({
runExitCleanup: vi.fn().mockResolvedValue(undefined),
}));
vi.mock('../hooks/useKeypress.js', () => ({
useKeypress: vi.fn(),
}));
vi.mock('../components/shared/RadioButtonSelect.js', () => ({
RadioButtonSelect: vi.fn(({ items }) => (
<>
{items.map((item: { value: string; label: string }) => (
<Text key={item.value}>{item.label}</Text>
))}
</>
)),
}));
const mockedRadioButtonSelect = RadioButtonSelect as Mock;
const mockedUseKeypress = useKeypress as Mock;
const mockedOpenBrowser = openBrowserSecurely as Mock;
const mockedShouldLaunchBrowser = shouldLaunchBrowser as Mock;
const mockedRunExitCleanup = runExitCleanup as Mock;
const DEFAULT_SUSPENSION_INFO: AccountSuspensionInfo = {
message:
'This service has been disabled in this account for violation of Terms of Service. Please submit an appeal to continue using this product.',
appealUrl: 'https://example.com/appeal',
appealLinkText: 'Appeal Here',
};
describe('BannedAccountDialog', () => {
let onExit: Mock;
let onChangeAuth: Mock;
beforeEach(() => {
vi.resetAllMocks();
mockedShouldLaunchBrowser.mockReturnValue(true);
mockedOpenBrowser.mockResolvedValue(undefined);
mockedRunExitCleanup.mockResolvedValue(undefined);
onExit = vi.fn();
onChangeAuth = vi.fn();
});
it('renders the suspension message from accountSuspensionInfo', async () => {
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<BannedAccountDialog
accountSuspensionInfo={DEFAULT_SUSPENSION_INFO}
onExit={onExit}
onChangeAuth={onChangeAuth}
/>,
);
await waitUntilReady();
const frame = lastFrame();
expect(frame).toContain('Account Suspended');
expect(frame).toContain('violation of Terms of Service');
expect(frame).toContain('Escape to exit');
unmount();
});
it('renders menu options with appeal link text from response', async () => {
const { waitUntilReady, unmount } = renderWithProviders(
<BannedAccountDialog
accountSuspensionInfo={DEFAULT_SUSPENSION_INFO}
onExit={onExit}
onChangeAuth={onChangeAuth}
/>,
);
await waitUntilReady();
const items = mockedRadioButtonSelect.mock.calls[0][0].items;
expect(items).toHaveLength(3);
expect(items[0].label).toBe('Appeal Here');
expect(items[1].label).toBe('Change authentication');
expect(items[2].label).toBe('Exit');
unmount();
});
it('hides form option when no appealUrl is provided', async () => {
const infoWithoutUrl: AccountSuspensionInfo = {
message: 'Account suspended.',
};
const { waitUntilReady, unmount } = renderWithProviders(
<BannedAccountDialog
accountSuspensionInfo={infoWithoutUrl}
onExit={onExit}
onChangeAuth={onChangeAuth}
/>,
);
await waitUntilReady();
const items = mockedRadioButtonSelect.mock.calls[0][0].items;
expect(items).toHaveLength(2);
expect(items[0].label).toBe('Change authentication');
expect(items[1].label).toBe('Exit');
unmount();
});
it('uses default label when appealLinkText is not provided', async () => {
const infoWithoutLinkText: AccountSuspensionInfo = {
message: 'Account suspended.',
appealUrl: 'https://example.com/appeal',
};
const { waitUntilReady, unmount } = renderWithProviders(
<BannedAccountDialog
accountSuspensionInfo={infoWithoutLinkText}
onExit={onExit}
onChangeAuth={onChangeAuth}
/>,
);
await waitUntilReady();
const items = mockedRadioButtonSelect.mock.calls[0][0].items;
expect(items[0].label).toBe('Open the Google Form');
unmount();
});
it('opens browser when appeal option is selected', async () => {
const { waitUntilReady, unmount } = renderWithProviders(
<BannedAccountDialog
accountSuspensionInfo={DEFAULT_SUSPENSION_INFO}
onExit={onExit}
onChangeAuth={onChangeAuth}
/>,
);
await waitUntilReady();
const { onSelect } = mockedRadioButtonSelect.mock.calls[0][0];
await onSelect('open_form');
expect(mockedOpenBrowser).toHaveBeenCalledWith(
'https://example.com/appeal',
);
expect(onExit).not.toHaveBeenCalled();
unmount();
});
it('shows URL when browser cannot be launched', async () => {
mockedShouldLaunchBrowser.mockReturnValue(false);
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<BannedAccountDialog
accountSuspensionInfo={DEFAULT_SUSPENSION_INFO}
onExit={onExit}
onChangeAuth={onChangeAuth}
/>,
);
await waitUntilReady();
const { onSelect } = mockedRadioButtonSelect.mock.calls[0][0];
onSelect('open_form');
await waitFor(() => {
expect(lastFrame()).toContain('Please open this URL in a browser');
});
expect(mockedOpenBrowser).not.toHaveBeenCalled();
unmount();
});
it('calls onExit when "Exit" is selected', async () => {
const { waitUntilReady, unmount } = renderWithProviders(
<BannedAccountDialog
accountSuspensionInfo={DEFAULT_SUSPENSION_INFO}
onExit={onExit}
onChangeAuth={onChangeAuth}
/>,
);
await waitUntilReady();
const { onSelect } = mockedRadioButtonSelect.mock.calls[0][0];
await onSelect('exit');
expect(mockedRunExitCleanup).toHaveBeenCalled();
expect(onExit).toHaveBeenCalled();
unmount();
});
it('calls onChangeAuth when "Change authentication" is selected', async () => {
const { waitUntilReady, unmount } = renderWithProviders(
<BannedAccountDialog
accountSuspensionInfo={DEFAULT_SUSPENSION_INFO}
onExit={onExit}
onChangeAuth={onChangeAuth}
/>,
);
await waitUntilReady();
const { onSelect } = mockedRadioButtonSelect.mock.calls[0][0];
onSelect('change_auth');
expect(onChangeAuth).toHaveBeenCalled();
expect(onExit).not.toHaveBeenCalled();
unmount();
});
it('exits on escape key', async () => {
const { waitUntilReady, unmount } = renderWithProviders(
<BannedAccountDialog
accountSuspensionInfo={DEFAULT_SUSPENSION_INFO}
onExit={onExit}
onChangeAuth={onChangeAuth}
/>,
);
await waitUntilReady();
const keypressHandler = mockedUseKeypress.mock.calls[0][0];
const result = keypressHandler({ name: 'escape' });
expect(result).toBe(true);
unmount();
});
it('renders snapshot correctly', async () => {
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<BannedAccountDialog
accountSuspensionInfo={DEFAULT_SUSPENSION_INFO}
onExit={onExit}
onChangeAuth={onChangeAuth}
/>,
);
await waitUntilReady();
expect(lastFrame()).toMatchSnapshot();
unmount();
});
});
@@ -0,0 +1,138 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import type React from 'react';
import { useCallback, useMemo, useState } from 'react';
import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js';
import { RadioButtonSelect } from '../components/shared/RadioButtonSelect.js';
import { useKeypress } from '../hooks/useKeypress.js';
import {
openBrowserSecurely,
shouldLaunchBrowser,
} from '@google/gemini-cli-core';
import { runExitCleanup } from '../../utils/cleanup.js';
import type { AccountSuspensionInfo } from '../contexts/UIStateContext.js';
interface BannedAccountDialogProps {
accountSuspensionInfo: AccountSuspensionInfo;
onExit: () => void;
onChangeAuth: () => void;
}
export function BannedAccountDialog({
accountSuspensionInfo,
onExit,
onChangeAuth,
}: BannedAccountDialogProps): React.JSX.Element {
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const appealUrl = accountSuspensionInfo.appealUrl;
const appealLinkText =
accountSuspensionInfo.appealLinkText ?? 'Open the Google Form';
const items = useMemo(() => {
const menuItems = [];
if (appealUrl) {
menuItems.push({
label: appealLinkText,
value: 'open_form' as const,
key: 'open_form',
});
}
menuItems.push(
{
label: 'Change authentication',
value: 'change_auth' as const,
key: 'change_auth',
},
{
label: 'Exit',
value: 'exit' as const,
key: 'exit',
},
);
return menuItems;
}, [appealUrl, appealLinkText]);
useKeypress(
(key) => {
if (key.name === 'escape') {
void handleExit();
return true;
}
return false;
},
{ isActive: true },
);
const handleExit = useCallback(async () => {
await runExitCleanup();
onExit();
}, [onExit]);
const handleSelect = useCallback(
async (choice: string) => {
if (choice === 'open_form' && appealUrl) {
if (!shouldLaunchBrowser()) {
setErrorMessage(`Please open this URL in a browser: ${appealUrl}`);
return;
}
try {
await openBrowserSecurely(appealUrl);
} catch {
setErrorMessage(`Failed to open browser. Please visit: ${appealUrl}`);
}
} else if (choice === 'change_auth') {
onChangeAuth();
} else {
await handleExit();
}
},
[handleExit, onChangeAuth, appealUrl],
);
return (
<Box flexDirection="column" padding={1}>
<Text bold color={theme.status.error}>
Error: Account Suspended
</Text>
<Box marginTop={1}>
<Text>{accountSuspensionInfo.message}</Text>
</Box>
{appealUrl && (
<>
<Box marginTop={1}>
<Text>Appeal URL:</Text>
</Box>
<Box>
<Text color={theme.text.link}>[{appealUrl}]</Text>
</Box>
</>
)}
{errorMessage && (
<Box marginTop={1}>
<Text color={theme.status.error}>{errorMessage}</Text>
</Box>
)}
<Box marginTop={1}>
<RadioButtonSelect
items={items}
onSelect={(choice) => void handleSelect(choice)}
/>
</Box>
<Box marginTop={1}>
<Text dimColor>Escape to exit</Text>
</Box>
</Box>
);
}
@@ -0,0 +1,17 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`BannedAccountDialog > renders snapshot correctly 1`] = `
"
Error: Account Suspended
This service has been disabled in this account for violation of Terms of Service. Please submit an
appeal to continue using this product.
Appeal URL:
[https://example.com/appeal]
Appeal HereChange authenticationExit
Escape to exit
"
`;
+18 -1
View File
@@ -11,6 +11,7 @@ import {
type Config,
loadApiKey,
debugLogger,
isAccountSuspendedError,
} from '@google/gemini-cli-core';
import { getErrorMessage } from '@google/gemini-cli-core';
import { AuthState } from '../types.js';
@@ -34,16 +35,21 @@ export function validateAuthMethodWithSettings(
return validateAuthMethod(authType);
}
import type { AccountSuspensionInfo } from '../contexts/UIStateContext.js';
export const useAuthCommand = (
settings: LoadedSettings,
config: Config,
initialAuthError: string | null = null,
initialAccountSuspensionInfo: AccountSuspensionInfo | null = null,
) => {
const [authState, setAuthState] = useState<AuthState>(
initialAuthError ? AuthState.Updating : AuthState.Unauthenticated,
);
const [authError, setAuthError] = useState<string | null>(initialAuthError);
const [accountSuspensionInfo, setAccountSuspensionInfo] =
useState<AccountSuspensionInfo | null>(initialAccountSuspensionInfo);
const [apiKeyDefaultValue, setApiKeyDefaultValue] = useState<
string | undefined
>(undefined);
@@ -130,7 +136,16 @@ export const useAuthCommand = (
setAuthError(null);
setAuthState(AuthState.Authenticated);
} catch (e) {
onAuthError(`Failed to login. Message: ${getErrorMessage(e)}`);
const suspendedError = isAccountSuspendedError(e);
if (suspendedError) {
setAccountSuspensionInfo({
message: suspendedError.message,
appealUrl: suspendedError.appealUrl,
appealLinkText: suspendedError.appealLinkText,
});
} else {
onAuthError(`Failed to login. Message: ${getErrorMessage(e)}`);
}
}
})();
}, [
@@ -150,5 +165,7 @@ export const useAuthCommand = (
onAuthError,
apiKeyDefaultValue,
reloadApiKey,
accountSuspensionInfo,
setAccountSuspensionInfo,
};
};
@@ -21,6 +21,10 @@ import {
ConfigExtensionDialog,
type ConfigExtensionDialogProps,
} from '../components/ConfigExtensionDialog.js';
import {
ExtensionRegistryView,
type ExtensionRegistryViewProps,
} from '../components/views/ExtensionRegistryView.js';
import { type CommandContext, type SlashCommand } from './types.js';
import {
@@ -39,6 +43,8 @@ import {
} from '../../config/extension-manager.js';
import { SettingScope } from '../../config/settings.js';
import { stat } from 'node:fs/promises';
import { type RegistryExtension } from '../../config/extensionRegistryClient.js';
import { waitFor } from '../../test-utils/async.js';
vi.mock('../../config/extension-manager.js', async (importOriginal) => {
const actual =
@@ -167,6 +173,7 @@ describe('extensionsCommand', () => {
},
ui: {
dispatchExtensionStateUpdate: mockDispatchExtensionState,
removeComponent: vi.fn(),
},
});
});
@@ -429,6 +436,61 @@ describe('extensionsCommand', () => {
throw new Error('Explore action not found');
}
it('should return ExtensionRegistryView custom dialog when experimental.extensionRegistry is true', async () => {
mockContext.services.settings.merged.experimental.extensionRegistry = true;
const result = await exploreAction(mockContext, '');
expect(result).toBeDefined();
if (result?.type !== 'custom_dialog') {
throw new Error('Expected custom_dialog');
}
const component =
result.component as ReactElement<ExtensionRegistryViewProps>;
expect(component.type).toBe(ExtensionRegistryView);
expect(component.props.extensionManager).toBe(mockExtensionLoader);
});
it('should handle onSelect and onClose in ExtensionRegistryView', async () => {
mockContext.services.settings.merged.experimental.extensionRegistry = true;
const result = await exploreAction(mockContext, '');
if (result?.type !== 'custom_dialog') {
throw new Error('Expected custom_dialog');
}
const component =
result.component as ReactElement<ExtensionRegistryViewProps>;
const extension = {
extensionName: 'test-ext',
url: 'https://github.com/test/ext.git',
} as RegistryExtension;
vi.mocked(inferInstallMetadata).mockResolvedValue({
source: extension.url,
type: 'git',
});
mockInstallExtension.mockResolvedValue({ name: extension.url });
// Call onSelect
component.props.onSelect?.(extension);
await waitFor(() => {
expect(inferInstallMetadata).toHaveBeenCalledWith(extension.url);
expect(mockInstallExtension).toHaveBeenCalledWith({
source: extension.url,
type: 'git',
});
});
expect(mockContext.ui.removeComponent).toHaveBeenCalledTimes(1);
// Call onClose
component.props.onClose?.();
expect(mockContext.ui.removeComponent).toHaveBeenCalledTimes(2);
});
it("should add an info message and call 'open' in a non-sandbox environment", async () => {
// Ensure no special environment variables that would affect behavior
vi.stubEnv('NODE_ENV', '');
@@ -280,7 +280,9 @@ async function exploreAction(
type: 'custom_dialog' as const,
component: React.createElement(ExtensionRegistryView, {
onSelect: (extension) => {
debugLogger.debug(`Selected extension: ${extension.extensionName}`);
debugLogger.log(`Selected extension: ${extension.extensionName}`);
void installAction(context, extension.url);
context.ui.removeComponent();
},
onClose: () => context.ui.removeComponent(),
extensionManager,

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