Compare commits

..

8 Commits

Author SHA1 Message Date
gemini-cli-robot b05fb545ef chore(release): v0.20.0 2025-12-09 20:18:11 +00:00
gemini-cli-robot d0ce3c4c5d chore(release): v0.20.0-preview.5 2025-12-09 01:52:41 +00:00
gemini-cli-robot 8872ee0ace fix(patch): cherry-pick 171103a to release/v0.20.0-preview.2-pr-14742 to patch version v0.20.0-preview.2 and create version 0.20.0-preview.5 (#14752)
Co-authored-by: Gal Zahavi <38544478+galz10@users.noreply.github.com>
Co-authored-by: Jack Wotherspoon <jackwoth@google.com>
Co-authored-by: christine betts <chrstn@uw.edu>
2025-12-09 01:01:46 +00:00
gemini-cli-robot 9d7b9e6cd1 chore(release): v0.20.0-preview.2 2025-12-08 20:39:26 +00:00
gemini-cli-robot f9997f92c9 fix(patch): cherry-pick 828afe1 to release/v0.20.0-preview.1-pr-14159 to patch version v0.20.0-preview.1 and create version 0.20.0-preview.2 (#14733)
Co-authored-by: Allen Hutchison <adh@google.com>
2025-12-08 19:54:40 +00:00
gemini-cli-robot aae64683ce chore(release): v0.20.0-preview.1 2025-12-05 04:50:06 +00:00
gemini-cli-robot 356eb7ced0 fix(patch): cherry-pick d284fa6 to release/v0.20.0-preview.0-pr-14545 [CONFLICTS] (#14559)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-12-04 20:33:32 -08:00
gemini-cli-robot 2908555435 chore(release): v0.20.0-preview.0 2025-12-02 23:05:16 +00:00
1180 changed files with 34056 additions and 119010 deletions
-29
View File
@@ -1,29 +0,0 @@
description="Injects context of all relevant cli files"
prompt = """
The following output contains the complete
source code of packages/core.
**Pay extremely close attention to these files.** They define the project's
core architecture, component patterns, and testing standards.
The source code contains the content of absolutely every source code file in
packages/core.
You should very rarely need to read any other files from packages/core to resolve
prompts.
!{find packages/core \\( -path packages/cli/dist -o -path packages/core/dist -o -name node_modules \\) -prune -o -type f \\( -name "*.ts" -o -name "*.tsx" \\) ! -name "*.test.ts" ! -name "*.test.tsx" ! -name "*.d.ts" -exec echo "--- {} ---" \\; -exec cat {} \\;}
In addition to the code context, you MUST strictly adhere to the following frontend-specific development guidelines when writing code in packages/core.
## Configuration & Settings
* **Settings vs Args**: Use settings for user-configurable options; do not add new CLI arguments.
* **Schema**: Add new settings to `packages/cli/src/config/settingsSchema.ts`.
* **Documentation**:
* If `showInDialog: true`, document in `docs/get-started/configuration.md`.
* Ensure `requiresRestart` is correctly set.
## General
* **Logging**: Use `debugLogger` for errors. NEVER leave `console.log/warn/error` in the code.
* **TypeScript**: Avoid the non-null assertion operator (`!`).
{{args}}.
"""
+2 -2
View File
@@ -1,13 +1,13 @@
description="Injects context of all relevant cli files"
prompt = """
The source code contains the content of absolutely every source code file in
packages/cli and key files from packages/core. In addition to the source code, the following test files are
packages/cli. In addition to the source code, the following test files are
included as they are test files that conform to the project's testing standards:
`packages/cli/src/ui/components/InputPrompt.test.tsx` and `packages/cli/src/ui/App.test.tsx`.
You should very rarely need to read any other files from packages/cli to resolve
prompts.
!{find packages/cli -path packages/cli/dist -prune -o -type f \\( -name "*.ts" -o -name "*.tsx" \\) ! -name "*.test.ts" ! -name "*.test.tsx" ! -name "*.d.ts" -exec echo "--- {} ---" \\; -exec cat {} \\; && echo "--- packages/cli/src/ui/components/InputPrompt.test.tsx ---" && cat packages/cli/src/ui/components/InputPrompt.test.tsx && echo "--- packages/cli/src/ui/App.test.tsx ---" && cat packages/cli/src/ui/App.test.tsx && echo "--- packages/core/src/core/coreToolScheduler.ts ---" && cat packages/core/src/core/coreToolScheduler.ts && echo "--- packages/core/src/core/nonInteractiveToolExecutor.ts ---" && cat packages/core/src/core/nonInteractiveToolExecutor.ts && echo "--- packages/core/src/confirmation-bus/message-bus.ts ---" && cat packages/core/src/confirmation-bus/message-bus.ts && echo "--- packages/core/src/confirmation-bus/types.ts ---" && cat packages/core/src/confirmation-bus/types.ts && echo "--- packages/core/src/utils/events.ts ---" && cat packages/core/src/utils/events.ts && echo "--- packages/core/src/core/client.ts ---" && cat packages/core/src/core/client.ts && echo "--- packages/core/src/core/geminiChat.ts ---" && cat packages/core/src/core/geminiChat.ts && echo "--- packages/core/src/core/turn.ts ---" && cat packages/core/src/core/turn.ts && echo "--- packages/core/src/agents/executor.ts ---" && cat packages/core/src/agents/executor.ts && echo "--- packages/core/src/telemetry/types.ts ---" && cat packages/core/src/telemetry/types.ts && echo "--- packages/core/src/telemetry/loggers.ts ---" && cat packages/core/src/telemetry/loggers.ts && echo "--- packages/core/src/telemetry/uiTelemetry.ts ---" && cat packages/core/src/telemetry/uiTelemetry.ts}
!{find packages/cli -path packages/cli/dist -prune -o -type f \\( -name "*.ts" -o -name "*.tsx" \\) ! -name "*.test.ts" ! -name "*.test.tsx" ! -name "*.d.ts" -exec echo "--- {} ---" \\; -exec cat {} \\; && echo "--- packages/cli/src/ui/components/InputPrompt.test.tsx ---" && cat packages/cli/src/ui/components/InputPrompt.test.tsx && echo "--- packages/cli/src/ui/App.test.tsx ---" && cat packages/cli/src/ui/App.test.tsx}
**Pay extremely close attention to these files.** They define the project's
core architecture, component patterns, and testing standards.
-16
View File
@@ -1,16 +0,0 @@
description = "Analyze the influence of system instructions on a specific action."
prompt = """
# Introspection Task
Take a step back and analyze your own system instructions and internal logic.
The user is curious about the reasoning behind a specific action or decision you've made.
**Specific point of interest:** {{args}}
Please provide a detailed breakdown of:
1. Which parts of your system instructions (global, workspace-specific, or provided via GEMINI.md) influenced this behavior?
2. What was your internal thought process leading up to this action?
3. Are there any ambiguities or conflicting instructions that played a role?
Your goal is to provide transparency into your underlying logic so the user can potentially improve the instructions in the future.
"""
-5
View File
@@ -1,5 +0,0 @@
{
"experimental": {
"skills": true
}
}
-65
View File
@@ -1,65 +0,0 @@
---
name: code-reviewer
description:
Use this skill to review code. It supports both local changes (staged or working tree)
and remote Pull Requests (by ID or URL). It focuses on correctness, maintainability,
and adherence to project standards.
---
# Code Reviewer
This skill guides the agent in conducting professional and thorough code reviews for both local development and remote Pull Requests.
## Workflow
### 1. Determine Review Target
* **Remote PR**: If the user provides a PR number or URL (e.g., "Review PR #123"), target that remote PR.
* **Local Changes**: If no specific PR is mentioned, or if the user asks to "review my changes", target the current local file system states (staged and unstaged changes).
### 2. Preparation
#### For Remote PRs:
1. **Checkout**: Use the GitHub CLI to checkout the PR.
```bash
gh pr checkout <PR_NUMBER>
```
2. **Preflight**: Execute the project's standard verification suite to catch automated failures early.
```bash
npm run preflight
```
3. **Context**: Read the PR description and any existing comments to understand the goal and history.
#### For Local Changes:
1. **Identify Changes**:
* Check status: `git status`
* Read diffs: `git diff` (working tree) and/or `git diff --staged` (staged).
2. **Preflight (Optional)**: If the changes are substantial, ask the user if they want to run `npm run preflight` before reviewing.
### 3. In-Depth Analysis
Analyze the code changes based on the following pillars:
* **Correctness**: Does the code achieve its stated purpose without bugs or logical errors?
* **Maintainability**: Is the code clean, well-structured, and easy to understand and modify in the future? Consider factors like code clarity, modularity, and adherence to established design patterns.
* **Readability**: Is the code well-commented (where necessary) and consistently formatted according to our project's coding style guidelines?
* **Efficiency**: Are there any obvious performance bottlenecks or resource inefficiencies introduced by the changes?
* **Security**: Are there any potential security vulnerabilities or insecure coding practices?
* **Edge Cases and Error Handling**: Does the code appropriately handle edge cases and potential errors?
* **Testability**: Is the new or modified code adequately covered by tests (even if preflight checks pass)? Suggest additional test cases that would improve coverage or robustness.
### 4. Provide Feedback
#### Structure
* **Summary**: A high-level overview of the review.
* **Findings**:
* **Critical**: Bugs, security issues, or breaking changes.
* **Improvements**: Suggestions for better code quality or performance.
* **Nitpicks**: Formatting or minor style issues (optional).
* **Conclusion**: Clear recommendation (Approved / Request Changes).
#### Tone
* Be constructive, professional, and friendly.
* Explain *why* a change is requested.
* For approvals, acknowledge the specific value of the contribution.
### 5. Cleanup (Remote PRs only)
* After the review, ask the user if they want to switch back to the default branch (e.g., `main` or `master`).
-63
View File
@@ -1,63 +0,0 @@
---
name: docs-writer
description:
Use this skill when asked to write documentation (`/docs` directory)
for Gemini CLI.
---
# `docs-writer` skill instructions
As an expert technical writer for the Gemini CLI project, your goal is to
produce documentation that is accurate, clear, and consistent with the project's
standards. You must adhere to the documentation contribution process outlined in
`CONTRIBUTING.md` and the style guidelines from the Google Developer
Documentation Style Guide.
## Step 1: Understand the goal and create a plan
1. **Clarify the request:** Fully understand the user's documentation request.
Identify the core feature, command, or concept that needs to be documented.
2. **Ask questions:** If the request is ambiguous or lacks detail, ask
clarifying questions. Don't invent or assume. It's better to ask than to
write incorrect documentation.
3. **Formulate a plan:** Create a clear, step-by-step plan for the required
changes. If requested or necessary, store this plan in a temporary file or
a file identified by the user.
## Step 2: Investigate and gather information
1. **Read the code:** Thoroughly examine the relevant codebase, primarily within
the `packages/` directory, to ensure your writing is backed by the
implementation.
2. **Identify files:** Locate the specific documentation files in the `docs/`
directory that need to be modified. Always read the latest
version of a file before you begin to edit it.
3. **Check for connections:** Consider related documentation. If you add a new
page, check if `docs/sidebar.json` needs to be updated. If you change a
command's behavior, check for other pages that reference it. Make sure links
in these pages are up to date.
## Step 3: Draft the documentation
1. **Follow the style guide:**
- Text must be wrapped at 80 characters. Exceptions are long links or
tables, unless otherwise stated by the user.
- Use sentence case for headings, titles, and bolded text.
- Address the reader as "you".
- Use contractions to keep the tone more casual.
- Use simple, direct, and active language and the present tense.
- Keep paragraphs short and focused.
- Always refer to Gemini CLI as `Gemini CLI`, never `the Gemini CLI`.
2. **Use `replace` and `write_file`:** Use the file system tools to apply your
planned changes precisely. For small edits, `replace` is preferred. For new
files or large rewrites, `write_file` is more appropriate.
## Step 4: Verify and finalize
1. **Review your work:** After making changes, re-read the files to ensure the
documentation is well-formatted, content is correct and based on existing
code, and that all new links are valid.
2. **Offer to run npm format:** Once all changes are complete and the user
confirms they have no more requests, offer to run the project's formatting
script to ensure consistency. Propose the following command:
`npm run format`
-73
View File
@@ -1,73 +0,0 @@
---
name: pr-creator
description:
Use this skill when asked to create a pull request (PR). It ensures all PRs
follow the repository's established templates and standards.
---
# Pull Request Creator
This skill guides the creation of high-quality Pull Requests that adhere to the
repository's standards.
## Workflow
Follow these steps to create a Pull Request:
1. **Branch Management**: Check the current branch to avoid working directly
on `main`.
- Run `git branch --show-current`.
- If the current branch is `main`, create and switch to a new descriptive
branch:
```bash
git checkout -b <new-branch-name>
```
2. **Locate Template**: Search for a pull request template in the repository.
- Check `.github/pull_request_template.md`
- Check `.github/PULL_REQUEST_TEMPLATE.md`
- If multiple templates exist (e.g., in `.github/PULL_REQUEST_TEMPLATE/`),
ask the user which one to use or select the most appropriate one based on
the context (e.g., `bug_fix.md` vs `feature.md`).
3. **Read Template**: Read the content of the identified template file.
4. **Draft Description**: Create a PR description that strictly follows the
template's structure.
- **Headings**: Keep all headings from the template.
- **Checklists**: Review each item. Mark with `[x]` if completed. If an item
is not applicable, leave it unchecked or mark as `[ ]` (depending on the
template's instructions) or remove it if the template allows flexibility
(but prefer keeping it unchecked for transparency).
- **Content**: Fill in the sections with clear, concise summaries of your
changes.
- **Related Issues**: Link any issues fixed or related to this PR (e.g.,
"Fixes #123").
5. **Preflight Check**: Before creating the PR, run the workspace preflight
script to ensure all build, lint, and test checks pass.
```bash
npm run preflight
```
If any checks fail, address the issues before proceeding to create the PR.
6. **Create PR**: Use the `gh` CLI to create the PR. To avoid shell escaping
issues with multi-line Markdown, write the description to a temporary file
first.
```bash
# 1. Write the drafted description to a temporary file
# 2. Create the PR using the --body-file flag
gh pr create --title "type(scope): succinct description" --body-file <temp_file_path>
# 3. Remove the temporary file
rm <temp_file_path>
```
- **Title**: Ensure the title follows the
[Conventional Commits](https://www.conventionalcommits.org/) format if the
repository uses it (e.g., `feat(ui): add new button`,
`fix(core): resolve crash`).
## Principles
- **Compliance**: Never ignore the PR template. It exists for a reason.
- **Completeness**: Fill out all relevant sections.
- **Accuracy**: Don't check boxes for tasks you haven't done.
-3
View File
@@ -11,6 +11,3 @@
/.github/workflows/ @google-gemini/gemini-cli-askmode-approvers
/packages/cli/package.json @google-gemini/gemini-cli-askmode-approvers
/packages/core/package.json @google-gemini/gemini-cli-askmode-approvers
# Docs have a dedicated approver group in addition to maintainers
/docs/ @google-gemini/gemini-cli-maintainers @google-gemini/gemini-cli-docs
+2
View File
@@ -1,5 +1,7 @@
name: 'Bug Report'
description: 'Report a bug to help us improve Gemini CLI'
labels:
- 'status/need-triage'
body:
- type: 'markdown'
attributes:
+20 -18
View File
@@ -1,33 +1,35 @@
# See https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: 'npm'
directory: '/'
schedule:
interval: 'weekly'
day: 'monday'
open-pull-requests-limit: 10
interval: 'daily'
target-branch: 'main'
commit-message:
prefix: 'chore(deps)'
include: 'scope'
reviewers:
- 'joshualitt'
- 'google-gemini/gemini-cli-askmode-approvers'
groups:
npm-dependencies:
patterns:
- '*'
# Group all non-major updates together.
# This is to reduce the number of PRs that need to be reviewed.
# Major updates will still be created as separate PRs.
npm-minor-patch:
applies-to: 'version-updates'
update-types:
- 'minor'
- 'patch'
open-pull-requests-limit: 0
- package-ecosystem: 'github-actions'
directory: '/'
schedule:
interval: 'weekly'
day: 'monday'
open-pull-requests-limit: 10
interval: 'daily'
target-branch: 'main'
commit-message:
prefix: 'chore(deps)'
include: 'scope'
reviewers:
- 'joshualitt'
groups:
actions-dependencies:
patterns:
- '*'
update-types:
- 'minor'
- 'patch'
- 'google-gemini/gemini-cli-askmode-approvers'
open-pull-requests-limit: 0
-138
View File
@@ -1,138 +0,0 @@
/* eslint-disable */
/* global require, console, process */
/**
* Script to backfill the 'status/need-triage' label to all open issues
* that are NOT currently labeled with '🔒 maintainer only' or 'help wanted'.
*/
const { execFileSync } = require('child_process');
const isDryRun = process.argv.includes('--dry-run');
const REPO = 'google-gemini/gemini-cli';
/**
* Executes a GitHub CLI command safely using an argument array to prevent command injection.
* @param {string[]} args
* @returns {string|null}
*/
function runGh(args) {
try {
// Using execFileSync with an array of arguments is safe as it doesn't use a shell.
// We set a large maxBuffer (10MB) to handle repositories with many issues.
return execFileSync('gh', args, {
encoding: 'utf8',
maxBuffer: 10 * 1024 * 1024,
stdio: ['ignore', 'pipe', 'pipe'],
}).trim();
} catch (error) {
const stderr = error.stderr ? ` Stderr: ${error.stderr.trim()}` : '';
console.error(
`❌ Error running gh ${args.join(' ')}: ${error.message}${stderr}`,
);
return null;
}
}
async function main() {
console.log('🔐 GitHub CLI security check...');
const authStatus = runGh(['auth', 'status']);
if (authStatus === null) {
console.error('❌ GitHub CLI (gh) is not installed or not authenticated.');
process.exit(1);
}
if (isDryRun) {
console.log('🧪 DRY RUN MODE ENABLED - No changes will be made.\n');
}
console.log(`🔍 Fetching and filtering open issues from ${REPO}...`);
// We use the /issues endpoint with pagination to bypass the 1000-result limit.
// The jq filter ensures we exclude PRs, maintainer-only, help-wanted, and existing status/need-triage.
const jqFilter =
'.[] | select(.pull_request == null) | select([.labels[].name] as $l | (any($l[]; . == "🔒 maintainer only") | not) and (any($l[]; . == "help wanted") | not) and (any($l[]; . == "status/need-triage") | not)) | {number: .number, title: .title}';
const output = runGh([
'api',
`repos/${REPO}/issues?state=open&per_page=100`,
'--paginate',
'--jq',
jqFilter,
]);
if (output === null) {
process.exit(1);
}
const issues = output
.split('\n')
.filter((line) => line.trim())
.map((line) => {
try {
return JSON.parse(line);
} catch (_e) {
console.error(`⚠️ Failed to parse line: ${line}`);
return null;
}
})
.filter(Boolean);
console.log(`✅ Found ${issues.length} issues matching criteria.`);
if (issues.length === 0) {
console.log('✨ No issues need backfilling.');
return;
}
let successCount = 0;
let failCount = 0;
if (isDryRun) {
for (const issue of issues) {
console.log(
`[DRY RUN] Would label issue #${issue.number}: ${issue.title}`,
);
}
successCount = issues.length;
} else {
console.log(`🏷️ Applying labels to ${issues.length} issues...`);
for (const issue of issues) {
const issueNumber = String(issue.number);
console.log(`🏷️ Labeling issue #${issueNumber}: ${issue.title}`);
const result = runGh([
'issue',
'edit',
issueNumber,
'--add-label',
'status/need-triage',
'--repo',
REPO,
]);
if (result !== null) {
successCount++;
} else {
failCount++;
}
}
}
console.log(`\n📊 Summary:`);
console.log(` - Success: ${successCount}`);
console.log(` - Failed: ${failCount}`);
if (failCount > 0) {
console.error(`\n❌ Backfill completed with ${failCount} errors.`);
process.exit(1);
} else {
console.log(`\n🎉 ${isDryRun ? 'Dry run' : 'Backfill'} complete!`);
}
}
main().catch((error) => {
console.error('❌ Unexpected error:', error);
process.exit(1);
});
@@ -1,190 +0,0 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/* eslint-disable */
/* global require, console, process */
/**
* Script to backfill a process change notification comment to all open PRs
* not created by members of the 'gemini-cli-maintainers' team.
*
* Skip PRs that are already associated with an issue.
*/
const { execFileSync } = require('child_process');
const isDryRun = process.argv.includes('--dry-run');
const REPO = 'google-gemini/gemini-cli';
const ORG = 'google-gemini';
const TEAM_SLUG = 'gemini-cli-maintainers';
const DISCUSSION_URL =
'https://github.com/google-gemini/gemini-cli/discussions/16706';
/**
* Executes a GitHub CLI command safely using an argument array.
*/
function runGh(args, options = {}) {
const { silent = false } = options;
try {
return execFileSync('gh', args, {
encoding: 'utf8',
maxBuffer: 10 * 1024 * 1024,
stdio: ['ignore', 'pipe', 'pipe'],
}).trim();
} catch (error) {
if (!silent) {
const stderr = error.stderr ? ` Stderr: ${error.stderr.trim()}` : '';
console.error(
`❌ Error running gh ${args.join(' ')}: ${error.message}${stderr}`,
);
}
return null;
}
}
/**
* Checks if a user is a member of the maintainers team.
*/
const membershipCache = new Map();
function isMaintainer(username) {
if (membershipCache.has(username)) return membershipCache.get(username);
// GitHub returns 404 if user is not a member.
// We use silent: true to avoid logging 404s as errors.
const result = runGh(
['api', `orgs/${ORG}/teams/${TEAM_SLUG}/memberships/${username}`],
{ silent: true },
);
const isMember = result !== null;
membershipCache.set(username, isMember);
return isMember;
}
async function main() {
console.log('🔐 GitHub CLI security check...');
if (runGh(['auth', 'status']) === null) {
console.error('❌ GitHub CLI (gh) is not authenticated.');
process.exit(1);
}
if (isDryRun) {
console.log('🧪 DRY RUN MODE ENABLED\n');
}
console.log(`📥 Fetching open PRs from ${REPO}...`);
// Fetch number, author, and closingIssuesReferences to check if linked to an issue
const prsJson = runGh([
'pr',
'list',
'--repo',
REPO,
'--state',
'open',
'--limit',
'1000',
'--json',
'number,author,closingIssuesReferences',
]);
if (prsJson === null) process.exit(1);
const prs = JSON.parse(prsJson);
console.log(`📊 Found ${prs.length} open PRs. Filtering...`);
let targetPrs = [];
for (const pr of prs) {
const author = pr.author.login;
const issueCount = pr.closingIssuesReferences
? pr.closingIssuesReferences.length
: 0;
if (issueCount > 0) {
// Skip if already linked to an issue
continue;
}
if (!isMaintainer(author)) {
targetPrs.push(pr);
}
}
console.log(
`✅ Found ${targetPrs.length} PRs from non-maintainers without associated issues.`,
);
const commentBody =
"\nHi @{AUTHOR}, thank you so much for your contribution to Gemini CLI! We really appreciate the time and effort you've put into this.\n\nWe're making some updates to our contribution process to improve how we track and review changes. Please take a moment to review our recent discussion post: [Improving Our Contribution Process & Introducing New Guidelines](${DISCUSSION_URL}).\n\nKey Update: Starting **January 26, 2026**, the Gemini CLI project will require all pull requests to be associated with an existing issue. Any pull requests not linked to an issue by that date will be automatically closed.\n\nThank you for your understanding and for being a part of our community!\n ".trim();
let successCount = 0;
let skipCount = 0;
let failCount = 0;
for (const pr of targetPrs) {
const prNumber = String(pr.number);
const author = pr.author.login;
// Check if we already commented (idempotency)
// We use silent: true here because view might fail if PR is deleted mid-run
const existingComments = runGh(
[
'pr',
'view',
prNumber,
'--repo',
REPO,
'--json',
'comments',
'--jq',
`.comments[].body | contains("${DISCUSSION_URL}")`,
],
{ silent: true },
);
if (existingComments && existingComments.includes('true')) {
console.log(
`⏭️ PR #${prNumber} already has the notification. Skipping.`,
);
skipCount++;
continue;
}
if (isDryRun) {
console.log(`[DRY RUN] Would notify @${author} on PR #${prNumber}`);
successCount++;
} else {
console.log(`💬 Notifying @${author} on PR #${prNumber}...`);
const personalizedComment = commentBody.replace('{AUTHOR}', author);
const result = runGh([
'pr',
'comment',
prNumber,
'--repo',
REPO,
'--body',
personalizedComment,
]);
if (result !== null) {
successCount++;
} else {
failCount++;
}
}
}
console.log(`\n📊 Summary:`);
console.log(` - Notified: ${successCount}`);
console.log(` - Skipped: ${skipCount}`);
console.log(` - Failed: ${failCount}`);
if (failCount > 0) process.exit(1);
}
main().catch((e) => {
console.error(e);
process.exit(1);
});
+100 -147
View File
@@ -1,180 +1,133 @@
#!/usr/bin/env bash
# @license
# Copyright 2026 Google LLC
# SPDX-License-Identifier: Apache-2.0
set -euo pipefail
# Initialize a comma-separated string to hold PR numbers that need a comment
PRS_NEEDING_COMMENT=""
# Global cache for issue labels (compatible with Bash 3.2)
# Stores "|ISSUE_NUM:LABELS|" segments
ISSUE_LABELS_CACHE_FLAT="|"
# Function to get labels from an issue (with caching)
get_issue_labels() {
local ISSUE_NUM="${1}"
if [[ -z "${ISSUE_NUM}" || "${ISSUE_NUM}" == "null" || "${ISSUE_NUM}" == "" ]]; then
return
# Function to process a single PR
process_pr() {
if [[ -z "${GITHUB_REPOSITORY:-}" ]]; then
echo "‼️ Missing \$GITHUB_REPOSITORY - this must be run from GitHub Actions"
return 1
fi
# Check cache
case "${ISSUE_LABELS_CACHE_FLAT}" in
*"|${ISSUE_NUM}:"*)
local suffix="${ISSUE_LABELS_CACHE_FLAT#*|${ISSUE_NUM}:}"
echo "${suffix%%|*}"
return
;;
*)
# Cache miss, proceed to fetch
;;
esac
echo " 📥 Fetching labels from issue #${ISSUE_NUM}" >&2
local gh_output
if ! gh_output=$(gh issue view "${ISSUE_NUM}" --repo "${GITHUB_REPOSITORY}" --json labels -q '.labels[].name' 2>/dev/null); then
echo " ⚠️ Could not fetch issue #${ISSUE_NUM}" >&2
ISSUE_LABELS_CACHE_FLAT="${ISSUE_LABELS_CACHE_FLAT}${ISSUE_NUM}:|"
return
if [[ -z "${GITHUB_OUTPUT:-}" ]]; then
echo "‼️ Missing \$GITHUB_OUTPUT - this must be run from GitHub Actions"
return 1
fi
local labels
labels=$(echo "${gh_output}" | grep -x -E '(area|priority)/.*|help wanted|🔒 maintainer only' | tr '\n' ',' | sed 's/,$//' || echo "")
# Save to flat cache
ISSUE_LABELS_CACHE_FLAT="${ISSUE_LABELS_CACHE_FLAT}${ISSUE_NUM}:${labels}|"
echo "${labels}"
}
# Function to process a single PR with pre-fetched data
process_pr_optimized() {
local PR_NUMBER="${1}"
local IS_DRAFT="${2}"
local ISSUE_NUMBER="${3}"
local CURRENT_LABELS="${4}" # Comma-separated labels
local PR_NUMBER=$1
echo "🔄 Processing PR #${PR_NUMBER}"
local LABELS_TO_ADD=""
local LABELS_TO_REMOVE=""
if [[ -z "${ISSUE_NUMBER}" || "${ISSUE_NUMBER}" == "null" || "${ISSUE_NUMBER}" == "" ]]; then
if [[ "${IS_DRAFT}" == "true" ]]; then
echo " 📝 PR #${PR_NUMBER} is a draft and has no linked issue"
if [[ ",${CURRENT_LABELS}," == *",status/need-issue,"* ]]; then
echo " Removing status/need-issue label"
LABELS_TO_REMOVE="status/need-issue"
fi
else
echo " ⚠️ No linked issue found for PR #${PR_NUMBER}"
if [[ ",${CURRENT_LABELS}," != *",status/need-issue,"* ]]; then
echo " Adding status/need-issue label"
LABELS_TO_ADD="status/need-issue"
fi
if [[ -z "${PRS_NEEDING_COMMENT}" ]]; then
PRS_NEEDING_COMMENT="${PR_NUMBER}"
else
PRS_NEEDING_COMMENT="${PRS_NEEDING_COMMENT},${PR_NUMBER}"
fi
fi
else
echo " 🔗 Found linked issue #${ISSUE_NUMBER}"
if [[ ",${CURRENT_LABELS}," == *",status/need-issue,"* ]]; then
echo " Removing status/need-issue label"
LABELS_TO_REMOVE="status/need-issue"
fi
local ISSUE_LABELS
ISSUE_LABELS=$(get_issue_labels "${ISSUE_NUMBER}")
if [[ -n "${ISSUE_LABELS}" ]]; then
local IFS_OLD="${IFS}"
IFS=','
for label in ${ISSUE_LABELS}; do
if [[ -n "${label}" ]] && [[ ",${CURRENT_LABELS}," != *",${label},"* ]]; then
if [[ -z "${LABELS_TO_ADD}" ]]; then
LABELS_TO_ADD="${label}"
else
LABELS_TO_ADD="${LABELS_TO_ADD},${label}"
fi
fi
done
IFS="${IFS_OLD}"
fi
if [[ -z "${LABELS_TO_ADD}" && -z "${LABELS_TO_REMOVE}" ]]; then
echo " ✅ Labels already synchronized"
fi
# Get closing issue number with error handling
local ISSUE_NUMBER
if ! ISSUE_NUMBER=$(gh pr view "${PR_NUMBER}" --repo "${GITHUB_REPOSITORY}" --json closingIssuesReferences -q '.closingIssuesReferences.nodes[0].number' 2>/dev/null); then
echo " ⚠️ Could not fetch closing issue for PR #${PR_NUMBER}"
fi
if [[ -n "${LABELS_TO_ADD}" || -n "${LABELS_TO_REMOVE}" ]]; then
local EDIT_CMD=("gh" "pr" "edit" "${PR_NUMBER}" "--repo" "${GITHUB_REPOSITORY}")
if [[ -z "${ISSUE_NUMBER}" ]]; then
echo "⚠️ No linked issue found for PR #${PR_NUMBER}, adding status/need-issue label"
if ! gh pr edit "${PR_NUMBER}" --repo "${GITHUB_REPOSITORY}" --add-label "status/need-issue" 2>/dev/null; then
echo " ⚠️ Failed to add label (may already exist or have permission issues)"
fi
# Add PR number to the list
if [[ -z "${PRS_NEEDING_COMMENT}" ]]; then
PRS_NEEDING_COMMENT="${PR_NUMBER}"
else
PRS_NEEDING_COMMENT="${PRS_NEEDING_COMMENT},${PR_NUMBER}"
fi
echo "needs_comment=true" >> "${GITHUB_OUTPUT}"
else
echo "🔗 Found linked issue #${ISSUE_NUMBER}"
# Remove status/need-issue label if present
if ! gh pr edit "${PR_NUMBER}" --repo "${GITHUB_REPOSITORY}" --remove-label "status/need-issue" 2>/dev/null; then
echo " status/need-issue label not present or could not be removed"
fi
# Get issue labels
echo "📥 Fetching labels from issue #${ISSUE_NUMBER}"
local ISSUE_LABELS=""
if ! ISSUE_LABELS=$(gh issue view "${ISSUE_NUMBER}" --repo "${GITHUB_REPOSITORY}" --json labels -q '.labels[].name' 2>/dev/null | tr '\n' ',' | sed 's/,$//' || echo ""); then
echo " ⚠️ Could not fetch issue #${ISSUE_NUMBER} (may not exist or be in different repo)"
ISSUE_LABELS=""
fi
# Get PR labels
echo "📥 Fetching labels from PR #${PR_NUMBER}"
local PR_LABELS=""
if ! PR_LABELS=$(gh pr view "${PR_NUMBER}" --repo "${GITHUB_REPOSITORY}" --json labels -q '.labels[].name' 2>/dev/null | tr '\n' ',' | sed 's/,$//' || echo ""); then
echo " ⚠️ Could not fetch PR labels"
PR_LABELS=""
fi
echo " Issue labels: ${ISSUE_LABELS}"
echo " PR labels: ${PR_LABELS}"
# Convert comma-separated strings to arrays
local ISSUE_LABEL_ARRAY PR_LABEL_ARRAY
IFS=',' read -ra ISSUE_LABEL_ARRAY <<< "${ISSUE_LABELS}"
IFS=',' read -ra PR_LABEL_ARRAY <<< "${PR_LABELS}"
# Find labels to add (on issue but not on PR)
local LABELS_TO_ADD=""
for label in "${ISSUE_LABEL_ARRAY[@]}"; do
if [[ -n "${label}" ]] && [[ " ${PR_LABEL_ARRAY[*]} " != *" ${label} "* ]]; then
if [[ -z "${LABELS_TO_ADD}" ]]; then
LABELS_TO_ADD="${label}"
else
LABELS_TO_ADD="${LABELS_TO_ADD},${label}"
fi
fi
done
# Apply label changes
if [[ -n "${LABELS_TO_ADD}" ]]; then
echo " Syncing labels to add: ${LABELS_TO_ADD}"
EDIT_CMD+=("--add-label" "${LABELS_TO_ADD}")
echo " Adding labels: ${LABELS_TO_ADD}"
if ! gh pr edit "${PR_NUMBER}" --repo "${GITHUB_REPOSITORY}" --add-label "${LABELS_TO_ADD}" 2>/dev/null; then
echo " ⚠️ Failed to add some labels"
fi
fi
if [[ -n "${LABELS_TO_REMOVE}" ]]; then
echo " Syncing labels to remove: ${LABELS_TO_REMOVE}"
EDIT_CMD+=("--remove-label" "${LABELS_TO_REMOVE}")
if [[ -z "${LABELS_TO_ADD}" ]]; then
echo "✅ Labels already synchronized"
fi
("${EDIT_CMD[@]}" || true)
echo "needs_comment=false" >> "${GITHUB_OUTPUT}"
fi
}
if [[ -z "${GITHUB_REPOSITORY:-}" ]]; then
echo "‼️ Missing \$GITHUB_REPOSITORY - this must be run from GitHub Actions"
exit 1
fi
if [[ -z "${GITHUB_OUTPUT:-}" ]]; then
echo "‼️ Missing \$GITHUB_OUTPUT - this must be run from GitHub Actions"
exit 1
fi
JQ_EXTRACT_FIELDS='{
number: .number,
isDraft: .isDraft,
issue: (.closingIssuesReferences[0].number // (.body // "" | capture("(^|[^a-zA-Z0-9])#(?<num>[0-9]+)([^a-zA-Z0-9]|$)")? | .num) // "null"),
labels: [.labels[].name] | join(",")
}'
JQ_TSV_FORMAT='"\((.number | tostring))\t\(.isDraft)\t\((.issue // null) | tostring)\t\(.labels)"'
# If PR_NUMBER is set, process only that PR
if [[ -n "${PR_NUMBER:-}" ]]; then
echo "🔄 Processing single PR #${PR_NUMBER}"
PR_DATA=$(gh pr view "${PR_NUMBER}" --repo "${GITHUB_REPOSITORY}" --json number,closingIssuesReferences,isDraft,body,labels 2>/dev/null) || {
echo "❌ Failed to fetch data for PR #${PR_NUMBER}"
if ! process_pr "${PR_NUMBER}"; then
echo "❌ Failed to process PR #${PR_NUMBER}"
exit 1
}
line=$(echo "${PR_DATA}" | jq -r "${JQ_EXTRACT_FIELDS} | ${JQ_TSV_FORMAT}")
IFS=$'\t' read -r pr_num is_draft issue_num current_labels <<< "${line}"
process_pr_optimized "${pr_num}" "${is_draft}" "${issue_num}" "${current_labels}"
fi
else
# Otherwise, get all open PRs and process them
# The script logic will determine which ones need issue linking or label sync
echo "📥 Getting all open pull requests..."
PR_DATA_ALL=$(gh pr list --repo "${GITHUB_REPOSITORY}" --state open --limit 1000 --json number,closingIssuesReferences,isDraft,body,labels 2>/dev/null) || {
if ! PR_NUMBERS=$(gh pr list --repo "${GITHUB_REPOSITORY}" --state open --limit 1000 --json number -q '.[].number' 2>/dev/null); then
echo "❌ Failed to fetch PR list"
exit 1
}
fi
PR_COUNT=$(echo "${PR_DATA_ALL}" | jq '. | length')
echo "📊 Found ${PR_COUNT} open PRs to process"
if [[ -z "${PR_NUMBERS}" ]]; then
echo "✅ No open PRs found"
else
# Count the number of PRs
PR_COUNT=$(echo "${PR_NUMBERS}" | wc -w | tr -d ' ')
echo "📊 Found ${PR_COUNT} open PRs to process"
# Use a temporary file to avoid masking exit codes in process substitution
tmp_file=$(mktemp)
echo "${PR_DATA_ALL}" | jq -r ".[] | ${JQ_EXTRACT_FIELDS} | ${JQ_TSV_FORMAT}" > "${tmp_file}"
while read -r line; do
[[ -z "${line}" ]] && continue
IFS=$'\t' read -r pr_num is_draft issue_num current_labels <<< "${line}"
process_pr_optimized "${pr_num}" "${is_draft}" "${issue_num}" "${current_labels}"
done < "${tmp_file}"
rm -f "${tmp_file}"
for pr_number in ${PR_NUMBERS}; do
if ! process_pr "${pr_number}"; then
echo "⚠️ Failed to process PR #${pr_number}, continuing with next PR..."
continue
fi
done
fi
fi
# Ensure output is always set, even if empty
if [[ -z "${PRS_NEEDING_COMMENT}" ]]; then
echo "prs_needing_comment=[]" >> "${GITHUB_OUTPUT}"
else
-355
View File
@@ -1,355 +0,0 @@
/* eslint-disable @typescript-eslint/no-require-imports */
/* global process, console, require */
const { Octokit } = require('@octokit/rest');
/**
* Sync Maintainer Labels (Recursive with strict parent-child relationship detection)
* - Uses Native Sub-issues.
* - Uses Markdown Task Lists (- [ ] #123).
* - Filters for OPEN issues only.
* - Skips DUPLICATES.
* - Skips Pull Requests.
* - ONLY labels issues in the PUBLIC (gemini-cli) repo.
*/
const REPO_OWNER = 'google-gemini';
const PUBLIC_REPO = 'gemini-cli';
const PRIVATE_REPO = 'maintainers-gemini-cli';
const ALLOWED_REPOS = [PUBLIC_REPO, PRIVATE_REPO];
const ROOT_ISSUES = [
{ owner: REPO_OWNER, repo: PUBLIC_REPO, number: 15374 },
{ owner: REPO_OWNER, repo: PUBLIC_REPO, number: 15456 },
{ owner: REPO_OWNER, repo: PUBLIC_REPO, number: 15324 },
];
const TARGET_LABEL = '🔒 maintainer only';
const isDryRun =
process.argv.includes('--dry-run') || process.env.DRY_RUN === 'true';
const octokit = new Octokit({
auth: process.env.GITHUB_TOKEN,
});
/**
* Extracts child issue references from markdown Task Lists ONLY.
* e.g. - [ ] #123 or - [x] google-gemini/gemini-cli#123
*/
function extractTaskListLinks(text, contextOwner, contextRepo) {
if (!text) return [];
const childIssues = new Map();
const add = (owner, repo, number) => {
if (ALLOWED_REPOS.includes(repo)) {
const key = `${owner}/${repo}#${number}`;
childIssues.set(key, { owner, repo, number: parseInt(number, 10) });
}
};
// 1. Full URLs in task lists
const urlRegex =
/-\s+\[[ x]\].*https:\/\/github\.com\/([a-zA-Z0-9._-]+)\/([a-zA-Z0-9._-]+)\/issues\/(\d+)\b/g;
let match;
while ((match = urlRegex.exec(text)) !== null) {
add(match[1], match[2], match[3]);
}
// 2. Cross-repo refs in task lists: owner/repo#123
const crossRepoRegex =
/-\s+\[[ x]\].*([a-zA-Z0-9._-]+)\/([a-zA-Z0-9._-]+)#(\d+)\b/g;
while ((match = crossRepoRegex.exec(text)) !== null) {
add(match[1], match[2], match[3]);
}
// 3. Short refs in task lists: #123
const shortRefRegex = /-\s+\[[ x]\].*#(\d+)\b/g;
while ((match = shortRefRegex.exec(text)) !== null) {
add(contextOwner, contextRepo, match[1]);
}
return Array.from(childIssues.values());
}
/**
* Fetches issue data via GraphQL with full pagination for sub-issues, comments, and labels.
*/
async function fetchIssueData(owner, repo, number) {
const query = `
query($owner:String!, $repo:String!, $number:Int!) {
repository(owner:$owner, name:$repo) {
issue(number:$number) {
state
title
body
labels(first: 100) {
nodes { name }
pageInfo { hasNextPage endCursor }
}
subIssues(first: 100) {
nodes {
number
repository {
name
owner { login }
}
}
pageInfo { hasNextPage endCursor }
}
comments(first: 100) {
nodes {
body
}
}
}
}
}
`;
try {
const response = await octokit.graphql(query, { owner, repo, number });
const data = response.repository.issue;
if (!data) return null;
const issue = {
state: data.state,
title: data.title,
body: data.body || '',
labels: data.labels.nodes.map((n) => n.name),
subIssues: [...data.subIssues.nodes],
comments: data.comments.nodes.map((n) => n.body),
};
// Paginate subIssues if there are more than 100
if (data.subIssues.pageInfo.hasNextPage) {
const moreSubIssues = await paginateConnection(
owner,
repo,
number,
'subIssues',
'number repository { name owner { login } }',
data.subIssues.pageInfo.endCursor,
);
issue.subIssues.push(...moreSubIssues);
}
// Paginate labels if there are more than 100 (unlikely but for completeness)
if (data.labels.pageInfo.hasNextPage) {
const moreLabels = await paginateConnection(
owner,
repo,
number,
'labels',
'name',
data.labels.pageInfo.endCursor,
(n) => n.name,
);
issue.labels.push(...moreLabels);
}
// Note: Comments are handled via Task Lists in body + first 100 comments.
// If an issue has > 100 comments with task lists, we'd need to paginate those too.
// Given the 1,100+ issue discovery count, 100 comments is usually sufficient,
// but we can add it for absolute completeness.
// (Skipping for now to avoid excessive API churn unless clearly needed).
return issue;
} catch (error) {
if (error.errors && error.errors.some((e) => e.type === 'NOT_FOUND')) {
return null;
}
throw error;
}
}
/**
* Helper to paginate any GraphQL connection.
*/
async function paginateConnection(
owner,
repo,
number,
connectionName,
nodeFields,
initialCursor,
transformNode = (n) => n,
) {
let additionalNodes = [];
let hasNext = true;
let cursor = initialCursor;
while (hasNext) {
const query = `
query($owner:String!, $repo:String!, $number:Int!, $cursor:String) {
repository(owner:$owner, name:$repo) {
issue(number:$number) {
${connectionName}(first: 100, after: $cursor) {
nodes { ${nodeFields} }
pageInfo { hasNextPage endCursor }
}
}
}
}
`;
const response = await octokit.graphql(query, {
owner,
repo,
number,
cursor,
});
const connection = response.repository.issue[connectionName];
additionalNodes.push(...connection.nodes.map(transformNode));
hasNext = connection.pageInfo.hasNextPage;
cursor = connection.pageInfo.endCursor;
}
return additionalNodes;
}
/**
* Validates if an issue should be processed (Open, not a duplicate, not a PR)
*/
function shouldProcess(issueData) {
if (!issueData) return false;
if (issueData.state !== 'OPEN') return false;
const labels = issueData.labels.map((l) => l.toLowerCase());
if (labels.includes('duplicate') || labels.includes('kind/duplicate')) {
return false;
}
return true;
}
async function getAllDescendants(roots) {
const allDescendants = new Map();
const visited = new Set();
const queue = [...roots];
for (const root of roots) {
visited.add(`${root.owner}/${root.repo}#${root.number}`);
}
console.log(`Starting discovery from ${roots.length} roots...`);
while (queue.length > 0) {
const current = queue.shift();
const currentKey = `${current.owner}/${current.repo}#${current.number}`;
try {
const issueData = await fetchIssueData(
current.owner,
current.repo,
current.number,
);
if (!shouldProcess(issueData)) {
continue;
}
// ONLY add to labeling list if it's in the PUBLIC repository
if (current.repo === PUBLIC_REPO) {
// Don't label the roots themselves
if (
!ROOT_ISSUES.some(
(r) => r.number === current.number && r.repo === current.repo,
)
) {
allDescendants.set(currentKey, {
...current,
title: issueData.title,
labels: issueData.labels,
});
}
}
const children = new Map();
// 1. Process Native Sub-issues
if (issueData.subIssues) {
for (const node of issueData.subIssues) {
const childOwner = node.repository.owner.login;
const childRepo = node.repository.name;
const childNumber = node.number;
const key = `${childOwner}/${childRepo}#${childNumber}`;
children.set(key, {
owner: childOwner,
repo: childRepo,
number: childNumber,
});
}
}
// 2. Process Markdown Task Lists in Body and Comments
let combinedText = issueData.body || '';
if (issueData.comments) {
for (const commentBody of issueData.comments) {
combinedText += '\n' + (commentBody || '');
}
}
const taskListLinks = extractTaskListLinks(
combinedText,
current.owner,
current.repo,
);
for (const link of taskListLinks) {
const key = `${link.owner}/${link.repo}#${link.number}`;
children.set(key, link);
}
// Queue children (regardless of which repo they are in, for recursion)
for (const [key, child] of children) {
if (!visited.has(key)) {
visited.add(key);
queue.push(child);
}
}
} catch (error) {
console.error(`Error processing ${currentKey}: ${error.message}`);
}
}
return Array.from(allDescendants.values());
}
async function run() {
if (isDryRun) {
console.log('=== DRY RUN MODE: No labels will be applied ===');
}
const descendants = await getAllDescendants(ROOT_ISSUES);
console.log(
`\nFound ${descendants.length} total unique open descendant issues in ${PUBLIC_REPO}.`,
);
for (const issueInfo of descendants) {
const issueKey = `${issueInfo.owner}/${issueInfo.repo}#${issueInfo.number}`;
try {
// Data is already available from the discovery phase
const hasLabel = issueInfo.labels.some((l) => l === TARGET_LABEL);
if (!hasLabel) {
if (isDryRun) {
console.log(
`[DRY RUN] Would label ${issueKey}: "${issueInfo.title}"`,
);
} else {
console.log(`Labeling ${issueKey}: "${issueInfo.title}"...`);
await octokit.rest.issues.addLabels({
owner: issueInfo.owner,
repo: issueInfo.repo,
issue_number: issueInfo.number,
labels: [TARGET_LABEL],
});
}
}
} catch (error) {
console.error(`Error processing label for ${issueKey}: ${error.message}`);
}
}
}
run().catch((error) => {
console.error(error);
process.exit(1);
});
+15 -45
View File
@@ -43,15 +43,13 @@ jobs:
- id: 'merge-queue-ci-skipper'
uses: 'cariad-tech/merge-queue-ci-skipper@1032489e59437862c90a08a2c92809c903883772' # ratchet:cariad-tech/merge-queue-ci-skipper@main
with:
secret: '${{ secrets.GEMINI_CLI_ROBOT_GITHUB_PAT }}'
secret: '${{ secrets.GITHUB_TOKEN }}'
lint:
name: 'Lint'
runs-on: 'gemini-cli-ubuntu-16-core'
needs: 'merge_queue_skipper'
if: "${{needs.merge_queue_skipper.outputs.skip == 'false'}}"
env:
GEMINI_LINT_TEMP_DIR: '${{ github.workspace }}/.gemini-linters'
steps:
- name: 'Checkout'
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
@@ -65,21 +63,9 @@ jobs:
node-version-file: '.nvmrc'
cache: 'npm'
- name: 'Cache Linters'
uses: 'actions/cache@v4'
with:
path: '${{ env.GEMINI_LINT_TEMP_DIR }}'
key: "${{ runner.os }}-${{ runner.arch }}-linters-${{ hashFiles('scripts/lint.js') }}"
- name: 'Install dependencies'
run: 'npm ci'
- name: 'Cache ESLint'
uses: 'actions/cache@v4'
with:
path: '.eslintcache'
key: "${{ runner.os }}-eslint-${{ hashFiles('package-lock.json', 'eslint.config.js') }}"
- name: 'Validate NOTICES.txt'
run: 'git diff --exit-code packages/vscode-ide-companion/NOTICES.txt'
@@ -125,9 +111,10 @@ jobs:
args: '--verbose --accept 200,503 ./**/*.md'
fail: true
test_linux:
name: 'Test (Linux) - ${{ matrix.node-version }}, ${{ matrix.shard }}'
name: 'Test (Linux)'
runs-on: 'gemini-cli-ubuntu-16-core'
needs:
- 'lint'
- 'merge_queue_skipper'
if: "${{needs.merge_queue_skipper.outputs.skip == 'false'}}"
permissions:
@@ -140,9 +127,6 @@ jobs:
- '20.x'
- '22.x'
- '24.x'
shard:
- 'cli'
- 'others'
steps:
- name: 'Checkout'
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
@@ -162,14 +146,7 @@ jobs:
- name: 'Run tests and generate reports'
env:
NO_COLOR: true
run: |
if [[ "${{ matrix.shard }}" == "cli" ]]; then
npm run test:ci --workspace @google/gemini-cli
else
# Explicitly list non-cli packages to ensure they are sharded correctly
npm run test:ci --workspace @google/gemini-cli-core --workspace @google/gemini-cli-a2a-server --workspace gemini-cli-vscode-ide-companion --workspace @google/gemini-cli-test-utils --if-present
npm run test:scripts
fi
run: 'npm run test:ci'
- name: 'Bundle'
run: 'npm run bundle'
@@ -185,7 +162,7 @@ jobs:
${{ always() && (github.event.pull_request.head.repo.full_name == github.repository) }}
uses: 'dorny/test-reporter@dc3a92680fcc15842eef52e8c4606ea7ce6bd3f3' # ratchet:dorny/test-reporter@v2
with:
name: 'Test Results (Node ${{ runner.os }}, ${{ matrix.node-version }}, ${{ matrix.shard }})'
name: 'Test Results (Node ${{ matrix.node-version }})'
path: 'packages/*/junit.xml'
reporter: 'java-junit'
fail-on-error: 'false'
@@ -195,13 +172,14 @@ jobs:
${{ always() && (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }}
uses: 'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02' # ratchet:actions/upload-artifact@v4
with:
name: 'test-results-fork-${{ runner.os }}-${{ matrix.node-version }}-${{ matrix.shard }}'
name: 'test-results-fork-${{ matrix.node-version }}-${{ runner.os }}'
path: 'packages/*/junit.xml'
test_mac:
name: 'Test (Mac) - ${{ matrix.node-version }}, ${{ matrix.shard }}'
runs-on: 'macos-latest'
name: 'Test (Mac)'
runs-on: '${{ matrix.os }}'
needs:
- 'lint'
- 'merge_queue_skipper'
if: "${{needs.merge_queue_skipper.outputs.skip == 'false'}}"
permissions:
@@ -211,13 +189,12 @@ jobs:
continue-on-error: true
strategy:
matrix:
os:
- 'macos-latest'
node-version:
- '20.x'
- '22.x'
- '24.x'
shard:
- 'cli'
- 'others'
steps:
- name: 'Checkout'
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
@@ -237,14 +214,7 @@ jobs:
- name: 'Run tests and generate reports'
env:
NO_COLOR: true
run: |
if [[ "${{ matrix.shard }}" == "cli" ]]; then
npm run test:ci --workspace @google/gemini-cli -- --coverage.enabled=false
else
# Explicitly list non-cli packages to ensure they are sharded correctly
npm run test:ci --workspace @google/gemini-cli-core --workspace @google/gemini-cli-a2a-server --workspace gemini-cli-vscode-ide-companion --workspace @google/gemini-cli-test-utils --if-present -- --coverage.enabled=false
npm run test:scripts
fi
run: 'npm run test:ci -- --coverage.enabled=false'
- name: 'Bundle'
run: 'npm run bundle'
@@ -260,7 +230,7 @@ jobs:
${{ always() && (github.event.pull_request.head.repo.full_name == github.repository) }}
uses: 'dorny/test-reporter@dc3a92680fcc15842eef52e8c4606ea7ce6bd3f3' # ratchet:dorny/test-reporter@v2
with:
name: 'Test Results (Node ${{ runner.os }}, ${{ matrix.node-version }}, ${{ matrix.shard }})'
name: 'Test Results (Node ${{ matrix.node-version }})'
path: 'packages/*/junit.xml'
reporter: 'java-junit'
fail-on-error: 'false'
@@ -270,7 +240,7 @@ jobs:
${{ always() && (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }}
uses: 'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02' # ratchet:actions/upload-artifact@v4
with:
name: 'test-results-fork-${{ runner.os }}-${{ matrix.node-version }}-${{ matrix.shard }}'
name: 'test-results-fork-${{ matrix.node-version }}-${{ runner.os }}'
path: 'packages/*/junit.xml'
- name: 'Upload coverage reports'
@@ -278,7 +248,7 @@ jobs:
${{ always() }}
uses: 'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02' # ratchet:actions/upload-artifact@v4
with:
name: 'coverage-reports-${{ runner.os }}-${{ matrix.node-version }}-${{ matrix.shard }}'
name: 'coverage-reports-${{ matrix.node-version }}-${{ matrix.os }}'
path: 'packages/*/coverage'
codeql:
+247
View File
@@ -0,0 +1,247 @@
name: 'Testing: E2E'
on:
push:
branches:
- 'main'
# This will run for PRs from the base repository, providing secrets.
pull_request:
branches:
- 'main'
- 'release/**'
# This will run for PRs from forks when a label is added.
pull_request_target:
types: ['labeled']
merge_group:
workflow_dispatch:
inputs:
branch_ref:
description: 'Branch to run on'
required: true
default: 'main'
type: 'string'
concurrency:
group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}'
cancel-in-progress: |-
${{ github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/heads/release/') }}
jobs:
merge_queue_skipper:
name: 'Merge Queue Skipper'
runs-on: 'gemini-cli-ubuntu-16-core'
permissions: 'read-all'
outputs:
skip: '${{ steps.merge-queue-e2e-skipper.outputs.skip-check }}'
steps:
- id: 'merge-queue-e2e-skipper'
uses: 'cariad-tech/merge-queue-ci-skipper@1032489e59437862c90a08a2c92809c903883772' # ratchet:cariad-tech/merge-queue-ci-skipper@main
with:
secret: '${{ secrets.GITHUB_TOKEN }}'
continue-on-error: true
e2e_linux:
name: 'E2E Test (Linux) - ${{ matrix.sandbox }}'
needs: 'merge_queue_skipper'
if: |
(needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip == 'false') &&
(github.event_name == 'push' ||
github.event_name == 'merge_group' ||
github.event_name == 'workflow_dispatch' ||
(github.event.pull_request.head.repo.full_name == github.repository) ||
(github.event.label.name == 'maintainer:e2e:ok'))
runs-on: 'gemini-cli-ubuntu-16-core'
strategy:
fail-fast: false
matrix:
sandbox:
- 'sandbox:none'
- 'sandbox:docker'
node-version:
- '20.x'
steps:
- name: 'Checkout (fork)'
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
if: "github.event_name == 'pull_request_target'"
with:
ref: '${{ github.event.pull_request.head.sha }}'
repository: '${{ github.event.pull_request.head.repo.full_name }}'
- name: 'Checkout (internal)'
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
if: "github.event_name != 'pull_request_target'"
with:
ref: '${{ github.event.inputs.branch_ref || github.ref }}'
- name: 'Set up Node.js ${{ matrix.node-version }}'
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
with:
node-version: '${{ matrix.node-version }}'
- name: 'Install dependencies'
run: 'npm ci'
- name: 'Build project'
run: 'npm run build'
- name: 'Set up Docker'
if: "matrix.sandbox == 'sandbox:docker'"
uses: 'docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435' # ratchet:docker/setup-buildx-action@v3
- name: 'Run E2E tests'
env:
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
KEEP_OUTPUT: 'true'
VERBOSE: 'true'
shell: 'bash'
run: |
if [[ "${{ matrix.sandbox }}" == "sandbox:docker" ]]; then
npm run test:integration:sandbox:docker
else
npm run test:integration:sandbox:none
fi
e2e_mac:
name: 'E2E Test (macOS)'
needs: 'merge_queue_skipper'
if: |
(needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip == 'false') &&
(github.event_name == 'push' ||
github.event_name == 'merge_group' ||
github.event_name == 'workflow_dispatch' ||
(github.event.pull_request.head.repo.full_name == github.repository) ||
(github.event.label.name == 'maintainer:e2e:ok'))
runs-on: 'macos-latest'
steps:
- name: 'Checkout (fork)'
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
if: "github.event_name == 'pull_request_target'"
with:
ref: '${{ github.event.pull_request.head.sha }}'
repository: '${{ github.event.pull_request.head.repo.full_name }}'
- name: 'Checkout (internal)'
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
if: "github.event_name != 'pull_request_target'"
with:
ref: '${{ github.event.inputs.branch_ref || github.ref }}'
- name: 'Set up Node.js 20.x'
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
with:
node-version: '20.x'
- name: 'Install dependencies'
run: 'npm ci'
- name: 'Build project'
run: 'npm run build'
- name: 'Fix rollup optional dependencies on macOS'
if: "runner.os == 'macOS'"
run: |
npm cache clean --force
- name: 'Run E2E tests (non-Windows)'
if: "runner.os != 'Windows'"
env:
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
KEEP_OUTPUT: 'true'
SANDBOX: 'sandbox:none'
VERBOSE: 'true'
run: 'npm run test:integration:sandbox:none'
e2e_windows:
name: 'Slow E2E - Win'
needs: 'merge_queue_skipper'
if: |
(needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip == 'false') &&
(github.event_name == 'push' ||
github.event_name == 'merge_group' ||
github.event_name == 'workflow_dispatch' ||
(github.event.pull_request.head.repo.full_name == github.repository) ||
(github.event.label.name == 'maintainer:e2e:ok'))
runs-on: 'gemini-cli-windows-16-core'
continue-on-error: true
steps:
- name: 'Checkout (fork)'
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
if: "github.event_name == 'pull_request_target'"
with:
ref: '${{ github.event.pull_request.head.sha }}'
repository: '${{ github.event.pull_request.head.repo.full_name }}'
- name: 'Checkout (internal)'
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
if: "github.event_name != 'pull_request_target'"
with:
ref: '${{ github.event.inputs.branch_ref || github.ref }}'
- name: 'Set up Node.js 20.x'
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
with:
node-version: '20.x'
cache: 'npm'
- name: 'Configure Windows Defender exclusions'
run: |
Add-MpPreference -ExclusionPath $env:GITHUB_WORKSPACE -Force
Add-MpPreference -ExclusionPath "$env:GITHUB_WORKSPACE\node_modules" -Force
Add-MpPreference -ExclusionPath "$env:GITHUB_WORKSPACE\packages" -Force
Add-MpPreference -ExclusionPath "$env:TEMP" -Force
shell: 'pwsh'
- name: 'Configure npm for Windows performance'
run: |
npm config set progress false
npm config set audit false
npm config set fund false
npm config set loglevel error
npm config set maxsockets 32
npm config set registry https://registry.npmjs.org/
shell: 'pwsh'
- name: 'Install dependencies'
run: 'npm ci'
shell: 'pwsh'
- name: 'Build project'
run: 'npm run build'
shell: 'pwsh'
- name: 'Run E2E tests'
env:
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
KEEP_OUTPUT: 'true'
SANDBOX: 'sandbox:none'
VERBOSE: 'true'
NODE_OPTIONS: '--max-old-space-size=32768 --max-semi-space-size=256'
UV_THREADPOOL_SIZE: '32'
NODE_ENV: 'test'
shell: 'pwsh'
run: 'npm run test:integration:sandbox:none'
e2e:
name: 'E2E'
if: |
always() && (
github.event_name == 'push' ||
github.event_name == 'merge_group' ||
github.event_name == 'workflow_dispatch' ||
(github.event.pull_request.head.repo.full_name == github.repository) ||
(github.event.label.name == 'maintainer:e2e:ok')
)
needs:
- 'e2e_linux'
- 'e2e_mac'
runs-on: 'gemini-cli-ubuntu-16-core'
steps:
- name: 'Check E2E test results'
run: |
if [[ (${{ needs.e2e_linux.result }} != 'success' && ${{ needs.e2e_linux.result }} != 'skipped') || \
(${{ needs.e2e_mac.result }} != 'success' && ${{ needs.e2e_mac.result }} != 'skipped') ]]; then
echo "One or more E2E jobs failed."
exit 1
fi
echo "All required E2E jobs passed!"
-101
View File
@@ -1,101 +0,0 @@
name: 'Evals: Nightly'
on:
schedule:
- cron: '0 1 * * *' # Runs at 1 AM every day
workflow_dispatch:
inputs:
run_all:
description: 'Run all evaluations (including usually passing)'
type: 'boolean'
default: true
test_name_pattern:
description: 'Test name pattern or file name'
required: false
type: 'string'
permissions:
contents: 'read'
checks: 'write'
actions: 'read'
jobs:
evals:
name: 'Evals (USUALLY_PASSING) nightly run'
runs-on: 'gemini-cli-ubuntu-16-core'
strategy:
fail-fast: false
matrix:
model:
- 'gemini-3-pro-preview'
- 'gemini-3-flash-preview'
- 'gemini-2.5-pro'
- 'gemini-2.5-flash'
- 'gemini-2.5-flash-lite'
run_attempt: [1, 2, 3]
steps:
- name: 'Checkout'
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
- name: 'Set up Node.js'
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
- name: 'Install dependencies'
run: 'npm ci'
- name: 'Build project'
run: 'npm run build'
- name: 'Create logs directory'
run: 'mkdir -p evals/logs'
- name: 'Run Evals'
continue-on-error: true
env:
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
GEMINI_MODEL: '${{ matrix.model }}'
RUN_EVALS: "${{ github.event.inputs.run_all != 'false' }}"
TEST_NAME_PATTERN: '${{ github.event.inputs.test_name_pattern }}'
run: |
CMD="npm run test:all_evals"
PATTERN="${{ env.TEST_NAME_PATTERN }}"
if [[ -n "$PATTERN" ]]; then
if [[ "$PATTERN" == *.ts || "$PATTERN" == *.js || "$PATTERN" == */* ]]; then
$CMD -- "$PATTERN"
else
$CMD -- -t "$PATTERN"
fi
else
$CMD
fi
- name: 'Upload Logs'
if: 'always()'
uses: 'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02' # ratchet:actions/upload-artifact@v4
with:
name: 'eval-logs-${{ matrix.model }}-${{ matrix.run_attempt }}'
path: 'evals/logs'
retention-days: 7
aggregate-results:
name: 'Aggregate Results'
needs: ['evals']
if: 'always()'
runs-on: 'gemini-cli-ubuntu-16-core'
steps:
- name: 'Checkout'
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
- name: 'Download Logs'
uses: 'actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806' # ratchet:actions/download-artifact@v4
with:
path: 'artifacts'
- name: 'Generate Summary'
env:
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
run: 'node scripts/aggregate_evals.js artifacts >> "$GITHUB_STEP_SUMMARY"'
@@ -101,6 +101,7 @@ jobs:
"FIRESTORE_DATABASE_ID": "(default)",
"GOOGLE_APPLICATION_CREDENTIALS": "${GOOGLE_APPLICATION_CREDENTIALS}"
},
"enabled": true,
"timeout": 600000
}
},
@@ -14,15 +14,9 @@ on:
description: 'issue number to triage'
required: true
type: 'number'
workflow_call:
inputs:
issue_number:
description: 'issue number to triage'
required: false
type: 'string'
concurrency:
group: '${{ github.workflow }}-${{ github.event.issue.number || github.event.inputs.issue_number || inputs.issue_number }}'
group: '${{ github.workflow }}-${{ github.event.issue.number || github.event.inputs.issue_number }}'
cancel-in-progress: true
defaults:
@@ -40,18 +34,20 @@ permissions:
jobs:
triage-issue:
if: |-
(github.repository == 'google-gemini/gemini-cli' || github.repository == 'google-gemini/maintainers-gemini-cli') &&
github.repository == 'google-gemini/gemini-cli' &&
(
github.event_name == 'workflow_dispatch' ||
(
(github.event_name == 'issues' || github.event_name == 'issue_comment') &&
contains(github.event.issue.labels.*.name, 'status/need-triage') &&
(github.event_name != 'issue_comment' || (
contains(github.event.comment.body, '@gemini-cli /triage') &&
(github.event.comment.author_association == 'OWNER' || github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'COLLABORATOR')
))
)
) &&
!contains(github.event.issue.labels.*.name, 'area/')
!contains(github.event.issue.labels.*.name, 'area/') &&
!contains(github.event.issue.labels.*.name, 'priority/')
timeout-minutes: 5
runs-on: 'ubuntu-latest'
steps:
@@ -63,11 +59,10 @@ jobs:
with:
github-token: '${{ secrets.GITHUB_TOKEN }}'
script: |
const issueNumber = ${{ github.event.inputs.issue_number || inputs.issue_number }};
const { data: issue } = await github.rest.issues.get({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
issue_number: ${{ github.event.inputs.issue_number }},
});
core.setOutput('title', issue.title);
core.setOutput('body', issue.body);
@@ -78,11 +73,16 @@ jobs:
if: |-
github.event_name == 'workflow_dispatch'
env:
ISSUE_NUMBER_INPUT: '${{ github.event.inputs.issue_number || inputs.issue_number }}'
ISSUE_NUMBER_INPUT: '${{ github.event.inputs.issue_number }}'
LABELS: '${{ steps.get_issue_data.outputs.labels }}'
run: |
if echo "${LABELS}" | grep -q 'area/'; then
echo "Issue #${ISSUE_NUMBER_INPUT} already has 'area/' label. Stopping workflow."
if ! echo "${LABELS}" | grep -q 'status/need-triage'; then
echo "Issue #${ISSUE_NUMBER_INPUT} does not have the 'status/need-triage' label. Stopping workflow."
exit 1
fi
if echo "${LABELS}" | grep -q 'area/' || echo "${LABELS}" | grep -q 'priority/'; then
echo "Issue #${ISSUE_NUMBER_INPUT} already has 'area/' or 'priority/' labels. Stopping workflow."
exit 1
fi
@@ -93,10 +93,6 @@ jobs:
- name: 'Generate GitHub App Token'
id: 'generate_token'
env:
APP_ID: '${{ secrets.APP_ID }}'
if: |-
${{ env.APP_ID != '' }}
uses: 'actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b' # ratchet:actions/create-github-app-token@v2
with:
app-id: '${{ secrets.APP_ID }}'
@@ -107,13 +103,18 @@ jobs:
id: 'get_labels'
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
with:
github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
github-token: '${{ steps.generate_token.outputs.token }}'
script: |-
const { data: labels } = await github.rest.issues.listLabelsForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
});
const allowedLabels = [
'priority/p0',
'priority/p1',
'priority/p2',
'priority/p3',
'priority/unknown',
'area/agent',
'area/enterprise',
'area/non-interactive',
@@ -138,7 +139,7 @@ jobs:
ISSUE_BODY: >-
${{ github.event_name == 'workflow_dispatch' && steps.get_issue_data.outputs.body || github.event.issue.body }}
ISSUE_NUMBER: >-
${{ github.event_name == 'workflow_dispatch' && (github.event.inputs.issue_number || inputs.issue_number) || github.event.issue.number }}
${{ github.event_name == 'workflow_dispatch' && github.event.inputs.issue_number || github.event.issue.number }}
REPOSITORY: '${{ github.repository }}'
AVAILABLE_LABELS: '${{ steps.get_labels.outputs.available_labels }}'
with:
@@ -160,19 +161,21 @@ jobs:
prompt: |-
## Role
You are an issue triage assistant. Your role is to analyze a GitHub issue and determine the single most appropriate area/ label based on the definitions provided.
You are an issue triage assistant. Your role is to analyze a GitHub issue and determine the single most appropriate area/ label and the single most appropriate priority/ label based on the definitions provided.
## Steps
1. Review the issue title and body: ${{ env.ISSUE_TITLE }} and ${{ env.ISSUE_BODY }}.
2. Review the available labels: ${{ env.AVAILABLE_LABELS }}.
3. Select exactly one area/ label that best matches the issue based on Reference 1: Area Definitions.
4. Fallback Logic:
4. Select exactly one priority/ label that best matches the issue based on Reference 2: Priority Definitions.
5. Fallback Logic:
- If you cannot confidently determine the correct area/ label from the definitions, you must use area/unknown.
5. Output your selected label in JSON format and nothing else. Example:
{"labels_to_set": ["area/core"]}
- If you cannot confidently determine the correct priority/ label from the definitions, you must use priority/unknown.
6. Output your two selected labels in JSON format and nothing else. Example:
{"labels_to_set": ["area/core", "priority/p1"]}
## Guidelines
- Your output must contain exactly one area/ label.
- Your output must contain exactly one area/ label and exactly one priority/ label.
- Triage only the current issue based on its title and body.
- Output only valid JSON format.
- Do not include any explanation or additional text, just the JSON.
@@ -255,6 +258,38 @@ jobs:
area/unknown
- Description: Issues that do not clearly fit into any other defined area/ category, or where information is too limited to make a determination. Use this when no other area is appropriate.
Reference 2: Priority Definitions
priority/p0: Critical / Blocker
- Definition: A catastrophic failure that makes the CLI unusable for most users or poses a severe security risk. This includes installation failures, authentication failures, persistent crashes, or critical security vulnerabilities.
- Key Questions:
- Is the CLI failing to install or run?
- Does it fail to authenticate or connect to the Gemini API, making all commands useless?
- Is it consistently crashing on basic, common commands?
- Does this represent a critical security vulnerability?
priority/p1: High
- Definition: A severe issue where a core feature (e.g., text generation, code generation, file processing) is unusable, failing, has severe performance degradation, or providing fundamentally incorrect output formatting (e.g., truncated text, broken JSON). Affects many users, and there is no reasonable workaround.
- Key Questions:
- Is a core feature failing for a specific, large user group (e.g., all Windows users, all users of a specific shell)?
- Is the CLI failing to process a supported input or misinterpreting critical flags?
- Is the CLI's output formatting consistently broken, making the response unusable?
- Is a core command or feature extremely slow, making it impractical to use?
priority/p2: Medium
- Definition: A moderately impactful issue causing inconvenience or a non-optimal experience, but a reasonable workaround exists. This also includes failures in non-core features.
- Key Questions:
- Is a command or flag behaving incorrectly, but the user can achieve their goal via other means?
- Is there a significant, non-blocking UI/UX problem in the terminal (e.g., broken progress indicators, bad terminal coloring)?
priority/p3: Low
- Definition: A minor, low-impact issue with minimal effect on functionality. This includes most cosmetic defects, typos in documentation, or unclear help text.
- Key Questions:
- Is this a typo in the README.md, gemini --help text, or other documentation?
- Is this a minor cosmetic issue (e.g., text alignment in output, an extra newline) that doesn't affect usability?
priority/unknown
- Description: Issues that do not clearly fit into any other defined priority/ category, or where information is too limited to make a determination. Use this when no other priority is appropriate.
- name: 'Apply Labels to Issue'
if: |-
${{ steps.gemini_issue_analysis.outputs.summary != '' }}
@@ -264,7 +299,7 @@ jobs:
LABELS_OUTPUT: '${{ steps.gemini_issue_analysis.outputs.summary }}'
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
with:
github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
github-token: '${{ steps.generate_token.outputs.token }}'
script: |
const rawOutput = process.env.LABELS_OUTPUT;
core.info(`Raw output from model: ${rawOutput}`);
@@ -292,8 +327,8 @@ jobs:
const issueNumber = parseInt(process.env.ISSUE_NUMBER);
const labelsToAdd = parsedLabels.labels_to_set || [];
if (labelsToAdd.length !== 1) {
core.setFailed(`Expected exactly 1 label (area/), but got ${labelsToAdd.length}. Labels: ${labelsToAdd.join(', ')}`);
if (labelsToAdd.length !== 2) {
core.setFailed(`Expected exactly 2 labels (one area/ and one priority/), but got ${labelsToAdd.length}. Labels: ${labelsToAdd.join(', ')}`);
return;
}
@@ -306,6 +341,22 @@ jobs:
});
core.info(`Successfully added labels for #${issueNumber}: ${labelsToAdd.join(', ')}`);
// Remove the 'status/need-triage' label
try {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
name: 'status/need-triage'
});
core.info(`Successfully removed 'status/need-triage' label.`);
} catch (error) {
// If the label doesn't exist, the API call will throw a 404. We can ignore this.
if (error.status !== 404) {
core.warning(`Failed to remove 'status/need-triage': ${error.message}`);
}
}
- name: 'Post Issue Analysis Failure Comment'
if: |-
${{ failure() && steps.gemini_issue_analysis.outcome == 'failure' }}
@@ -314,7 +365,7 @@ jobs:
RUN_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
with:
github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
github-token: '${{ steps.generate_token.outputs.token }}'
script: |-
github.rest.issues.createComment({
owner: context.repo.owner,
@@ -81,6 +81,7 @@ jobs:
"FIRESTORE_DATABASE_ID": "(default)",
"GOOGLE_APPLICATION_CREDENTIALS": "${GOOGLE_APPLICATION_CREDENTIALS}"
},
"enabled": true,
"timeout": 600000
}
},
@@ -1,16 +1,12 @@
name: '📋 Gemini Scheduled Issue Triage'
on:
issues:
types:
- 'opened'
- 'reopened'
schedule:
- cron: '0 * * * *' # Runs every hour
workflow_dispatch:
concurrency:
group: '${{ github.workflow }}-${{ github.event.number || github.run_id }}'
group: '${{ github.workflow }}'
cancel-in-progress: true
defaults:
@@ -39,21 +35,7 @@ jobs:
private-key: '${{ secrets.PRIVATE_KEY }}'
permission-issues: 'write'
- name: 'Get issue from event'
if: |-
${{ github.event_name == 'issues' }}
id: 'get_issue_from_event'
env:
ISSUE_EVENT: '${{ toJSON(github.event.issue) }}'
run: |
set -euo pipefail
ISSUE_JSON=$(echo "$ISSUE_EVENT" | jq -c '[{number: .number, title: .title, body: .body}]')
echo "issues_to_triage=${ISSUE_JSON}" >> "${GITHUB_OUTPUT}"
echo "✅ Found issue #${{ github.event.issue.number }} from event to triage! 🎯"
- name: 'Find untriaged issues'
if: |-
${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }}
id: 'find_issues'
env:
GITHUB_TOKEN: '${{ steps.generate_token.outputs.token }}'
@@ -61,26 +43,22 @@ jobs:
run: |-
set -euo pipefail
echo '🔍 Finding issues missing area labels...'
NO_AREA_ISSUES="$(gh issue list --repo "${GITHUB_REPOSITORY}" \
--search 'is:open is:issue -label:area/core -label:area/agent -label:area/enterprise -label:area/non-interactive -label:area/security -label:area/platform -label:area/extensions -label:area/unknown' --limit 100 --json number,title,body)"
echo '🔍 Finding issues without labels...'
NO_LABEL_ISSUES="$(gh issue list --repo "${GITHUB_REPOSITORY}" \
--search 'is:open is:issue no:label' --json number,title,body)"
echo '🔍 Finding issues missing kind labels...'
NO_KIND_ISSUES="$(gh issue list --repo "${GITHUB_REPOSITORY}" \
--search 'is:open is:issue -label:kind/bug -label:kind/enhancement -label:kind/customer-issue -label:kind/question' --limit 100 --json number,title,body)"
echo '🏷️ Finding issues missing priority labels...'
NO_PRIORITY_ISSUES="$(gh issue list --repo "${GITHUB_REPOSITORY}" \
--search 'is:open is:issue -label:priority/p0 -label:priority/p1 -label:priority/p2 -label:priority/p3 -label:priority/unknown' --limit 100 --json number,title,body)"
echo '🏷️ Finding issues that need triage...'
NEED_TRIAGE_ISSUES="$(gh issue list --repo "${GITHUB_REPOSITORY}" \
--search "is:open is:issue label:\"status/need-triage\"" --limit 1000 --json number,title,body)"
echo '🔄 Merging and deduplicating issues...'
ISSUES="$(echo "${NO_AREA_ISSUES}" "${NO_KIND_ISSUES}" "${NO_PRIORITY_ISSUES}" | jq -c -s 'add | unique_by(.number)')"
ISSUES="$(echo "${NO_LABEL_ISSUES}" "${NEED_TRIAGE_ISSUES}" | jq -c -s 'add | unique_by(.number)')"
echo '📝 Setting output for GitHub Actions...'
echo "issues_to_triage=${ISSUES}" >> "${GITHUB_OUTPUT}"
ISSUE_COUNT="$(echo "${ISSUES}" | jq 'length')"
echo "✅ Found ${ISSUE_COUNT} unique issues to triage! 🎯"
echo "✅ Found ${ISSUE_COUNT} issues to triage! 🎯"
- name: 'Get Repository Labels'
id: 'get_labels'
@@ -99,13 +77,12 @@ jobs:
- name: 'Run Gemini Issue Analysis'
if: |-
(steps.get_issue_from_event.outputs.issues_to_triage != '' && steps.get_issue_from_event.outputs.issues_to_triage != '[]') ||
(steps.find_issues.outputs.issues_to_triage != '' && steps.find_issues.outputs.issues_to_triage != '[]')
${{ steps.find_issues.outputs.issues_to_triage != '[]' }}
uses: 'google-github-actions/run-gemini-cli@a3bf79042542528e91937b3a3a6fbc4967ee3c31' # ratchet:google-github-actions/run-gemini-cli@v0
id: 'gemini_issue_analysis'
env:
GITHUB_TOKEN: '' # Do not pass any auth token here since this runs on untrusted inputs
ISSUES_TO_TRIAGE: '${{ steps.get_issue_from_event.outputs.issues_to_triage || steps.find_issues.outputs.issues_to_triage }}'
ISSUES_TO_TRIAGE: '${{ steps.find_issues.outputs.issues_to_triage }}'
REPOSITORY: '${{ github.repository }}'
AVAILABLE_LABELS: '${{ steps.get_labels.outputs.available_labels }}'
with:
@@ -139,32 +116,38 @@ jobs:
1. You are only able to use the echo command. Review the available labels in the environment variable: "${AVAILABLE_LABELS}".
2. Check environment variable for issues to triage: $ISSUES_TO_TRIAGE (JSON array of issues)
3. Review the issue title, body and any comments provided in the environment variables.
4. Identify the most relevant labels from the existing labels, specifically focusing on area/*, kind/* and priority/*.
5. Label Policy:
- If the issue already has a kind/ label, do not change it.
- If the issue already has a priority/ label, do not change it.
- If the issue already has an area/ label, do not change it.
- If any of these are missing, select exactly ONE appropriate label for the missing category.
4. Identify the most relevant labels from the existing labels, focusing on kind/*, area/*, sub-area/* and priority/*.
5. If the issue already has area/ label, dont try to change it. Similarly, if the issue already has a kind/ label don't change it. And if the issue already has a priority/ label do not change it for example:
If an issue has area/core and kind/bug you will only add a priority/ label.
Instead if an issue has no labels, you will could add one lable of each kind.
6. Identify other applicable labels based on the issue content, such as status/*, help wanted, good first issue, etc.
7. Give me a single short explanation about why you are selecting each label in the process.
8. Output a JSON array of objects, each containing the issue number
7. For area/* and kind/* limit yourself to only the single most applicable label in each case.
8. Give me a single short explanation about why you are selecting each label in the process.
9. Output a JSON array of objects, each containing the issue number
and the labels to add and remove, along with an explanation. For example:
```
[
{
"issue_number": 123,
"labels_to_add": ["area/core", "kind/bug", "priority/p2"],
"labels_to_add": ["kind/bug", "priority/p2"],
"labels_to_remove": ["status/need-triage"],
"explanation": "This issue is a UI bug that needs to be addressed with medium priority."
"explanation": "This issue is a bug that needs to be addressed with medium priority."
},
{
"issue_number": 456,
"labels_to_add": ["kind/enhancement"],
"labels_to_remove": [],
"explanation": "This issue is an enhancement request that could improve the user experience."
}
]
```
If an issue cannot be classified, do not include it in the output array.
9. For each issue please check if CLI version is present, this is usually in the output of the /about command and will look like 0.1.5
10. For each issue please check if CLI version is present, this is usually in the output of the /about command and will look like 0.1.5
- Anything more than 6 versions older than the most recent should add the status/need-retesting label
10. If you see that the issue doesn't look like it has sufficient information recommend the status/need-information label and leave a comment politely requesting the relevant information, eg.. if repro steps are missing request for repro steps. if version information is missing request for version information into the explanation section below.
11. If you think an issue might be a Priority/P0 do not apply the priority/p0 label. Instead apply a status/manual-triage label and include a note in your explanation.
12. If you are uncertain about a category, use the area/unknown, kind/question, or priority/unknown labels as appropriate. If you are extremely uncertain, apply the status/manual-triage label.
11. If you see that the issue doesn't look like it has sufficient information recommend the status/need-information label and leave a comment politely requesting the relevant information, eg.. if repro steps are missing request for repro steps. if version information is missing request for version information into the explanation section below.
- After identifying appropriate labels to an issue, add "status/need-triage" label to labels_to_remove in the output.
12. If you think an issue might be a Priority/P0 do not apply the priority/p0 label. Instead apply a status/manual-triage label and include a note in your explanation.
13. If you are uncertain and have not been able to apply one each of kind/, area/ and priority/ , apply the status/manual-triage label.
## Guidelines
@@ -174,46 +157,100 @@ jobs:
- Do not add comments or modify the issue content.
- Do not remove the following labels maintainer, help wanted or good first issue.
- Triage only the current issue.
- Identify only one area/ label.
- Identify only one area/ label
- Identify only one kind/ label (Do not apply kind/duplicate or kind/parent-issue)
- Identify only one priority/ label.
- Identify all applicable sub-area/* and priority/* labels based on the issue content. It's ok to have multiple of these.
- Once you categorize the issue if it needs information bump down the priority by 1 eg.. a p0 would become a p1 a p1 would become a p2. P2 and P3 can stay as is in this scenario.
Categorization Guidelines (Priority):
P0 - Urgent Blocking Issues:
- DO NOT APPLY THIS LABEL AUTOMATICALLY. Use status/manual-triage instead.
- Definition: Urgent, block a significant percentage of the user base, and prevent frequent use of the Gemini CLI.
- This includes core stability blockers (e.g., authentication failures, broken upgrades), critical crashes, and P0 security vulnerabilities.
- Impact: Blocks development or testing for the entire team; Major security vulnerability; Causes data loss or corruption with no workaround; Crashes the application or makes a core feature completely unusable for all or most users.
- Qualifier: Is the main function of the software broken?
P1 - High-Impact Issues:
- Definition: Affect a large number of users, blocking them from using parts of the Gemini CLI, or make the CLI frequently unusable even with workarounds available.
- Impact: A core feature is broken or behaving incorrectly for a large number of users or use cases; Severe performance degradation; No straightforward workaround exists.
- Qualifier: Is a key feature unusable or giving very wrong results?
P2 - Significant Issues:
- Definition: Affect some users significantly, such as preventing the use of certain features or authentication types.
- Can also be issues that many users complain about, causing annoyance or hindering daily use.
- Impact: Affects a non-critical feature or a smaller, specific subset of users; An inconvenient but functional workaround is available; Noticeable UI/UX problems that look unprofessional.
- Qualifier: Is it an annoying but non-blocking problem?
P3 - Low-Impact Issues:
- Definition: Typically usability issues that cause annoyance to a limited user base.
- Includes feature requests that could be addressed in the near future and may be suitable for community contributions.
- Impact: Minor cosmetic issues; An edge-case bug that is very difficult to reproduce and affects a tiny fraction of users.
- Qualifier: Is it a "nice-to-fix" issue?
Categorization Guidelines (Area):
area/agent: Core Agent, Tools, Memory, Sub-Agents, Hooks, Agent Quality
area/core: User Interface, OS Support, Core Functionality
area/enterprise: Telemetry, Policy, Quota / Licensing
area/extensions: Gemini CLI extensions capability
area/non-interactive: GitHub Actions, SDK, 3P Integrations, Shell Scripting, Command line automation
area/platform: Build infra, Release mgmt, Testing, Eval infra, Capacity, Quota mgmt
area/security: security related issues
Categorization Guidelines:
P0: Critical / Blocker
- A P0 bug is a catastrophic failure that demands immediate attention.
- To be a P0 it means almost all users are running into this issue and it is blocking users from being able to use the product.
- You would see this in the form of many comments from different developers on the bug.
- It represents a complete showstopper for a significant portion of users or for the development process itself.
Impact:
- Blocks development or testing for the entire team.
- Major security vulnerability that could compromise user data or system integrity.
- Causes data loss or corruption with no workaround.
- Crashes the application or makes a core feature completely unusable for all or most users in a production environment. Will it cause severe quality degration?
- Is it preventing contributors from contributing to the repository or is it a release blocker?
Qualifier: Is the main function of the software broken?
Example: The gemini auth login command fails with an unrecoverable error, preventing any user from authenticating and using the rest of the CLI.
P1: High
- A P1 bug is a serious issue that significantly degrades the user experience or impacts a core feature.
- While not a complete blocker, it's a major problem that needs a fast resolution. Feature requests are almost never P1.
- Once again this would be affecting many users.
- You would see this in the form of comments from different developers on the bug.
Impact:
- A core feature is broken or behaving incorrectly for a large number of users or large number of use cases.
- Review the bug details and comments to try figure out if this issue affects a large set of use cases or if it's a narrow set of use cases.
- Severe performance degradation making the application frustratingly slow.
- No straightforward workaround exists, or the workaround is difficult and non-obvious.
Qualifier: Is a key feature unusable or giving very wrong results?
Example: Gemini CLI enters a loop when making read-many-files tool call. I am unable to break out of the loop and gemini doesn't follow instructions subsequently.
P2: Medium
- A P2 bug is a moderately impactful issue. It's a noticeable problem but doesn't prevent the use of the software's main functionality.
Impact:
- Affects a non-critical feature or a smaller, specific subset of users.
- An inconvenient but functional workaround is available and easy to execute.
- Noticeable UI/UX problems that don't break functionality but look unprofessional (e.g., elements are misaligned or overlapping).
Qualifier: Is it an annoying but non-blocking problem?
Example: An error message is unclear or contains a typo, causing user confusion but not halting their workflow.
P3: Low
- A P3 bug is a minor, low-impact issue that is trivial or cosmetic. It has little to no effect on the overall functionality of the application.
Impact:
- Minor cosmetic issues like color inconsistencies, typos in documentation, or slight alignment problems on a non-critical page.
- An edge-case bug that is very difficult to reproduce and affects a tiny fraction of users.
Qualifier: Is it a "nice-to-fix" issue?
Example: Spelling mistakes etc.
Additional Context:
- If users are talking about issues where the model gets downgraded from pro to flash then i want you to categorize that as a performance issue.
- This product is designed to use different models eg.. using pro, downgrading to flash etc.
- When users report that they dont expect the model to change those would be categorized as feature requests.
- If users are talking about issues where the model gets downgraded from pro to flash then i want you to categorize that as a performance issue
- This product is designed to use different models eg.. using pro, downgrading to flash etc.
- When users report that they dont expect the model to change those would be categorized as feature requests.
Definition of Areas
area/ux:
- Issues concerning user-facing elements like command usability, interactive features, help docs, and perceived performance.
- I am seeing my screen flicker when using Gemini CLI
- I am seeing the output malformed
- Theme changes aren't taking effect
- My keyboard inputs arent' being recognzied
area/platform:
- Issues related to installation, packaging, OS compatibility (Windows, macOS, Linux), and the underlying CLI framework.
area/background: Issues related to long-running background tasks, daemons, and autonomous or proactive agent features.
area/models:
- i am not getting a response that is reasonable or expected. this can include things like
- I am calling a tool and the tool is not performing as expected.
- i am expecting a tool to be called and it is not getting called ,
- Including experience when using
- built-in tools (e.g., web search, code interpreter, read file, writefile, etc..),
- Function calling issues should be under this area
- i am getting responses from the model that are malformed.
- Issues concerning Gemini quality of response and inference,
- Issues talking about unnecessary token consumption.
- Issues talking about Model getting stuck in a loop be watchful as this could be the root cause for issues that otherwise seem like model performance issues.
- Memory compression
- unexpected responses,
- poor quality of generated code
area/tools:
- These are primarily issues related to Model Context Protocol
- These are issues that mention MCP support
- feature requests asking for support for new tools.
area/core:
- Issues with fundamental components like command parsing, configuration management, session state, and the main API client logic. Introducing multi-modality
area/contribution:
- Issues related to improving the developer contribution experience, such as CI/CD pipelines, build scripts, and test automation infrastructure.
area/authentication:
- Issues related to user identity, login flows, API key handling, credential storage, and access token management, unable to sign in selecting wrong authentication path etc..
area/security-privacy:
- Issues concerning vulnerability patching, dependency security, data sanitization, privacy controls, and preventing unauthorized data access.
area/extensibility:
- Issues related to the plugin system, extension APIs, or making the CLI's functionality available in other applications, github actions, ide support etc..
area/performance:
- Issues focused on model performance
- Issues with running out of capacity,
- 429 errors etc..
- could also pertain to latency,
- other general software performance like, memory usage, CPU consumption, and algorithmic efficiency.
- Switching models from one to the other unexpectedly.
- name: 'Apply Labels to Issues'
if: |-
@@ -263,6 +300,24 @@ jobs:
core.info(`Successfully added labels for #${issueNumber}: ${labelsToAdd.join(', ')}${explanation}`);
}
if (entry.labels_to_remove && entry.labels_to_remove.length > 0) {
for (const label of entry.labels_to_remove) {
try {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
name: label
});
} catch (error) {
if (error.status !== 404) {
throw error;
}
}
}
core.info(`Successfully removed labels for #${issueNumber}: ${entry.labels_to_remove.join(', ')}`);
}
if (entry.explanation) {
await github.rest.issues.createComment({
owner: context.repo.owner,
@@ -39,7 +39,3 @@ jobs:
GITHUB_REPOSITORY: '${{ github.repository }}'
run: |-
./.github/scripts/pr-triage.sh
# If prs_needing_comment is empty, set it to [] explicitly for downstream steps
if [[ -z "$(grep 'prs_needing_comment' "${GITHUB_OUTPUT}" | cut -d'=' -f2-)" ]]; then
echo "prs_needing_comment=[]" >> "${GITHUB_OUTPUT}"
fi
@@ -1,158 +0,0 @@
name: '🔒 Gemini Scheduled Stale Issue Closer'
on:
schedule:
- cron: '0 0 * * 0' # Every Sunday at midnight UTC
workflow_dispatch:
inputs:
dry_run:
description: 'Run in dry-run mode (no changes applied)'
required: false
default: false
type: 'boolean'
concurrency:
group: '${{ github.workflow }}'
cancel-in-progress: true
defaults:
run:
shell: 'bash'
jobs:
close-stale-issues:
runs-on: 'ubuntu-latest'
permissions:
issues: 'write'
steps:
- name: 'Generate GitHub App Token'
id: 'generate_token'
uses: 'actions/create-github-app-token@v1'
with:
app-id: '${{ secrets.APP_ID }}'
private-key: '${{ secrets.PRIVATE_KEY }}'
permission-issues: 'write'
- name: 'Process Stale Issues'
uses: 'actions/github-script@v7'
env:
DRY_RUN: '${{ inputs.dry_run }}'
with:
github-token: '${{ steps.generate_token.outputs.token }}'
script: |
const dryRun = process.env.DRY_RUN === 'true';
if (dryRun) {
core.info('DRY RUN MODE ENABLED: No changes will be applied.');
}
const batchLabel = 'Stale';
const threeMonthsAgo = new Date();
threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3);
const tenDaysAgo = new Date();
tenDaysAgo.setDate(tenDaysAgo.getDate() - 10);
core.info(`Cutoff date for creation: ${threeMonthsAgo.toISOString()}`);
core.info(`Cutoff date for updates: ${tenDaysAgo.toISOString()}`);
const query = `repo:${context.repo.owner}/${context.repo.repo} is:issue is:open created:<${threeMonthsAgo.toISOString()}`;
core.info(`Searching with query: ${query}`);
const itemsToCheck = await github.paginate(github.rest.search.issuesAndPullRequests, {
q: query,
sort: 'created',
order: 'asc',
per_page: 100
});
core.info(`Found ${itemsToCheck.length} open issues to check.`);
let processedCount = 0;
for (const issue of itemsToCheck) {
const createdAt = new Date(issue.created_at);
const updatedAt = new Date(issue.updated_at);
const reactionCount = issue.reactions.total_count;
// Basic thresholds
if (reactionCount >= 5) {
continue;
}
// Skip if it has a maintainer, help wanted, or Public Roadmap label
const rawLabels = issue.labels.map((l) => l.name);
const lowercaseLabels = rawLabels.map((l) => l.toLowerCase());
if (
lowercaseLabels.some((l) => l.includes('maintainer')) ||
lowercaseLabels.includes('help wanted') ||
rawLabels.includes('🗓️ Public Roadmap')
) {
continue;
}
let isStale = updatedAt < tenDaysAgo;
// If apparently active, check if it's only bot activity
if (!isStale) {
try {
const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
per_page: 100,
sort: 'created',
direction: 'desc'
});
const lastHumanComment = comments.data.find(comment => comment.user.type !== 'Bot');
if (lastHumanComment) {
isStale = new Date(lastHumanComment.created_at) < tenDaysAgo;
} else {
// No human comments. Check if creator is human.
if (issue.user.type !== 'Bot') {
isStale = createdAt < tenDaysAgo;
} else {
isStale = true; // Bot created, only bot comments
}
}
} catch (error) {
core.warning(`Failed to fetch comments for issue #${issue.number}: ${error.message}`);
continue;
}
}
if (isStale) {
processedCount++;
const message = `Closing stale issue #${issue.number}: "${issue.title}" (${issue.html_url})`;
core.info(message);
if (!dryRun) {
// Add label
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: [batchLabel]
});
// Add comment
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: 'Hello! As part of our effort to keep our backlog manageable and focus on the most active issues, we are tidying up older reports.\n\nIt looks like this issue hasn\'t been active for a while, so we are closing it for now. However, if you are still experiencing this bug on the latest stable build, please feel free to comment on this issue or create a new one with updated details.\n\nThank you for your contribution!'
});
// Close issue
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
state: 'closed',
state_reason: 'not_planned'
});
}
}
}
core.info(`\nTotal issues processed: ${processedCount}`);
@@ -1,205 +0,0 @@
name: 'Gemini Scheduled Stale PR Closer'
on:
schedule:
- cron: '0 2 * * *' # Every day at 2 AM UTC
pull_request:
types: ['opened', 'edited']
workflow_dispatch:
inputs:
dry_run:
description: 'Run in dry-run mode'
required: false
default: false
type: 'boolean'
jobs:
close-stale-prs:
if: "github.repository == 'google-gemini/gemini-cli'"
runs-on: 'ubuntu-latest'
permissions:
pull-requests: 'write'
issues: 'write'
steps:
- name: 'Generate GitHub App Token'
id: 'generate_token'
uses: 'actions/create-github-app-token@v1'
with:
app-id: '${{ secrets.APP_ID }}'
private-key: '${{ secrets.PRIVATE_KEY }}'
owner: '${{ github.repository_owner }}'
repositories: 'gemini-cli'
- name: 'Process Stale PRs'
uses: 'actions/github-script@v7'
env:
DRY_RUN: '${{ inputs.dry_run }}'
with:
github-token: '${{ steps.generate_token.outputs.token }}'
script: |
const dryRun = process.env.DRY_RUN === 'true';
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
// 1. Fetch maintainers for verification
let maintainerLogins = new Set();
try {
const members = await github.paginate(github.rest.teams.listMembersInOrg, {
org: context.repo.owner,
team_slug: 'gemini-cli-maintainers'
});
maintainerLogins = new Set(members.map(m => m.login));
} catch (e) {
core.warning('Failed to fetch team members');
}
const isMaintainer = (login, assoc) => {
if (maintainerLogins.size > 0) return maintainerLogins.has(login);
return ['OWNER', 'MEMBER', 'COLLABORATOR'].includes(assoc);
};
// 2. Determine which PRs to check
let prs = [];
if (context.eventName === 'pull_request') {
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number
});
prs = [pr];
} else {
prs = await github.paginate(github.rest.pulls.list, {
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
per_page: 100
});
}
for (const pr of prs) {
const maintainerPr = isMaintainer(pr.user.login, pr.author_association);
const isBot = pr.user.type === 'Bot' || pr.user.login.endsWith('[bot]');
// Detection Logic for Linked Issues
// Check 1: Official GitHub "Closing Issue" link (GraphQL)
const linkedIssueQuery = `query($owner:String!, $repo:String!, $number:Int!) {
repository(owner:$owner, name:$repo) {
pullRequest(number:$number) {
closingIssuesReferences(first: 1) { totalCount }
}
}
}`;
let hasClosingLink = false;
try {
const res = await github.graphql(linkedIssueQuery, {
owner: context.repo.owner, repo: context.repo.repo, number: pr.number
});
hasClosingLink = res.repository.pullRequest.closingIssuesReferences.totalCount > 0;
} catch (e) {}
// Check 2: Regex for mentions (e.g., "Related to #123", "Part of #123", "#123")
// We check for # followed by numbers or direct URLs to issues.
const body = pr.body || '';
const mentionRegex = /(?:#|https:\/\/github\.com\/[^\/]+\/[^\/]+\/issues\/)(\d+)/i;
const hasMentionLink = mentionRegex.test(body);
const hasLinkedIssue = hasClosingLink || hasMentionLink;
// Logic for Closed PRs (Auto-Reopen)
if (pr.state === 'closed' && context.eventName === 'pull_request' && context.payload.action === 'edited') {
if (hasLinkedIssue) {
core.info(`PR #${pr.number} now has a linked issue. Reopening.`);
if (!dryRun) {
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number,
state: 'open'
});
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body: "Thank you for linking an issue! This pull request has been automatically reopened."
});
}
}
continue;
}
// Logic for Open PRs (Immediate Closure)
if (pr.state === 'open' && !maintainerPr && !hasLinkedIssue && !isBot) {
core.info(`PR #${pr.number} is missing a linked issue. Closing.`);
if (!dryRun) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body: "Hi there! Thank you for your contribution to Gemini CLI. \n\nTo improve our contribution process and better track changes, we now require all pull requests to be associated with an existing issue, as announced in our [recent discussion](https://github.com/google-gemini/gemini-cli/discussions/16706) and as detailed in our [CONTRIBUTING.md](https://github.com/google-gemini/gemini-cli/blob/main/CONTRIBUTING.md#1-link-to-an-existing-issue).\n\nThis pull request is being closed because it is not currently linked to an issue. **Once you have updated the description of this PR to link an issue (e.g., by adding `Fixes #123` or `Related to #123`), it will be automatically reopened.**\n\n**How to link an issue:**\nAdd a keyword followed by the issue number (e.g., `Fixes #123`) in the description of your pull request. For more details on supported keywords and how linking works, please refer to the [GitHub Documentation on linking pull requests to issues](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue).\n\nThank you for your understanding and for being a part of our community!"
});
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number,
state: 'closed'
});
}
continue;
}
// Staleness check (Scheduled runs only)
if (pr.state === 'open' && context.eventName !== 'pull_request') {
const labels = pr.labels.map(l => l.name.toLowerCase());
if (labels.includes('help wanted') || labels.includes('🔒 maintainer only')) continue;
let lastActivity = new Date(0);
try {
const reviews = await github.paginate(github.rest.pulls.listReviews, {
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number
});
for (const r of reviews) {
if (isMaintainer(r.user.login, r.author_association)) {
const d = new Date(r.submitted_at || r.updated_at);
if (d > lastActivity) lastActivity = d;
}
}
const comments = await github.paginate(github.rest.issues.listComments, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number
});
for (const c of comments) {
if (isMaintainer(c.user.login, c.author_association)) {
const d = new Date(c.updated_at);
if (d > lastActivity) lastActivity = d;
}
}
} catch (e) {}
if (maintainerPr) {
const d = new Date(pr.created_at);
if (d > lastActivity) lastActivity = d;
}
if (lastActivity < thirtyDaysAgo) {
core.info(`PR #${pr.number} is stale.`);
if (!dryRun) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body: "Hi there! Thank you for your contribution to Gemini CLI. We really appreciate the time and effort you've put into this pull request.\n\nTo keep our backlog manageable and ensure we're focusing on current priorities, we are closing pull requests that haven't seen maintainer activity for 30 days. Currently, the team is prioritizing work associated with **🔒 maintainer only** or **help wanted** issues.\n\nIf you believe this change is still critical, please feel free to comment with updated details. Otherwise, we encourage contributors to focus on open issues labeled as **help wanted**. Thank you for your understanding!"
});
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number,
state: 'closed'
});
}
}
}
}
@@ -1,46 +0,0 @@
name: '🏷️ Issue Opened Labeler'
on:
issues:
types:
- 'opened'
jobs:
label-issue:
runs-on: 'ubuntu-latest'
if: |-
${{ github.repository == 'google-gemini/gemini-cli' || github.repository == 'google-gemini/maintainers-gemini-cli' }}
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@a8d616148505b5069dccd32f177bb87d7f39123b' # ratchet:actions/create-github-app-token@v2
with:
app-id: '${{ secrets.APP_ID }}'
private-key: '${{ secrets.PRIVATE_KEY }}'
- name: 'Add need-triage label'
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
with:
github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
script: |-
const { data: issue } = await github.rest.issues.get({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const hasLabel = issue.labels.some(l => l.name === 'status/need-triage');
if (!hasLabel) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: ['status/need-triage']
});
} else {
core.info('Issue already has status/need-triage label. Skipping.');
}
@@ -1,57 +0,0 @@
name: 'Label Child Issues for Project Rollup'
on:
issues:
types: ['opened', 'edited', 'reopened']
schedule:
- cron: '0 * * * *' # Run every hour
workflow_dispatch:
permissions:
issues: 'write'
contents: 'read'
jobs:
# Event-based: Quick reaction to new/edited issues in THIS repo
labeler:
if: "github.event_name == 'issues'"
runs-on: 'ubuntu-latest'
steps:
- name: 'Checkout'
uses: 'actions/checkout@v4'
- name: 'Setup Node.js'
uses: 'actions/setup-node@v4'
with:
node-version: '20'
cache: 'npm'
- name: 'Install Dependencies'
run: 'npm ci'
- name: 'Run Multi-Repo Sync Script'
env:
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
run: 'node .github/scripts/sync-maintainer-labels.cjs'
# Scheduled/Manual: Recursive sync across multiple repos
sync-maintainer-labels:
if: "github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'"
runs-on: 'ubuntu-latest'
steps:
- name: 'Checkout'
uses: 'actions/checkout@v4'
- name: 'Setup Node.js'
uses: 'actions/setup-node@v4'
with:
node-version: '20'
cache: 'npm'
- name: 'Install Dependencies'
run: 'npm ci'
- name: 'Run Multi-Repo Sync Script'
env:
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
run: 'node .github/scripts/sync-maintainer-labels.cjs'
@@ -1,174 +0,0 @@
name: 'Label Workstream Rollup'
on:
issues:
types: ['opened', 'edited', 'reopened']
schedule:
- cron: '0 * * * *'
workflow_dispatch:
jobs:
labeler:
runs-on: 'ubuntu-latest'
permissions:
issues: 'write'
steps:
- name: 'Check for Parent Workstream and Apply Label'
uses: 'actions/github-script@v7'
with:
script: |
const labelToAdd = 'workstream-rollup';
// Allow-list of parent issue URLs
const allowedParentUrls = [
'https://github.com/google-gemini/gemini-cli/issues/15374',
'https://github.com/google-gemini/gemini-cli/issues/15456',
'https://github.com/google-gemini/gemini-cli/issues/15324',
'https://github.com/google-gemini/gemini-cli/issues/17202',
'https://github.com/google-gemini/gemini-cli/issues/17203'
];
// Single issue processing (for event triggers)
async function processSingleIssue(owner, repo, number) {
const query = `
query($owner:String!, $repo:String!, $number:Int!) {
repository(owner:$owner, name:$repo) {
issue(number:$number) {
number
parent {
url
parent {
url
parent {
url
parent {
url
parent {
url
}
}
}
}
}
}
}
}
`;
try {
const result = await github.graphql(query, { owner, repo, number });
if (!result || !result.repository || !result.repository.issue) {
console.log(`Issue #${number} not found or data missing.`);
return;
}
const issue = result.repository.issue;
await checkAndLabel(issue, owner, repo);
} catch (error) {
console.error(`Failed to process issue #${number}:`, error);
throw error; // Re-throw to be caught by main execution
}
}
// Bulk processing (for schedule/dispatch)
async function processAllOpenIssues(owner, repo) {
const query = `
query($owner:String!, $repo:String!, $cursor:String) {
repository(owner:$owner, name:$repo) {
issues(first: 100, states: OPEN, after: $cursor) {
pageInfo {
hasNextPage
endCursor
}
nodes {
number
parent {
url
parent {
url
parent {
url
parent {
url
parent {
url
}
}
}
}
}
}
}
}
}
`;
let hasNextPage = true;
let cursor = null;
while (hasNextPage) {
try {
const result = await github.graphql(query, { owner, repo, cursor });
if (!result || !result.repository || !result.repository.issues) {
console.error('Invalid response structure from GitHub API');
break;
}
const issues = result.repository.issues.nodes || [];
console.log(`Processing batch of ${issues.length} issues...`);
for (const issue of issues) {
await checkAndLabel(issue, owner, repo);
}
hasNextPage = result.repository.issues.pageInfo.hasNextPage;
cursor = result.repository.issues.pageInfo.endCursor;
} catch (error) {
console.error('Failed to fetch issues batch:', error);
throw error; // Re-throw to be caught by main execution
}
}
}
async function checkAndLabel(issue, owner, repo) {
if (!issue || !issue.parent) return;
let currentParent = issue.parent;
let tracedParents = [];
let matched = false;
while (currentParent) {
tracedParents.push(currentParent.url);
if (allowedParentUrls.includes(currentParent.url)) {
console.log(`SUCCESS: Issue #${issue.number} is a descendant of ${currentParent.url}. Trace: ${tracedParents.join(' -> ')}. Adding label.`);
await github.rest.issues.addLabels({
owner,
repo,
issue_number: issue.number,
labels: [labelToAdd]
});
matched = true;
break;
}
currentParent = currentParent.parent;
}
if (!matched && context.eventName === 'issues') {
console.log(`Issue #${issue.number} did not match any allowed workstreams. Trace: ${tracedParents.join(' -> ') || 'None'}.`);
}
}
// Main execution
try {
if (context.eventName === 'issues') {
console.log(`Processing single issue #${context.payload.issue.number}...`);
await processSingleIssue(context.repo.owner, context.repo.repo, context.payload.issue.number);
} else {
console.log(`Running for event: ${context.eventName}. Processing all open issues...`);
await processAllOpenIssues(context.repo.owner, context.repo.repo);
}
} catch (error) {
core.setFailed(`Workflow failed: ${error.message}`);
}
-2
View File
@@ -12,8 +12,6 @@ on:
jobs:
linkChecker:
if: |-
${{ github.repository == 'google-gemini/gemini-cli' }}
runs-on: 'ubuntu-latest'
steps:
- uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
@@ -1,83 +0,0 @@
name: '🏷️ PR Contribution Guidelines Notifier'
on:
pull_request:
types:
- 'opened'
jobs:
notify-process-change:
runs-on: 'ubuntu-latest'
if: |-
github.repository == 'google-gemini/gemini-cli' || github.repository == 'google-gemini/maintainers-gemini-cli'
permissions:
pull-requests: 'write'
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@a8d616148505b5069dccd32f177bb87d7f39123b' # ratchet:actions/create-github-app-token@v2
with:
app-id: '${{ secrets.APP_ID }}'
private-key: '${{ secrets.PRIVATE_KEY }}'
- name: 'Check membership and post comment'
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
with:
github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
script: |-
const org = context.repo.owner;
const repo = context.repo.repo;
const username = context.payload.pull_request.user.login;
const pr_number = context.payload.pull_request.number;
// 1. Check if the PR author is a maintainer
const authorAssociation = context.payload.pull_request.author_association;
if (['OWNER', 'MEMBER', 'COLLABORATOR'].includes(authorAssociation)) {
core.info(`${username} is a maintainer (Association: ${authorAssociation}). No notification needed.`);
return;
}
// 2. Check if the PR is already associated with an issue
const query = `
query($owner:String!, $repo:String!, $number:Int!) {
repository(owner:$owner, name:$repo) {
pullRequest(number:$number) {
closingIssuesReferences(first: 1) {
totalCount
}
}
}
}
`;
const variables = { owner: org, repo: repo, number: pr_number };
const result = await github.graphql(query, variables);
const issueCount = result.repository.pullRequest.closingIssuesReferences.totalCount;
if (issueCount > 0) {
core.info(`PR #${pr_number} is already associated with an issue. No notification needed.`);
return;
}
// 3. Post the notification comment
core.info(`${username} is not a maintainer and PR #${pr_number} has no linked issue. Posting notification.`);
const comment = `
Hi @${username}, thank you so much for your contribution to Gemini CLI! We really appreciate the time and effort you've put into this.
We're making some updates to our contribution process to improve how we track and review changes. Please take a moment to review our recent discussion post: [Improving Our Contribution Process & Introducing New Guidelines](https://github.com/google-gemini/gemini-cli/discussions/16706).
Key Update: Starting **January 26, 2026**, the Gemini CLI project will require all pull requests to be associated with an existing issue. Any pull requests not linked to an issue by that date will be automatically closed.
Thank you for your understanding and for being a part of our community!
`.trim().replace(/^[ ]+/gm, '');
await github.rest.issues.createComment({
owner: org,
repo: repo,
issue_number: pr_number,
body: comment
});
-1
View File
@@ -31,7 +31,6 @@ on:
jobs:
release:
if: "github.repository == 'google-gemini/gemini-cli'"
environment: "${{ github.event.inputs.environment || 'prod' }}"
runs-on: 'ubuntu-latest'
permissions:
+2 -2
View File
@@ -40,5 +40,5 @@ jobs:
If this is still relevant, you are welcome to reopen or leave a comment. Thanks for contributing!
days-before-stale: 60
days-before-close: 14
exempt-issue-labels: 'pinned,security,🔒 maintainer only,help wanted,🗓️ Public Roadmap'
exempt-pr-labels: 'pinned,security,🔒 maintainer only,help wanted,🗓️ Public Roadmap'
exempt-issue-labels: 'pinned,security'
exempt-pr-labels: 'pinned,security'
@@ -1,4 +1,4 @@
name: 'Testing: E2E (Chained)'
name: 'Test Chained E2E'
on:
push:
@@ -37,15 +37,14 @@ jobs:
- id: 'merge-queue-e2e-skipper'
uses: 'cariad-tech/merge-queue-ci-skipper@1032489e59437862c90a08a2c92809c903883772' # ratchet:cariad-tech/merge-queue-ci-skipper@main
with:
secret: '${{ secrets.GEMINI_CLI_ROBOT_GITHUB_PAT }}'
secret: '${{ secrets.GITHUB_TOKEN }}'
continue-on-error: true
download_repo_name:
runs-on: 'gemini-cli-ubuntu-16-core'
if: "${{github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_run'}}"
if: "github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_run'"
outputs:
repo_name: '${{ steps.output-repo-name.outputs.repo_name }}'
head_sha: '${{ steps.output-repo-name.outputs.head_sha }}'
steps:
- name: 'Mock Repo Artifact'
if: "${{ github.event_name == 'workflow_dispatch' }}"
@@ -67,7 +66,7 @@ jobs:
name: 'repo_name'
run-id: '${{ env.RUN_ID }}'
path: '${{ runner.temp }}/artifacts'
- name: 'Output Repo Name and SHA'
- name: 'Output Repo Name'
id: 'output-repo-name'
uses: 'actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd' # ratchet:actions/github-script@v8
with:
@@ -76,16 +75,8 @@ jobs:
const fs = require('fs');
const path = require('path');
const temp = '${{ runner.temp }}/artifacts';
const repoPath = path.join(temp, 'repo_name');
if (fs.existsSync(repoPath)) {
const repo_name = String(fs.readFileSync(repoPath)).trim();
core.setOutput('repo_name', repo_name);
}
const shaPath = path.join(temp, 'head_sha');
if (fs.existsSync(shaPath)) {
const head_sha = String(fs.readFileSync(shaPath)).trim();
core.setOutput('head_sha', head_sha);
}
const repo_name = String(fs.readFileSync(path.join(temp, 'repo_name')));
core.setOutput('repo_name', repo_name);
parse_run_context:
name: 'Parse run context'
@@ -100,7 +91,7 @@ jobs:
name: 'Set dynamic repository and SHA'
env:
REPO: '${{ needs.download_repo_name.outputs.repo_name || github.repository }}'
SHA: '${{ needs.download_repo_name.outputs.head_sha || github.event.inputs.head_sha || github.event.workflow_run.head_sha || github.sha }}'
SHA: '${{ github.event.inputs.head_sha || github.event.workflow_run.head_sha || github.sha }}'
shell: 'bash'
run: |
echo "REPO=$REPO" >> "$GITHUB_OUTPUT"
@@ -109,20 +100,19 @@ jobs:
set_pending_status:
runs-on: 'gemini-cli-ubuntu-16-core'
permissions: 'write-all'
if: "github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_run'"
needs:
- 'parse_run_context'
if: 'always()'
steps:
- name: 'Set pending status'
uses: 'myrotvorets/set-commit-status-action@16037e056d73b2d3c88e37e393ff369047f70886' # ratchet:myrotvorets/set-commit-status-action@master
if: 'always()'
with:
allowForks: 'true'
repo: '${{ github.repository }}'
repo: '${{ needs.parse_run_context.outputs.repository }}'
sha: '${{ needs.parse_run_context.outputs.sha }}'
token: '${{ secrets.GEMINI_CLI_ROBOT_GITHUB_PAT }}'
token: '${{ secrets.GITHUB_TOKEN }}'
status: 'pending'
context: 'E2E (Chained)'
e2e_linux:
name: 'E2E Test (Linux) - ${{ matrix.sandbox }}'
@@ -131,7 +121,7 @@ jobs:
- 'parse_run_context'
runs-on: 'gemini-cli-ubuntu-16-core'
if: |
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip != 'true')
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip == 'false')
strategy:
fail-fast: false
matrix:
@@ -160,7 +150,7 @@ jobs:
run: 'npm run build'
- name: 'Set up Docker'
if: "${{matrix.sandbox == 'sandbox:docker'}}"
if: "matrix.sandbox == 'sandbox:docker'"
uses: 'docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435' # ratchet:docker/setup-buildx-action@v3
- name: 'Run E2E tests'
@@ -168,7 +158,6 @@ jobs:
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
KEEP_OUTPUT: 'true'
VERBOSE: 'true'
BUILD_SANDBOX_FLAGS: '--cache-from type=gha --cache-to type=gha,mode=max'
shell: 'bash'
run: |
if [[ "${{ matrix.sandbox }}" == "sandbox:docker" ]]; then
@@ -184,7 +173,7 @@ jobs:
- 'parse_run_context'
runs-on: 'macos-latest'
if: |
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip != 'true')
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip == 'false')
steps:
- name: 'Checkout'
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
@@ -204,11 +193,11 @@ jobs:
run: 'npm run build'
- name: 'Fix rollup optional dependencies on macOS'
if: "${{runner.os == 'macOS'}}"
if: "runner.os == 'macOS'"
run: |
npm cache clean --force
- name: 'Run E2E tests (non-Windows)'
if: "${{runner.os != 'Windows'}}"
if: "runner.os != 'Windows'"
env:
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
KEEP_OUTPUT: 'true'
@@ -222,7 +211,7 @@ jobs:
- 'merge_queue_skipper'
- 'parse_run_context'
if: |
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip != 'true')
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip == 'false')
runs-on: 'gemini-cli-windows-16-core'
continue-on-error: true
@@ -277,53 +266,20 @@ jobs:
shell: 'pwsh'
run: 'npm run test:integration:sandbox:none'
evals:
name: 'Evals (ALWAYS_PASSING)'
needs:
- 'merge_queue_skipper'
- 'parse_run_context'
runs-on: 'gemini-cli-ubuntu-16-core'
if: |
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip != 'true')
steps:
- name: 'Checkout'
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
with:
ref: '${{ needs.parse_run_context.outputs.sha }}'
repository: '${{ needs.parse_run_context.outputs.repository }}'
- name: 'Set up Node.js 20.x'
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
with:
node-version: '20.x'
- name: 'Install dependencies'
run: 'npm ci'
- name: 'Build project'
run: 'npm run build'
- name: 'Run Evals (Required to pass)'
env:
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
run: 'npm run test:always_passing_evals'
e2e:
name: 'E2E'
if: |
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip != 'true')
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip == 'false')
needs:
- 'e2e_linux'
- 'e2e_mac'
- 'evals'
- 'merge_queue_skipper'
runs-on: 'gemini-cli-ubuntu-16-core'
steps:
- name: 'Check E2E test results'
run: |
if [[ ${{ needs.e2e_linux.result }} != 'success' || \
${{ needs.e2e_mac.result }} != 'success' || \
${{ needs.evals.result }} != 'success' ]]; then
${{ needs.e2e_mac.result }} != 'success' ]]; then
echo "One or more E2E jobs failed."
exit 1
fi
@@ -332,7 +288,7 @@ jobs:
set_workflow_status:
runs-on: 'gemini-cli-ubuntu-16-core'
permissions: 'write-all'
if: 'always()'
if: "github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_run'"
needs:
- 'parse_run_context'
- 'e2e'
@@ -342,8 +298,7 @@ jobs:
if: 'always()'
with:
allowForks: 'true'
repo: '${{ github.repository }}'
repo: '${{ needs.parse_run_context.outputs.repository }}'
sha: '${{ needs.parse_run_context.outputs.sha }}'
token: '${{ secrets.GITHUB_TOKEN }}'
status: '${{ needs.e2e.result }}'
context: 'E2E (Chained)'
status: '${{ job.status }}'
+6 -11
View File
@@ -3,15 +3,11 @@ name: 'Trigger E2E'
on:
workflow_dispatch:
inputs:
repo_name:
description: 'Repository name (e.g., owner/repo)'
required: false
branch_ref:
description: 'Branch to run on'
required: true
default: 'main'
type: 'string'
head_sha:
description: 'SHA of the commit to test'
required: false
type: 'string'
pull_request:
jobs:
save_repo_name:
@@ -19,12 +15,11 @@ jobs:
steps:
- name: 'Save Repo name'
env:
REPO_NAME: '${{ github.event.inputs.repo_name || github.event.pull_request.head.repo.full_name }}'
HEAD_SHA: '${{ github.event.inputs.head_sha || github.event.pull_request.head.sha }}'
# Replace with github.event.pull_request.head.repo.full_name when switched to listen on pull request events. This repo name does not contain the org which is needed for checkout.
REPO_NAME: '${{ github.event.repository.name }}'
run: |
mkdir -p ./pr
echo '${{ env.REPO_NAME }}' > ./pr/repo_name
echo '${{ env.HEAD_SHA }}' > ./pr/head_sha
- uses: 'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02' # ratchet:actions/upload-artifact@v4
with:
name: 'repo_name'
-6
View File
@@ -11,8 +11,6 @@
.gemini/*
!.gemini/config.yaml
!.gemini/commands/
!.gemini/skills/
!.gemini/settings.json
# Note: .gemini-clipboard/ is NOT in gitignore so Gemini can access pasted images
@@ -55,9 +53,5 @@ gha-creds-*.json
# Log files
patch_output.log
gemini-debug.log
.genkit
.gemini-clipboard/
.eslintcache
evals/logs/
-2
View File
@@ -17,7 +17,5 @@ eslint.config.js
**/generated
gha-creds-*.json
junit.xml
.gemini-linters/
Thumbs.db
.pytest_cache
**/SKILL.md
+3 -13
View File
@@ -41,14 +41,7 @@ This project follows
The process for contributing code is as follows:
1. **Find an issue** that you want to work on. If an issue is tagged as
`🔒Maintainers only`, this means it is reserved for project maintainers. We
will not accept pull requests related to these issues. In the near future,
we will explicitly mark issues looking for contributions using the
`help wanted` label. If you believe an issue is a good candidate for
community contribution, please leave a comment on the issue. A maintainer
will review it and apply the `help-wanted` label if appropriate. Only
maintainers should attempt to add the `help-wanted` label to an issue.
1. **Find an issue** that you want to work on.
2. **Fork the repository** and create a new branch.
3. **Make your changes** in the `packages/` directory.
4. **Ensure all checks pass** by running `npm run preflight`.
@@ -99,11 +92,8 @@ any code is written.
- **For features:** The PR should be linked to the feature request or proposal
issue that has been approved by a maintainer.
If an issue for your change doesn't exist, we will automatically close your PR
along with a comment reminding you to associate the PR with an issue. The ideal
workflow starts with an issue that has been reviewed and approved by a
maintainer. Please **open the issue first** and wait for feedback before you
start coding.
If an issue for your change doesn't exist, please **open one first** and wait
for feedback before you start coding.
#### 2. Keep it small and focused
+387 -65
View File
@@ -1,78 +1,400 @@
# Gemini CLI Project Context
## Building and running
Gemini CLI is an open-source AI agent that brings the power of Gemini directly
into the terminal. It is designed to be a terminal-first, extensible, and
powerful tool for developers.
Before submitting any changes, it is crucial to validate them by running the
full preflight check. This command will build the repository, run all tests,
check for type errors, and lint the code.
## Project Overview
To run the full suite of checks, execute the following command:
- **Purpose:** Provide a seamless terminal interface for Gemini models,
supporting code understanding, generation, automation, and integration via MCP
(Model Context Protocol).
- **Main Technologies:**
- **Runtime:** Node.js (>=20.0.0, recommended ~20.19.0 for development)
- **Language:** TypeScript
- **UI Framework:** React (using [Ink](https://github.com/vadimdemedes/ink)
for CLI rendering)
- **Testing:** Vitest
- **Bundling:** esbuild
- **Linting/Formatting:** ESLint, Prettier
- **Architecture:** Monorepo structure using npm workspaces.
- `packages/cli`: User-facing terminal UI, input processing, and display
rendering.
- `packages/core`: Backend logic, Gemini API orchestration, prompt
construction, and tool execution.
- `packages/core/src/tools/`: Built-in tools for file system, shell, and web
operations.
- `packages/a2a-server`: Experimental Agent-to-Agent server.
- `packages/vscode-ide-companion`: VS Code extension pairing with the CLI.
```bash
npm run preflight
```
## Building and Running
This single command ensures that your changes meet all the quality gates of the
project. While you can run the individual steps (`build`, `test`, `typecheck`,
`lint`) separately, it is highly recommended to use `npm run preflight` to
ensure a comprehensive validation.
- **Install Dependencies:** `npm install`
- **Build All:** `npm run build:all` (Builds packages, sandbox, and VS Code
companion)
- **Build Packages:** `npm run build`
- **Run in Development:** `npm run start`
- **Run in Debug Mode:** `npm run debug` (Enables Node.js inspector)
- **Bundle Project:** `npm run bundle`
- **Clean Artifacts:** `npm run clean`
## Writing Tests
## Testing and Quality
This project uses **Vitest** as its primary testing framework. When writing
tests, aim to follow existing patterns. Key conventions include:
- **Test Commands:**
- **Unit (All):** `npm run test`
- **Integration (E2E):** `npm run test:e2e`
- **Workspace-Specific:** `npm test -w <pkg> -- <path>` (Note: `<path>` must
be relative to the workspace root, e.g.,
`-w @google/gemini-cli-core -- src/routing/modelRouterService.test.ts`)
- **Full Validation:** `npm run preflight` (Heaviest check; runs clean, install,
build, lint, type check, and tests. Recommended before submitting PRs.)
- **Individual Checks:** `npm run lint` / `npm run format` / `npm run typecheck`
### Test Structure and Framework
## Development Conventions
- **Framework**: All tests are written using Vitest (`describe`, `it`, `expect`,
`vi`).
- **File Location**: Test files (`*.test.ts` for logic, `*.test.tsx` for React
components) are co-located with the source files they test.
- **Configuration**: Test environments are defined in `vitest.config.ts` files.
- **Setup/Teardown**: Use `beforeEach` and `afterEach`. Commonly,
`vi.resetAllMocks()` is called in `beforeEach` and `vi.restoreAllMocks()` in
`afterEach`.
- **Contributions:** Follow the process outlined in `CONTRIBUTING.md`. Requires
signing the Google CLA.
- **Pull Requests:** Keep PRs small, focused, and linked to an existing issue.
- **Commit Messages:** Follow the
[Conventional Commits](https://www.conventionalcommits.org/) standard.
- **Coding Style:** Adhere to existing patterns in `packages/cli` (React/Ink)
and `packages/core` (Backend logic).
- **Imports:** Use specific imports and avoid restricted relative imports
between packages (enforced by ESLint).
### Mocking (`vi` from Vitest)
## Testing Conventions
- **ES Modules**: Mock with
`vi.mock('module-name', async (importOriginal) => { ... })`. Use
`importOriginal` for selective mocking.
- _Example_:
`vi.mock('os', async (importOriginal) => { const actual = await importOriginal(); return { ...actual, homedir: vi.fn() }; });`
- **Mocking Order**: For critical dependencies (e.g., `os`, `fs`) that affect
module-level constants, place `vi.mock` at the _very top_ of the test file,
before other imports.
- **Hoisting**: Use `const myMock = vi.hoisted(() => vi.fn());` if a mock
function needs to be defined before its use in a `vi.mock` factory.
- **Mock Functions**: Create with `vi.fn()`. Define behavior with
`mockImplementation()`, `mockResolvedValue()`, or `mockRejectedValue()`.
- **Spying**: Use `vi.spyOn(object, 'methodName')`. Restore spies with
`mockRestore()` in `afterEach`.
- **Environment Variables:** When testing code that depends on environment
variables, use `vi.stubEnv('NAME', 'value')` in `beforeEach` and
`vi.unstubAllEnvs()` in `afterEach`. Avoid modifying `process.env` directly as
it can lead to test leakage and is less reliable. To "unset" a variable, use
an empty string `vi.stubEnv('NAME', '')`.
### Commonly Mocked Modules
## Documentation
- **Node.js built-ins**: `fs`, `fs/promises`, `os` (especially `os.homedir()`),
`path`, `child_process` (`execSync`, `spawn`).
- **External SDKs**: `@google/genai`, `@modelcontextprotocol/sdk`.
- **Internal Project Modules**: Dependencies from other project packages are
often mocked.
- Suggest documentation updates when code changes render existing documentation
obsolete or incomplete.
- Located in the `docs/` directory.
- Use the `docs-writer` skill.
### React Component Testing (CLI UI - Ink)
- Use `render()` from `ink-testing-library`.
- Assert output with `lastFrame()`.
- Wrap components in necessary `Context.Provider`s.
- Mock custom React hooks and complex child components using `vi.mock()`.
### Asynchronous Testing
- Use `async/await`.
- For timers, use `vi.useFakeTimers()`, `vi.advanceTimersByTimeAsync()`,
`vi.runAllTimersAsync()`.
- Test promise rejections with `await expect(promise).rejects.toThrow(...)`.
### General Guidance
- When adding tests, first examine existing tests to understand and conform to
established conventions.
- Pay close attention to the mocks at the top of existing test files; they
reveal critical dependencies and how they are managed in a test environment.
## Git Repo
The main branch for this project is called "main"
## JavaScript/TypeScript
When contributing to this React, Node, and TypeScript codebase, please
prioritize the use of plain JavaScript objects with accompanying TypeScript
interface or type declarations over JavaScript class syntax. This approach
offers significant advantages, especially concerning interoperability with React
and overall code maintainability.
### Preferring Plain Objects over Classes
JavaScript classes, by their nature, are designed to encapsulate internal state
and behavior. While this can be useful in some object-oriented paradigms, it
often introduces unnecessary complexity and friction when working with React's
component-based architecture. Here's why plain objects are preferred:
- Seamless React Integration: React components thrive on explicit props and
state management. Classes' tendency to store internal state directly within
instances can make prop and state propagation harder to reason about and
maintain. Plain objects, on the other hand, are inherently immutable (when
used thoughtfully) and can be easily passed as props, simplifying data flow
and reducing unexpected side effects.
- Reduced Boilerplate and Increased Conciseness: Classes often promote the use
of constructors, this binding, getters, setters, and other boilerplate that
can unnecessarily bloat code. TypeScript interface and type declarations
provide powerful static type checking without the runtime overhead or
verbosity of class definitions. This allows for more succinct and readable
code, aligning with JavaScript's strengths in functional programming.
- Enhanced Readability and Predictability: Plain objects, especially when their
structure is clearly defined by TypeScript interfaces, are often easier to
read and understand. Their properties are directly accessible, and there's no
hidden internal state or complex inheritance chains to navigate. This
predictability leads to fewer bugs and a more maintainable codebase.
- Simplified Immutability: While not strictly enforced, plain objects encourage
an immutable approach to data. When you need to modify an object, you
typically create a new one with the desired changes, rather than mutating the
original. This pattern aligns perfectly with React's reconciliation process
and helps prevent subtle bugs related to shared mutable state.
- Better Serialization and Deserialization: Plain JavaScript objects are
naturally easy to serialize to JSON and deserialize back, which is a common
requirement in web development (e.g., for API communication or local storage).
Classes, with their methods and prototypes, can complicate this process.
### Embracing ES Module Syntax for Encapsulation
Rather than relying on Java-esque private or public class members, which can be
verbose and sometimes limit flexibility, we strongly prefer leveraging ES module
syntax (`import`/`export`) for encapsulating private and public APIs.
- Clearer Public API Definition: With ES modules, anything that is exported is
part of the public API of that module, while anything not exported is
inherently private to that module. This provides a very clear and explicit way
to define what parts of your code are meant to be consumed by other modules.
- Enhanced Testability (Without Exposing Internals): By default, unexported
functions or variables are not accessible from outside the module. This
encourages you to test the public API of your modules, rather than their
internal implementation details. If you find yourself needing to spy on or
stub an unexported function for testing purposes, it's often a "code smell"
indicating that the function might be a good candidate for extraction into its
own separate, testable module with a well-defined public API. This promotes a
more robust and maintainable testing strategy.
- Reduced Coupling: Explicitly defined module boundaries through import/export
help reduce coupling between different parts of your codebase. This makes it
easier to refactor, debug, and understand individual components in isolation.
### Avoiding `any` Types and Type Assertions; Preferring `unknown`
TypeScript's power lies in its ability to provide static type checking, catching
potential errors before your code runs. To fully leverage this, it's crucial to
avoid the `any` type and be judicious with type assertions.
- **The Dangers of `any`**: Using any effectively opts out of TypeScript's type
checking for that particular variable or expression. While it might seem
convenient in the short term, it introduces significant risks:
- **Loss of Type Safety**: You lose all the benefits of type checking, making
it easy to introduce runtime errors that TypeScript would otherwise have
caught.
- **Reduced Readability and Maintainability**: Code with `any` types is harder
to understand and maintain, as the expected type of data is no longer
explicitly defined.
- **Masking Underlying Issues**: Often, the need for any indicates a deeper
problem in the design of your code or the way you're interacting with
external libraries. It's a sign that you might need to refine your types or
refactor your code.
- **Preferring `unknown` over `any`**: When you absolutely cannot determine the
type of a value at compile time, and you're tempted to reach for any, consider
using unknown instead. unknown is a type-safe counterpart to any. While a
variable of type unknown can hold any value, you must perform type narrowing
(e.g., using typeof or instanceof checks, or a type assertion) before you can
perform any operations on it. This forces you to handle the unknown type
explicitly, preventing accidental runtime errors.
```ts
function processValue(value: unknown) {
if (typeof value === 'string') {
// value is now safely a string
console.log(value.toUpperCase());
} else if (typeof value === 'number') {
// value is now safely a number
console.log(value * 2);
}
// Without narrowing, you cannot access properties or methods on 'value'
// console.log(value.someProperty); // Error: Object is of type 'unknown'.
}
```
- **Type Assertions (`as Type`) - Use with Caution**: Type assertions tell the
TypeScript compiler, "Trust me, I know what I'm doing; this is definitely of
this type." While there are legitimate use cases (e.g., when dealing with
external libraries that don't have perfect type definitions, or when you have
more information than the compiler), they should be used sparingly and with
extreme caution.
- **Bypassing Type Checking**: Like `any`, type assertions bypass TypeScript's
safety checks. If your assertion is incorrect, you introduce a runtime error
that TypeScript would not have warned you about.
- **Code Smell in Testing**: A common scenario where `any` or type assertions
might be tempting is when trying to test "private" implementation details
(e.g., spying on or stubbing an unexported function within a module). This
is a strong indication of a "code smell" in your testing strategy and
potentially your code structure. Instead of trying to force access to
private internals, consider whether those internal details should be
refactored into a separate module with a well-defined public API. This makes
them inherently testable without compromising encapsulation.
### Type narrowing `switch` clauses
Use the `checkExhaustive` helper in the default clause of a switch statement.
This will ensure that all of the possible options within the value or
enumeration are used.
This helper method can be found in `packages/cli/src/utils/checks.ts`
### Embracing JavaScript's Array Operators
To further enhance code cleanliness and promote safe functional programming
practices, leverage JavaScript's rich set of array operators as much as
possible. Methods like `.map()`, `.filter()`, `.reduce()`, `.slice()`,
`.sort()`, and others are incredibly powerful for transforming and manipulating
data collections in an immutable and declarative way.
Using these operators:
- Promotes Immutability: Most array operators return new arrays, leaving the
original array untouched. This functional approach helps prevent unintended
side effects and makes your code more predictable.
- Improves Readability: Chaining array operators often lead to more concise and
expressive code than traditional for loops or imperative logic. The intent of
the operation is clear at a glance.
- Facilitates Functional Programming: These operators are cornerstones of
functional programming, encouraging the creation of pure functions that take
inputs and produce outputs without causing side effects. This paradigm is
highly beneficial for writing robust and testable code that pairs well with
React.
By consistently applying these principles, we can maintain a codebase that is
not only efficient and performant but also a joy to work with, both now and in
the future.
## React (mirrored and adjusted from [react-mcp-server](https://github.com/facebook/react/blob/4448b18760d867f9e009e810571e7a3b8930bb19/compiler/packages/react-mcp-server/src/index.ts#L376C1-L441C94))
### Role
You are a React assistant that helps users write more efficient and optimizable
React code. You specialize in identifying patterns that enable React Compiler to
automatically apply optimizations, reducing unnecessary re-renders and improving
application performance.
### Follow these guidelines in all code you produce and suggest
Use functional components with Hooks: Do not generate class components or use
old lifecycle methods. Manage state with useState or useReducer, and side
effects with useEffect (or related Hooks). Always prefer functions and Hooks for
any new component logic.
Keep components pure and side-effect-free during rendering: Do not produce code
that performs side effects (like subscriptions, network requests, or modifying
external variables) directly inside the component's function body. Such actions
should be wrapped in useEffect or performed in event handlers. Ensure your
render logic is a pure function of props and state.
Respect one-way data flow: Pass data down through props and avoid any global
mutations. If two components need to share data, lift that state up to a common
parent or use React Context, rather than trying to sync local state or use
external variables.
Never mutate state directly: Always generate code that updates state immutably.
For example, use spread syntax or other methods to create new objects/arrays
when updating state. Do not use assignments like state.someValue = ... or array
mutations like array.push() on state variables. Use the state setter (setState
from useState, etc.) to update state.
Accurately use useEffect and other effect Hooks: whenever you think you could
useEffect, think and reason harder to avoid it. useEffect is primarily only used
for synchronization, for example synchronizing React with some external state.
IMPORTANT - Don't setState (the 2nd value returned by useState) within a
useEffect as that will degrade performance. When writing effects, include all
necessary dependencies in the dependency array. Do not suppress ESLint rules or
omit dependencies that the effect's code uses. Structure the effect callbacks to
handle changing values properly (e.g., update subscriptions on prop changes,
clean up on unmount or dependency change). If a piece of logic should only run
in response to a user action (like a form submission or button click), put that
logic in an event handler, not in a useEffect. Where possible, useEffects should
return a cleanup function.
Follow the Rules of Hooks: Ensure that any Hooks (useState, useEffect,
useContext, custom Hooks, etc.) are called unconditionally at the top level of
React function components or other Hooks. Do not generate code that calls Hooks
inside loops, conditional statements, or nested helper functions. Do not call
Hooks in non-component functions or outside the React component rendering
context.
Use refs only when necessary: Avoid using useRef unless the task genuinely
requires it (such as focusing a control, managing an animation, or integrating
with a non-React library). Do not use refs to store application state that
should be reactive. If you do use refs, never write to or read from ref.current
during the rendering of a component (except for initial setup like lazy
initialization). Any ref usage should not affect the rendered output directly.
Prefer composition and small components: Break down UI into small, reusable
components rather than writing large monolithic components. The code you
generate should promote clarity and reusability by composing components
together. Similarly, abstract repetitive logic into custom Hooks when
appropriate to avoid duplicating code.
Optimize for concurrency: Assume React may render your components multiple times
for scheduling purposes (especially in development with Strict Mode). Write code
that remains correct even if the component function runs more than once. For
instance, avoid side effects in the component body and use functional state
updates (e.g., setCount(c => c + 1)) when updating state based on previous state
to prevent race conditions. Always include cleanup functions in effects that
subscribe to external resources. Don't write useEffects for "do this when this
changes" side effects. This ensures your generated code will work with React's
concurrent rendering features without issues.
Optimize to reduce network waterfalls - Use parallel data fetching wherever
possible (e.g., start multiple requests at once rather than one after another).
Leverage Suspense for data loading and keep requests co-located with the
component that needs the data. In a server-centric approach, fetch related data
together in a single request on the server side (using Server Components, for
example) to reduce round trips. Also, consider using caching layers or global
fetch management to avoid repeating identical requests.
Rely on React Compiler - useMemo, useCallback, and React.memo can be omitted if
React Compiler is enabled. Avoid premature optimization with manual memoization.
Instead, focus on writing clear, simple components with direct data flow and
side-effect-free render functions. Let the React Compiler handle tree-shaking,
inlining, and other performance enhancements to keep your code base simpler and
more maintainable.
Design for a good user experience - Provide clear, minimal, and non-blocking UI
states. When data is loading, show lightweight placeholders (e.g., skeleton
screens) rather than intrusive spinners everywhere. Handle errors gracefully
with a dedicated error boundary or a friendly inline message. Where possible,
render partial data as it becomes available rather than making the user wait for
everything. Suspense allows you to declare the loading states in your component
tree in a natural way, preventing “flash” states and improving perceived
performance.
### Process
1. Analyze the user's code for optimization opportunities:
- Check for React anti-patterns that prevent compiler optimization
- Look for component structure issues that limit compiler effectiveness
- Think about each suggestion you are making and consult React docs for best
practices
2. Provide actionable guidance:
- Explain specific code changes with clear reasoning
- Show before/after examples when suggesting changes
- Only suggest changes that meaningfully improve optimization potential
### Optimization Guidelines
- State updates should be structured to enable granular updates
- Side effects should be isolated and dependencies clearly defined
## Documentation guidelines
When working in the `/docs` directory, follow the guidelines in this section:
- **Role:** You are an expert technical writer and AI assistant for contributors
to Gemini CLI. Produce professional, accurate, and consistent documentation to
guide users of Gemini CLI.
- **Technical Accuracy:** Do not invent facts, commands, code, API names, or
output. All technical information specific to Gemini CLI must be based on code
found within this directory and its subdirectories.
- **Style Authority:** Your source for writing guidance and style is the
"Documentation contribution process" section in the root directory's
`CONTRIBUTING.md` file, as well as any guidelines provided this section.
- **Information Architecture Consideration:** Before proposing documentation
changes, consider the information architecture. If a change adds significant
new content to existing documents, evaluate if creating a new, more focused
page or changes to `sidebar.json` would provide a better user experience.
- **Proactive User Consideration:** The user experience should be a primary
concern when making changes to documentation. Aim to fill gaps in existing
knowledge whenever possible while keeping documentation concise and easy for
users to understand. If changes might hinder user understanding or
accessibility, proactively raise these concerns and propose alternatives.
## Comments policy
Only write high-value comments if at all. Avoid talking to the user through
comments.
## General requirements
- If there is something you do not understand or is ambiguous, seek confirmation
or clarification from the user before making changes based on assumptions.
- Use hyphens instead of underscores in flag names (e.g. `my-flag` instead of
`my_flag`).
- Always refer to Gemini CLI as `Gemini CLI`, never `the Gemini CLI`.
+6 -24
View File
@@ -1,10 +1,10 @@
# Gemini CLI
[![Gemini CLI CI](https://github.com/google-gemini/gemini-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/google-gemini/gemini-cli/actions/workflows/ci.yml)
[![Gemini CLI E2E (Chained)](https://github.com/google-gemini/gemini-cli/actions/workflows/chained_e2e.yml/badge.svg)](https://github.com/google-gemini/gemini-cli/actions/workflows/chained_e2e.yml)
[![Gemini CLI E2E](https://github.com/google-gemini/gemini-cli/actions/workflows/e2e.yml/badge.svg)](https://github.com/google-gemini/gemini-cli/actions/workflows/e2e.yml)
[![Version](https://img.shields.io/npm/v/@google/gemini-cli)](https://www.npmjs.com/package/@google/gemini-cli)
[![License](https://img.shields.io/github/license/google-gemini/gemini-cli)](https://github.com/google-gemini/gemini-cli/blob/main/LICENSE)
[![View Code Wiki](https://assets.codewiki.google/readme-badge/static.svg)](https://codewiki.google/github.com/google-gemini/gemini-cli?utm_source=badge&utm_medium=github&utm_campaign=github.com/google-gemini/gemini-cli)
[![View Code Wiki](https://www.gstatic.com/_/boq-sdlc-agents-ui/_/r/YUi5dj2UWvE.svg)](https://codewiki.google/github.com/google-gemini/gemini-cli)
![Gemini CLI Screenshot](./docs/assets/gemini-screenshot.png)
@@ -18,8 +18,7 @@ Learn all about Gemini CLI in our [documentation](https://geminicli.com/docs/).
- **🎯 Free tier**: 60 requests/min and 1,000 requests/day with personal Google
account.
- **🧠 Powerful Gemini 3 models**: Access to improved reasoning and 1M token
context window.
- **🧠 Powerful Gemini 2.5 Pro**: Access to 1M token context window.
- **🔧 Built-in tools**: Google Search grounding, file operations, shell
commands, web fetching.
- **🔌 Extensible**: MCP (Model Context Protocol) support for custom
@@ -40,7 +39,7 @@ Learn all about Gemini CLI in our [documentation](https://geminicli.com/docs/).
```bash
# Using npx (no installation required)
npx @google/gemini-cli
npx https://github.com/google-gemini/gemini-cli
```
#### Install globally with npm
@@ -55,23 +54,6 @@ npm install -g @google/gemini-cli
brew install gemini-cli
```
#### Install globally with MacPorts (macOS)
```bash
sudo port install gemini-cli
```
#### Install with Anaconda (for restricted environments)
```bash
# Create and activate a new environment
conda create -y -n gemini_env -c conda-forge nodejs
conda activate gemini_env
# Install Gemini CLI globally via npm (inside the environment)
npm install -g @google/gemini-cli
```
## Release Cadence and Tags
See [Releases](./docs/releases.md) for more details.
@@ -158,7 +140,7 @@ for details)
**Benefits:**
- **Free tier**: 60 requests/min and 1,000 requests/day
- **Gemini 3 models** with 1M token context window
- **Gemini 2.5 Pro** with 1M token context window
- **No API key management** - just sign in with your Google account
- **Automatic updates** to latest models
@@ -182,7 +164,7 @@ gemini
**Benefits:**
- **Free tier**: 1000 requests/day with Gemini 3 (mix of flash and pro)
- **Free tier**: 100 requests/day with Gemini 2.5 Pro
- **Model selection**: Choose specific Gemini models
- **Usage-based billing**: Upgrade for higher limits when needed
+3 -3
View File
@@ -13,11 +13,11 @@ input:
as handling the initial user input, presenting the final output, and
managing the overall user experience.
- **Key functions contained in the package:**
- [Input processing](/docs/cli/commands)
- [Input processing](/docs/cli/commands.md)
- History management
- Display rendering
- [Theme and UI customization](/docs/cli/themes)
- [CLI configuration settings](/docs/get-started/configuration)
- [Theme and UI customization](/docs/cli/themes.md)
- [CLI configuration settings](/docs/get-started/configuration.md)
2. **Core package (`packages/core`):**
- **Purpose:** This acts as the backend for the Gemini CLI. It receives
Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

+18 -268
View File
@@ -1,265 +1,15 @@
# Gemini CLI release notes
# Gemini CLI Changelog
Gemini CLI has three major release channels: nightly, preview, and stable. For
most users, we recommend the stable release.
Wondering what's new in Gemini CLI? This document provides key highlights and
notable changes to Gemini CLI.
On this page, you can find information regarding the current releases and
announcements from each release.
## v0.15.0 - Gemini CLI weekly update - 2025-11-03
For the full changelog, refer to
[Releases - google-gemini/gemini-cli](https://github.com/google-gemini/gemini-cli/releases)
on GitHub.
## Current releases
| Release channel | Notes |
| :-------------------- | :---------------------------------------------- |
| Nightly | Nightly release with the most recent changes. |
| [Preview](preview.md) | Experimental features ready for early feedback. |
| [Stable](latest.md) | Stable, recommended for general use. |
## Announcements: v0.25.0 - 2026-01-20
- **Skills and Agents Improvements:** We've enhanced the `activate_skill` tool,
added a new `pr-creator` skill
([#16232](https://github.com/google-gemini/gemini-cli/pull/16232) by
[@NTaylorMullen](https://github.com/NTaylorMullen)), enabled skills by
default, improved the `cli_help` agent
([#16100](https://github.com/google-gemini/gemini-cli/pull/16100) by
[@scidomino](https://github.com/scidomino)), and added a new `/agents refresh`
command ([#16204](https://github.com/google-gemini/gemini-cli/pull/16204) by
[@joshualitt](https://github.com/joshualitt)).
- **UI/UX Refinements:** You'll notice more transparent feedback for skills
([#15954](https://github.com/google-gemini/gemini-cli/pull/15954) by
[@NTaylorMullen](https://github.com/NTaylorMullen)), the ability to switch
focus between the shell and input with Tab
([#14332](https://github.com/google-gemini/gemini-cli/pull/14332) by
[@jacob314](https://github.com/jacob314)), and dynamic terminal tab titles
([#16378](https://github.com/google-gemini/gemini-cli/pull/16378) by
[@NTaylorMullen](https://github.com/NTaylorMullen)).
- **Core Functionality & Performance:** This release includes support for
built-in agent skills
([#16045](https://github.com/google-gemini/gemini-cli/pull/16045) by
[@NTaylorMullen](https://github.com/NTaylorMullen)), refined Gemini 3 system
instructions ([#16139](https://github.com/google-gemini/gemini-cli/pull/16139)
by [@NTaylorMullen](https://github.com/NTaylorMullen)), caching for ignore
instances to improve performance
([#16185](https://github.com/google-gemini/gemini-cli/pull/16185) by
[@EricRahm](https://github.com/EricRahm)), and enhanced retry mechanisms
([#16489](https://github.com/google-gemini/gemini-cli/pull/16489) by
[@sehoon38](https://github.com/sehoon38)).
- **Bug Fixes and Stability:** We've squashed numerous bugs across the CLI,
core, and workflows, addressing issues with subagent delegation, unicode
character crashes, and sticky header regressions.
## Announcements: v0.24.0 - 2026-01-14
- **Agent Skills:** We've introduced significant advancements in Agent Skills.
This includes initial documentation and tutorials to help you get started,
alongside enhanced support for remote agents, allowing for more distributed
and powerful automation within Gemini CLI.
([#15869](https://github.com/google-gemini/gemini-cli/pull/15869) by
[@NTaylorMullen](https://github.com/NTaylorMullen)),
([#16013](https://github.com/google-gemini/gemini-cli/pull/16013) by
[@adamweidman](https://github.com/adamweidman))
- **Improved UI/UX:** The user interface has received several updates, featuring
visual indicators for hook execution, a more refined display for settings, and
the ability to use the Tab key to effortlessly switch focus between the shell
and input areas.
([#15408](https://github.com/google-gemini/gemini-cli/pull/15408) by
[@abhipatel12](https://github.com/abhipatel12)),
([#14332](https://github.com/google-gemini/gemini-cli/pull/14332) by
[@galz10](https://github.com/galz10))
- **Enhanced Security:** Security has been a major focus, with default folder
trust now set to untrusted for increased safety. The Policy Engine has been
improved to allow specific modes in user and administrator policies, and
granular allowlisting for shell commands has been implemented, providing finer
control over tool execution.
([#15943](https://github.com/google-gemini/gemini-cli/pull/15943) by
[@galz10](https://github.com/galz10)),
([#15977](https://github.com/google-gemini/gemini-cli/pull/15977) by
[@NTaylorMullen](https://github.com/NTaylorMullen))
- **Core Functionality:** This release includes a mandatory MessageBus
injection, marking Phase 3 of a hard migration to a more robust internal
communication system. We've also added support for built-in skills with the
CLI itself, and enhanced model routing to effectively utilize subagents.
([#15776](https://github.com/google-gemini/gemini-cli/pull/15776) by
[@abhipatel12](https://github.com/abhipatel12)),
([#16300](https://github.com/google-gemini/gemini-cli/pull/16300) by
[@NTaylorMullen](https://github.com/NTaylorMullen))
- **Terminal Features:** Terminal interactions are more seamless with new
features like OSC 52 paste support, along with fixes for Windows clipboard
paste issues and general improvements to pasting in Windows terminals.
([#15336](https://github.com/google-gemini/gemini-cli/pull/15336) by
[@scidomino](https://github.com/scidomino)),
([#15932](https://github.com/google-gemini/gemini-cli/pull/15932) by
[@scidomino](https://github.com/scidomino))
- **New Commands:** To manage the new features, we've added several new
commands: `/agents refresh` to update agent configurations, `/skills reload`
to refresh skill definitions, and `/skills install/uninstall` for easier
management of your Agent Skills.
([#16204](https://github.com/google-gemini/gemini-cli/pull/16204) by
[@NTaylorMullen](https://github.com/NTaylorMullen)),
([#15865](https://github.com/google-gemini/gemini-cli/pull/15865) by
[@NTaylorMullen](https://github.com/NTaylorMullen)),
([#16377](https://github.com/google-gemini/gemini-cli/pull/16377) by
[@NTaylorMullen](https://github.com/NTaylorMullen))
## Announcements: v0.23.0 - 2026-01-07
- 🎉 **Experimental Agent Skills Support in Preview:** Gemini CLI now supports
[Agent Skills](https://agentskills.io/home) in our preview builds. This is an
early preview where were looking for feedback!
- Install Preview: `npm install -g @google/gemini-cli@preview`
- Enable in `/settings`
- Docs:
[https://geminicli.com/docs/cli/skills/](https://geminicli.com/docs/cli/skills/)
- **Gemini CLI wrapped:** Run `npx gemini-wrapped` to visualize your usage
stats, top models, languages, and more!
- **Windows clipboard image support:** Windows users can now paste images
directly from their clipboard into the CLI using `Alt`+`V`.
([pr](https://github.com/google-gemini/gemini-cli/pull/13997) by
[@sgeraldes](https://github.com/sgeraldes))
- **Terminal background color detection:** Automatically optimizes your
terminal's background color to select compatible themes and provide
accessibility warnings.
([pr](https://github.com/google-gemini/gemini-cli/pull/15132) by
[@jacob314](https://github.com/jacob314))
- **Session logout:** Use the new `/logout` command to instantly clear
credentials and reset your authentication state for seamless account
switching. ([pr](https://github.com/google-gemini/gemini-cli/pull/13383) by
[@CN-Scars](https://github.com/CN-Scars))
## Announcements: v0.22.0 - 2025-12-22
- 🎉**Free Tier + Gemini 3:** Free tier users now all have access to Gemini 3
Pro & Flash. Enable in `/settings` by toggling "Preview Features" to `true`.
- 🎉**Gemini CLI + Colab:** Gemini CLI is now pre-installed. Can be used
headlessly in notebook cells or interactively in the built-in terminal
([pic](https://imgur.com/a/G0Tn7vi))
- 🎉**Gemini CLI Extensions:**
- **Conductor:** Planning++, Gemini works with you to build out a detailed
plan, pull in extra details as needed, ultimately to give the LLM guardrails
with artifacts. Measure twice, implement once!
`gemini extensions install https://github.com/gemini-cli-extensions/conductor`
Blog:
[https://developers.googleblog.com/conductor-introducing-context-driven-development-for-gemini-cli/](https://developers.googleblog.com/conductor-introducing-context-driven-development-for-gemini-cli/)
- **Endor Labs:** Perform code analysis, vulnerability scanning, and
dependency checks using natural language.
`gemini extensions install https://github.com/endorlabs/gemini-extension`
## Announcements: v0.21.0 - 2025-12-15
- **⚡️⚡️⚡️ Gemini 3 Flash + Gemini CLI:** Better, faster and cheaper than 2.5
Pro - and in some scenarios better than 3 Pro! For paid tiers + free tier
users who were on the wait list enable **Preview Features** in `/settings.`
- For more information:
[Gemini 3 Flash is now available in Gemini CLI](https://developers.googleblog.com/gemini-3-flash-is-now-available-in-gemini-cli/).
- 🎉 Gemini CLI Extensions:
- Rill: Utilize natural language to analyze Rill data, enabling the
exploration of metrics and trends without the need for manual queries.
`gemini extensions install https://github.com/rilldata/rill-gemini-extension`
- Browserbase: Interact with web pages, take screenshots, extract information,
and perform automated actions with atomic precision.
`gemini extensions install https://github.com/browserbase/mcp-server-browserbase`
- Quota Visibility: The `/stats` command now displays quota information for all
available models, including those not used in the current session. (@sehoon38)
- Fuzzy Setting Search: Users can now quickly find settings using fuzzy search
within the settings dialog. (@sehoon38)
- MCP Resource Support: Users can now discover, view, and search through
resources using the @ command. (@MrLesk)
- Auto-execute Simple Slash Commands: Simple slash commands are now executed
immediately on enter. (@jackwotherspoon)
## Announcements: v0.20.0 - 2025-12-01
- **Multi-file Drag & Drop:** Users can now drag and drop multiple files into
the terminal, and the CLI will automatically prefix each valid path with `@`.
([pr](https://github.com/google-gemini/gemini-cli/pull/14832) by
[@jackwotherspoon](https://github.com/jackwotherspoon))
- **Persistent "Always Allow" Policies:** Users can now save "Always Allow"
decisions for tool executions, with granular control over specific shell
commands and multi-cloud platform tools.
([pr](https://github.com/google-gemini/gemini-cli/pull/14737) by
[@allenhutchison](https://github.com/allenhutchison))
## Announcements: v0.19.0 - 2025-11-24
- 🎉 **New extensions:**
- **Eleven Labs:** Create, play, manage your audio play tracks with the Eleven
Labs Gemini CLI extension:
`gemini extensions install https://github.com/elevenlabs/elevenlabs-mcp`
- **Zed integration:** Users can now leverage Gemini 3 within the Zed
integration after enabling "Preview Features" in their CLIs `/settings`.
([pr](https://github.com/google-gemini/gemini-cli/pull/13398) by
[@benbrandt](https://github.com/benbrandt))
- **Interactive shell:**
- **Click-to-Focus:** When "Use Alternate Buffer" setting is enabled, users
can click within the embedded shell output to focus it for input.
([pr](https://github.com/google-gemini/gemini-cli/pull/13341) by
[@galz10](https://github.com/galz10))
- **Loading phrase:** Clearly indicates when the interactive shell is awaiting
user input. ([vid](https://imgur.com/a/kjK8bUK),
[pr](https://github.com/google-gemini/gemini-cli/pull/12535) by
[@jackwotherspoon](https://github.com/jackwotherspoon))
## Announcements: v0.18.0 - 2025-11-17
- 🎉 **New extensions:**
- **Google Workspace**: Integrate Gemini CLI with your Workspace data. Write
docs, build slides, chat with others or even get your calc on in sheets:
`gemini extensions install https://github.com/gemini-cli-extensions/workspace`
- Blog:
[https://allen.hutchison.org/2025/11/19/bringing-the-office-to-the-terminal/](https://allen.hutchison.org/2025/11/19/bringing-the-office-to-the-terminal/)
- **Redis:** Manage and search data in Redis with natural language:
`gemini extensions install https://github.com/redis/mcp-redis`
- **Anomalo:** Query your data warehouse table metadata and quality status
through commands and natural language:
`gemini extensions install https://github.com/datagravity-ai/anomalo-gemini-extension`
- **Experimental permission improvements:** We are now experimenting with a new
policy engine in Gemini CLI. This allows users and administrators to create
fine-grained policy for tool calls. Currently behind a flag. See
[policy engine documentation](../core/policy-engine.md) for more information.
- Blog:
[https://allen.hutchison.org/2025/11/26/the-guardrails-of-autonomy/](https://allen.hutchison.org/2025/11/26/the-guardrails-of-autonomy/)
- **Gemini 3 support for paid:** Gemini 3 support has been rolled out to all API
key, Google AI Pro or Google AI Ultra (for individuals, not businesses) and
Gemini Code Assist Enterprise users. Enable it via `/settings` and toggling on
**Preview Features**.
- **Updated UI rollback:** Weve temporarily rolled back our updated UI to give
it more time to bake. This means for a time you wont have embedded scrolling
or mouse support. You can re-enable with `/settings` -> **Use Alternate Screen
Buffer** -> `true`.
- **Model in history:** Users can now toggle in `/settings` to display model in
their chat history. ([gif](https://imgur.com/a/uEmNKnQ),
[pr](https://github.com/google-gemini/gemini-cli/pull/13034) by
[@scidomino](https://github.com/scidomino))
- **Multi-uninstall:** Users can now uninstall multiple extensions with a single
command. ([pic](https://imgur.com/a/9Dtq8u2),
[pr](https://github.com/google-gemini/gemini-cli/pull/13016) by
[@JayadityaGit](https://github.com/JayadityaGit))
## Announcements: v0.16.0 - 2025-11-10
- **Gemini 3 + Gemini CLI:** launch 🚀🚀🚀
- **Data Commons Gemini CLI Extension** - A new Data Commons Gemini CLI
extension that lets you query open-source statistical data from
datacommons.org. **To get started, you'll need a Data Commons API key and uv
installed**. These and other details to get you started with the extension can
be found at
[https://github.com/gemini-cli-extensions/datacommons](https://github.com/gemini-cli-extensions/datacommons).
## Announcements: v0.15.0 - 2025-11-03
- **🎉 Seamless scrollable UI and mouse support:** Weve given Gemini CLI a
major facelift to make your terminal experience smoother and much more
polished. You now get a flicker-free display with sticky headers that keep
important context visible and a stable input prompt that doesn't jump around.
We even added mouse support so you can click right where you need to type!
- **🎉 Seamless scrollable UI & mouse support:** Weve given Gemini CLI a major
facelift to make your terminal experience smoother and much more polished. You
now get a flicker-free display with sticky headers that keep important context
visible and a stable input prompt that doesn't jump around. We even added
mouse support so you can click right where you need to type!
([gif](https://imgur.com/a/O6qc7bx),
[@jacob314](https://github.com/jacob314)).
- **Announcement:**
@@ -299,7 +49,7 @@ on GitHub.
correctly. ([pr](https://github.com/google-gemini/gemini-cli/pull/12186) by
[@kevinjwang1](https://github.com/kevinjwang1)).
## Announcements: v0.12.0 - 2025-10-27
## v0.12.0 - Gemini CLI weekly update - 2025-10-27
![Codebase investigator subagent in Gemini CLI.](https://i.imgur.com/4J1njsx.png)
@@ -362,7 +112,7 @@ on GitHub.
([pr](https://github.com/google-gemini/gemini-cli/pull/11593) by
[@joshualitt](https://github.com/joshualitt)).
## Announcements: v0.11.0 - 2025-10-20
## v0.11.0 - Gemini CLI weekly update - 2025-10-20
![Gemini CLI and Jules](https://storage.googleapis.com/gweb-developer-goog-blog-assets/images/Jules_Extension_-_Blog_Header_O346JNt.original.png)
@@ -405,7 +155,7 @@ on GitHub.
([pr](https://github.com/google-gemini/gemini-cli/pull/11318) by
[@allenhutchison](https://github.com/allenhutchison))
## Announcements: v0.10.0 - 2025-10-13
## v0.10.0 - Gemini CLI weekly update - 2025-10-13
- **Polish:** The team has been heads down bug fixing and investing heavily into
polishing existing flows, tools, and interactions.
@@ -422,7 +172,7 @@ on GitHub.
([pr](https://github.com/google-gemini/gemini-cli/pull/10819) by
[@jerop](https://github.com/jerop)).
## Announcements: v0.9.0 - 2025-10-06
## v0.9.0 - Gemini CLI weekly update - 2025-10-06
- 🎉 **Interactive Shell:** Run interactive commands like `vim`, `rebase -i`, or
even `gemini` 😎 directly in Gemini CLI:
@@ -447,7 +197,7 @@ on GitHub.
([pr](https://github.com/google-gemini/gemini-cli/pull/10108) by
[@sgnagnarella](https://github.com/sgnagnarella))
## Announcements: v0.8.0 - 2025-09-29
## v0.8.0 - Gemini CLI weekly update - 2025-09-29
- 🎉 **Announcing Gemini CLI Extensions** 🎉
- Completely customize your Gemini CLI experience to fit your workflow.
@@ -486,7 +236,7 @@ on GitHub.
changes, smaller features, UI updates, reliability and bug fixes + general
polish made it in this week!
## Announcements: v0.7.0 - 2025-09-22
## v0.7.0 - Gemini CLI weekly update - 2025-09-22
- 🎉**Build your own Gemini CLI IDE plugin:** We've published a spec for
creating IDE plugins to enable rich context-aware experiences and native
@@ -524,7 +274,7 @@ on GitHub.
changes, smaller features, UI updates, reliability and bug fixes + general
polish made it in this week!
## Announcements: v0.6.0 - 2025-09-15
## v0.6.0 - Gemini CLI weekly update - 2025-09-15
- 🎉 **Higher limits for Google AI Pro and Ultra subscribers:** Were psyched to
finally announce that Google AI Pro and AI Ultra subscribers now get access to
@@ -605,7 +355,7 @@ on GitHub.
changes, smaller features, UI updates, reliability and bug fixes + general
polish made it in this week!
## Announcements: v0.5.0 - 2025-09-08
## v0.5.0 - Gemini CLI weekly update - 2025-09-08
- 🎉**FastMCP + Gemini CLI**🎉: Quickly install and manage your Gemini CLI MCP
servers with FastMCP ([video](https://imgur.com/a/m8QdCPh),
@@ -650,7 +400,7 @@ on GitHub.
changes, smaller features, UI updates, reliability and bug fixes + general
polish made it in this week!
## Announcements: v0.4.0 - 2025-09-01
## v0.4.0 - Gemini CLI weekly update - 2025-09-01
- 🎉**Gemini CLI CloudRun and Security Integrations**🎉: Automate app deployment
and security analysis with CloudRun and Security extension integrations. Once
-370
View File
@@ -1,370 +0,0 @@
# Latest stable release: v0.25.0
Released: January 20, 2026
For most users, our latest stable release is the recommended release. Install
the latest stable version with:
```
npm install -g @google/gemini-cli
```
## Highlights
- **Skills and Agents Improvements:** Enhanced `activate_skill` tool, new
`pr-creator` skill, default enablement of skills, improved `cli_help` agent,
and a new `/agents refresh` command.
- **UI/UX Refinements:** Transparent feedback for skills, ability to switch
focus between shell and input with Tab, and dynamic terminal tab titles.
- **Core Functionality & Performance:** Support for built-in agent skills,
refined Gemini 3 system instructions, caching ignore instances for
performance, and improved retry mechanisms.
- **Bug Fixes and Stability:** Numerous bug fixes across the CLI, core, and
workflows, including issues with subagent delegation, unicode character
crashes, and sticky header regressions.
## What's Changed
- feat(core): improve activate_skill tool and use lowercase XML tags by
@NTaylorMullen in
[#16009](https://github.com/google-gemini/gemini-cli/pull/16009)
- Add initiation method telemetry property by @gundermanc in
[#15818](https://github.com/google-gemini/gemini-cli/pull/15818)
- chore(release): bump version to 0.25.0-nightly.20260107.59a18e710 by
@gemini-cli-robot in
[#16048](https://github.com/google-gemini/gemini-cli/pull/16048)
- Hx support by @kevinfjiang in
[#16032](https://github.com/google-gemini/gemini-cli/pull/16032)
- [Skills] Foundation: Centralize management logic and feedback rendering by
@NTaylorMullen in
[#15952](https://github.com/google-gemini/gemini-cli/pull/15952)
- Introduce GEMINI_CLI_HOME for strict test isolation by @NTaylorMullen in
[#15907](https://github.com/google-gemini/gemini-cli/pull/15907)
- [Skills] Multi-scope skill enablement and shadowing fix by @NTaylorMullen in
[#15953](https://github.com/google-gemini/gemini-cli/pull/15953)
- policy: extract legacy policy from core tool scheduler to policy engine by
@abhipatel12 in
[#15902](https://github.com/google-gemini/gemini-cli/pull/15902)
- Enhance TestRig with process management and timeouts by @NTaylorMullen in
[#15908](https://github.com/google-gemini/gemini-cli/pull/15908)
- Update troubleshooting doc for UNABLE_TO_GET_ISSUER_CERT_LOCALLY by @sehoon38
in [#16069](https://github.com/google-gemini/gemini-cli/pull/16069)
- Add keytar to dependencies by @chrstnb in
[#15928](https://github.com/google-gemini/gemini-cli/pull/15928)
- Simplify extension settings command by @chrstnb in
[#16001](https://github.com/google-gemini/gemini-cli/pull/16001)
- feat(admin): implement extensions disabled by @skeshive in
[#16024](https://github.com/google-gemini/gemini-cli/pull/16024)
- Core data structure updates for Rewind functionality by @Adib234 in
[#15714](https://github.com/google-gemini/gemini-cli/pull/15714)
- feat(hooks): simplify hook firing with HookSystem wrapper methods by @ved015
in [#15982](https://github.com/google-gemini/gemini-cli/pull/15982)
- Add exp.gws_experiment field to LogEventEntry by @gsquared94 in
[#16062](https://github.com/google-gemini/gemini-cli/pull/16062)
- Revert "feat(admin): implement extensions disabled" by @chrstnb in
[#16082](https://github.com/google-gemini/gemini-cli/pull/16082)
- feat(core): Decouple enabling hooks UI from subsystem. by @joshualitt in
[#16074](https://github.com/google-gemini/gemini-cli/pull/16074)
- docs: add docs for hooks + extensions by @abhipatel12 in
[#16073](https://github.com/google-gemini/gemini-cli/pull/16073)
- feat(core): Preliminary changes for subagent model routing. by @joshualitt in
[#16035](https://github.com/google-gemini/gemini-cli/pull/16035)
- Optimize CI workflow: Parallelize jobs and cache linters by @NTaylorMullen in
[#16054](https://github.com/google-gemini/gemini-cli/pull/16054)
- Add option to fallback for capacity errors in ProQuotaDi… by @sehoon38 in
[#16050](https://github.com/google-gemini/gemini-cli/pull/16050)
- feat: add confirmation details support + jsonrpc vs http rest support by
@adamfweidman in
[#16079](https://github.com/google-gemini/gemini-cli/pull/16079)
- fix(workflows): fix and limit labels for pr-triage.sh script by @jacob314 in
[#16096](https://github.com/google-gemini/gemini-cli/pull/16096)
- Fix and rename introspection agent -> cli help agent by @scidomino in
[#16097](https://github.com/google-gemini/gemini-cli/pull/16097)
- Docs: Changelogs update 20260105 by @jkcinouye in
[#15937](https://github.com/google-gemini/gemini-cli/pull/15937)
- enable cli_help agent by default by @scidomino in
[#16100](https://github.com/google-gemini/gemini-cli/pull/16100)
- Optimize json-output tests with mock responses by @NTaylorMullen in
[#16102](https://github.com/google-gemini/gemini-cli/pull/16102)
- Fix CI for forks by @scidomino in
[#16113](https://github.com/google-gemini/gemini-cli/pull/16113)
- Reduce nags about PRs that reference issues but don't fix them. by @jacob314
in [#16112](https://github.com/google-gemini/gemini-cli/pull/16112)
- feat(cli): add filepath autosuggestion after slash commands by @jasmeetsb in
[#14738](https://github.com/google-gemini/gemini-cli/pull/14738)
- Add upgrade option for paid users by @cayden-google in
[#15978](https://github.com/google-gemini/gemini-cli/pull/15978)
- [Skills] UX Polishing: Transparent feedback and CLI refinements by
@NTaylorMullen in
[#15954](https://github.com/google-gemini/gemini-cli/pull/15954)
- Polish: Move 'Failed to load skills' warning to debug logs by @NTaylorMullen
in [#16142](https://github.com/google-gemini/gemini-cli/pull/16142)
- feat(cli): export chat history in /bug and prefill GitHub issue by
@NTaylorMullen in
[#16115](https://github.com/google-gemini/gemini-cli/pull/16115)
- bug(core): fix issue with overrides to bases. by @joshualitt in
[#15255](https://github.com/google-gemini/gemini-cli/pull/15255)
- enableInteractiveShell for external tooling relying on a2a server by
@DavidAPierce in
[#16080](https://github.com/google-gemini/gemini-cli/pull/16080)
- Reapply "feat(admin): implement extensions disabled" (#16082) by @skeshive in
[#16109](https://github.com/google-gemini/gemini-cli/pull/16109)
- bug(core): Fix spewie getter in hookTranslator.ts by @joshualitt in
[#16108](https://github.com/google-gemini/gemini-cli/pull/16108)
- feat(hooks): add mcp_context to BeforeTool and AfterTool hook inputs by @vrv
in [#15656](https://github.com/google-gemini/gemini-cli/pull/15656)
- Add extension linking capabilities in cli by @kevinjwang1 in
[#16040](https://github.com/google-gemini/gemini-cli/pull/16040)
- Update the page's title to be consistent and show in site. by @kschaab in
[#16174](https://github.com/google-gemini/gemini-cli/pull/16174)
- docs: correct typo in bufferFastReturn JSDoc ("accomodate" → "accommodate") by
@minglu7 in [#16056](https://github.com/google-gemini/gemini-cli/pull/16056)
- fix: typo in MCP servers settings description by @alphanota in
[#15929](https://github.com/google-gemini/gemini-cli/pull/15929)
- fix: yolo should auto allow redirection by @abhipatel12 in
[#16183](https://github.com/google-gemini/gemini-cli/pull/16183)
- fix(cli): disableYoloMode shouldn't enforce default approval mode against args
by @psinha40898 in
[#16155](https://github.com/google-gemini/gemini-cli/pull/16155)
- feat: add native Sublime Text support to IDE detection by @phreakocious in
[#16083](https://github.com/google-gemini/gemini-cli/pull/16083)
- refactor(core): extract ToolModificationHandler from scheduler by @abhipatel12
in [#16118](https://github.com/google-gemini/gemini-cli/pull/16118)
- Add support for Antigravity terminal in terminal setup utility by @raky291 in
[#16051](https://github.com/google-gemini/gemini-cli/pull/16051)
- feat(core): Wire up model routing to subagents. by @joshualitt in
[#16043](https://github.com/google-gemini/gemini-cli/pull/16043)
- feat(cli): add /agents slash command to list available agents by @adamfweidman
in [#16182](https://github.com/google-gemini/gemini-cli/pull/16182)
- docs(cli): fix includeDirectories nesting in configuration.md by @maru0804 in
[#15067](https://github.com/google-gemini/gemini-cli/pull/15067)
- feat: implement file system reversion utilities for rewind by @Adib234 in
[#15715](https://github.com/google-gemini/gemini-cli/pull/15715)
- Always enable redaction in GitHub actions. by @gundermanc in
[#16200](https://github.com/google-gemini/gemini-cli/pull/16200)
- fix: remove unsupported 'enabled' key from workflow config by @Han5991 in
[#15611](https://github.com/google-gemini/gemini-cli/pull/15611)
- docs: Remove redundant and duplicate documentation files by @liqzheng in
[#14699](https://github.com/google-gemini/gemini-cli/pull/14699)
- docs: shorten run command and use published version by @dsherret in
[#16172](https://github.com/google-gemini/gemini-cli/pull/16172)
- test(command-registry): increase initialization test timeout by @wszqkzqk in
[#15979](https://github.com/google-gemini/gemini-cli/pull/15979)
- Ensure TERM is set to xterm-256color by @falouu in
[#15828](https://github.com/google-gemini/gemini-cli/pull/15828)
- The telemetry.js script should handle paths that contain spaces by @JohnJAS in
[#12078](https://github.com/google-gemini/gemini-cli/pull/12078)
- ci: guard links workflow from running on forks by @wtanaka in
[#15461](https://github.com/google-gemini/gemini-cli/pull/15461)
- ci: guard nightly release workflow from running on forks by @wtanaka in
[#15463](https://github.com/google-gemini/gemini-cli/pull/15463)
- Support @ suggestions for subagenets by @sehoon38 in
[#16201](https://github.com/google-gemini/gemini-cli/pull/16201)
- feat(hooks): Support explicit stop and block execution control in model hooks
by @SandyTao520 in
[#15947](https://github.com/google-gemini/gemini-cli/pull/15947)
- Refine Gemini 3 system instructions to reduce model verbosity by
@NTaylorMullen in
[#16139](https://github.com/google-gemini/gemini-cli/pull/16139)
- chore: clean up unused models and use consts by @sehoon38 in
[#16246](https://github.com/google-gemini/gemini-cli/pull/16246)
- Always enable bracketed paste by @scidomino in
[#16179](https://github.com/google-gemini/gemini-cli/pull/16179)
- refactor: migrate clearCommand hook calls to HookSystem by @ved015 in
[#16157](https://github.com/google-gemini/gemini-cli/pull/16157)
- refactor: migrate app containter hook calls to hook system by @ishaanxgupta in
[#16161](https://github.com/google-gemini/gemini-cli/pull/16161)
- Show settings source in extensions lists by @chrstnb in
[#16207](https://github.com/google-gemini/gemini-cli/pull/16207)
- feat(skills): add pr-creator skill and enable skills by @NTaylorMullen in
[#16232](https://github.com/google-gemini/gemini-cli/pull/16232)
- fix: handle Shift+Space in Kitty keyboard protocol terminals by @tt-a1i in
[#15767](https://github.com/google-gemini/gemini-cli/pull/15767)
- feat(core, ui): Add /agents refresh command. by @joshualitt in
[#16204](https://github.com/google-gemini/gemini-cli/pull/16204)
- feat(core): add local experiments override via GEMINI_EXP by @kevin-ramdass in
[#16181](https://github.com/google-gemini/gemini-cli/pull/16181)
- feat(ui): reduce home directory warning noise and add opt-out setting by
@NTaylorMullen in
[#16229](https://github.com/google-gemini/gemini-cli/pull/16229)
- refactor: migrate chatCompressionService to use HookSystem by @ved015 in
[#16259](https://github.com/google-gemini/gemini-cli/pull/16259)
- fix: properly use systemMessage for hooks in UI by @jackwotherspoon in
[#16250](https://github.com/google-gemini/gemini-cli/pull/16250)
- Infer modifyOtherKeys support by @scidomino in
[#16270](https://github.com/google-gemini/gemini-cli/pull/16270)
- feat(core): Cache ignore instances for performance by @EricRahm in
[#16185](https://github.com/google-gemini/gemini-cli/pull/16185)
- feat: apply remote admin settings (no-op) by @skeshive in
[#16106](https://github.com/google-gemini/gemini-cli/pull/16106)
- Autogenerate docs/cli/settings.md docs/getting-started/configuration.md was
already autogenerated but settings.md was not. by @jacob314 in
[#14408](https://github.com/google-gemini/gemini-cli/pull/14408)
- refactor(config): remove legacy V1 settings migration logic by @galz10 in
[#16252](https://github.com/google-gemini/gemini-cli/pull/16252)
- Fix an issue where the agent stops prematurely by @gundermanc in
[#16269](https://github.com/google-gemini/gemini-cli/pull/16269)
- Update system prompt to prefer non-interactive commands by @NTaylorMullen in
[#16117](https://github.com/google-gemini/gemini-cli/pull/16117)
- Update ink version to 6.4.7 by @jacob314 in
[#16284](https://github.com/google-gemini/gemini-cli/pull/16284)
- Support for Built-in Agent Skills by @NTaylorMullen in
[#16045](https://github.com/google-gemini/gemini-cli/pull/16045)
- fix(skills): remove "Restart required" message from non-interactive commands
by @NTaylorMullen in
[#16307](https://github.com/google-gemini/gemini-cli/pull/16307)
- remove unused sessionHookTriggers and exports by @ved015 in
[#16324](https://github.com/google-gemini/gemini-cli/pull/16324)
- Triage action cleanup by @bdmorgan in
[#16319](https://github.com/google-gemini/gemini-cli/pull/16319)
- fix: Add event-driven trigger to issue triage workflow by @bdmorgan in
[#16334](https://github.com/google-gemini/gemini-cli/pull/16334)
- fix(workflows): resolve triage workflow failures and actionlint errors by
@bdmorgan in [#16338](https://github.com/google-gemini/gemini-cli/pull/16338)
- docs: add note about experimental hooks by @abhipatel12 in
[#16337](https://github.com/google-gemini/gemini-cli/pull/16337)
- feat(cli): implement passive activity logger for session analysis by
@SandyTao520 in
[#15829](https://github.com/google-gemini/gemini-cli/pull/15829)
- feat(cli): add /chat debug command for nightly builds by @abhipatel12 in
[#16339](https://github.com/google-gemini/gemini-cli/pull/16339)
- style: format pr-creator skill by @NTaylorMullen in
[#16381](https://github.com/google-gemini/gemini-cli/pull/16381)
- feat(cli): Hooks enable-all/disable-all feature with dynamic status by
@AbdulTawabJuly in
[#15552](https://github.com/google-gemini/gemini-cli/pull/15552)
- fix(core): ensure silent local subagent delegation while allowing remote
confirmation by @adamfweidman in
[#16395](https://github.com/google-gemini/gemini-cli/pull/16395)
- Markdown w/ Frontmatter Agent Parser by @sehoon38 in
[#16094](https://github.com/google-gemini/gemini-cli/pull/16094)
- Fix crash on unicode character by @chrstnb in
[#16420](https://github.com/google-gemini/gemini-cli/pull/16420)
- Attempt to resolve OOM w/ useMemo on history items by @chrstnb in
[#16424](https://github.com/google-gemini/gemini-cli/pull/16424)
- fix(core): ensure sub-agent schema and prompt refresh during runtime by
@adamfweidman in
[#16409](https://github.com/google-gemini/gemini-cli/pull/16409)
- Update extension examples by @chrstnb in
[#16274](https://github.com/google-gemini/gemini-cli/pull/16274)
- revert the change that was recently added from a fix by @sehoon38 in
[#16390](https://github.com/google-gemini/gemini-cli/pull/16390)
- Add other hook wrapper methods to hooksystem by @ved015 in
[#16361](https://github.com/google-gemini/gemini-cli/pull/16361)
- feat: introduce useRewindLogic hook for conversation history navigation by
@Adib234 in [#15716](https://github.com/google-gemini/gemini-cli/pull/15716)
- docs: Fix formatting issue in memport documentation by @wanglc02 in
[#14774](https://github.com/google-gemini/gemini-cli/pull/14774)
- fix(policy): enhance shell command safety and parsing by @allenhutchison in
[#15034](https://github.com/google-gemini/gemini-cli/pull/15034)
- fix(core): avoid 'activate_skill' re-registration warning by @NTaylorMullen in
[#16398](https://github.com/google-gemini/gemini-cli/pull/16398)
- perf(workflows): optimize PR triage script for faster execution by @bdmorgan
in [#16355](https://github.com/google-gemini/gemini-cli/pull/16355)
- feat(admin): prompt user to restart the CLI if they change auth to oauth
mid-session or don't have auth type selected at start of session by @skeshive
in [#16426](https://github.com/google-gemini/gemini-cli/pull/16426)
- Update cli-help agent's system prompt in sub-agents section by @sehoon38 in
[#16441](https://github.com/google-gemini/gemini-cli/pull/16441)
- Revert "Update extension examples" by @chrstnb in
[#16442](https://github.com/google-gemini/gemini-cli/pull/16442)
- Fix: add back fastreturn support by @scidomino in
[#16440](https://github.com/google-gemini/gemini-cli/pull/16440)
- feat(a2a): Introduce /memory command for a2a server by @cocosheng-g in
[#14456](https://github.com/google-gemini/gemini-cli/pull/14456)
- docs: fix broken internal link by using relative path by @Gong-Mi in
[#15371](https://github.com/google-gemini/gemini-cli/pull/15371)
- migrate yolo/auto-edit keybindings by @scidomino in
[#16457](https://github.com/google-gemini/gemini-cli/pull/16457)
- feat(cli): add install and uninstall commands for skills by @NTaylorMullen in
[#16377](https://github.com/google-gemini/gemini-cli/pull/16377)
- feat(ui): use Tab to switch focus between shell and input by @jacob314 in
[#14332](https://github.com/google-gemini/gemini-cli/pull/14332)
- feat(core): support shipping built-in skills with the CLI by @NTaylorMullen in
[#16300](https://github.com/google-gemini/gemini-cli/pull/16300)
- Collect hardware details telemetry. by @gundermanc in
[#16119](https://github.com/google-gemini/gemini-cli/pull/16119)
- feat(agents): improve UI feedback and parser reliability by @NTaylorMullen in
[#16459](https://github.com/google-gemini/gemini-cli/pull/16459)
- Migrate keybindings by @scidomino in
[#16460](https://github.com/google-gemini/gemini-cli/pull/16460)
- feat(cli): cleanup activity logs alongside session files by @SandyTao520 in
[#16399](https://github.com/google-gemini/gemini-cli/pull/16399)
- feat(cli): implement dynamic terminal tab titles for CLI status by
@NTaylorMullen in
[#16378](https://github.com/google-gemini/gemini-cli/pull/16378)
- feat(core): add disableLLMCorrection setting to skip auto-correction in edit
tools by @SandyTao520 in
[#16000](https://github.com/google-gemini/gemini-cli/pull/16000)
- fix: Set both tab and window title instead of just window title by
@NTaylorMullen in
[#16464](https://github.com/google-gemini/gemini-cli/pull/16464)
- fix(policy): ensure MCP policies match unqualified names in non-interactive
mode by @NTaylorMullen in
[#16490](https://github.com/google-gemini/gemini-cli/pull/16490)
- fix(cli): refine 'Action Required' indicator and focus hints by @NTaylorMullen
in [#16497](https://github.com/google-gemini/gemini-cli/pull/16497)
- Refactor beforeAgent and afterAgent hookEvents to follow desired output by
@ved015 in [#16495](https://github.com/google-gemini/gemini-cli/pull/16495)
- feat(agents): clarify mandatory YAML frontmatter for sub-agents by
@NTaylorMullen in
[#16515](https://github.com/google-gemini/gemini-cli/pull/16515)
- docs(telemetry): add Google Cloud Monitoring dashboard documentation by @jerop
in [#16520](https://github.com/google-gemini/gemini-cli/pull/16520)
- Implement support for subagents as extensions. by @gundermanc in
[#16473](https://github.com/google-gemini/gemini-cli/pull/16473)
- refactor: make baseTimestamp optional in addItem and remove redundant calls by
@sehoon38 in [#16471](https://github.com/google-gemini/gemini-cli/pull/16471)
- Improve key binding names and descriptions by @scidomino in
[#16529](https://github.com/google-gemini/gemini-cli/pull/16529)
- feat(core, cli): Add support for agents in settings.json. by @joshualitt in
[#16433](https://github.com/google-gemini/gemini-cli/pull/16433)
- fix(cli): fix 'gemini skills install' unknown argument error by @NTaylorMullen
in [#16537](https://github.com/google-gemini/gemini-cli/pull/16537)
- chore(ui): optimize AgentsStatus layout with dense list style and group
separation by @adamfweidman in
[#16545](https://github.com/google-gemini/gemini-cli/pull/16545)
- fix(cli): allow @ file selector on slash command lines by @galz10 in
[#16370](https://github.com/google-gemini/gemini-cli/pull/16370)
- fix(ui): resolve sticky header regression in tool messages by @jacob314 in
[#16514](https://github.com/google-gemini/gemini-cli/pull/16514)
- feat(core): Align internal agent settings with configs exposed through
settings.json by @joshualitt in
[#16458](https://github.com/google-gemini/gemini-cli/pull/16458)
- fix(cli): copy uses OSC52 only in SSH/WSL by @assagman in
[#16554](https://github.com/google-gemini/gemini-cli/pull/16554)
- docs(skills): clarify skill directory structure and file location by
@NTaylorMullen in
[#16532](https://github.com/google-gemini/gemini-cli/pull/16532)
- Fix: make ctrl+x use preferred editor by @scidomino in
[#16556](https://github.com/google-gemini/gemini-cli/pull/16556)
- fix(core): Resolve race condition in tool response reporting by @abhipatel12
in [#16557](https://github.com/google-gemini/gemini-cli/pull/16557)
- feat(ui): highlight persist mode status in ModelDialog by @sehoon38 in
[#16483](https://github.com/google-gemini/gemini-cli/pull/16483)
- refactor: clean up A2A task output for users and LLMs by @adamfweidman in
[#16561](https://github.com/google-gemini/gemini-cli/pull/16561)
- feat(core/ui): enhance retry mechanism and UX by @sehoon38 in
[#16489](https://github.com/google-gemini/gemini-cli/pull/16489)
- Modernize MaxSizedBox to use and ResizeObservers by @jacob314 in
[#16565](https://github.com/google-gemini/gemini-cli/pull/16565)
- Behavioral evals framework. by @gundermanc in
[#16047](https://github.com/google-gemini/gemini-cli/pull/16047)
- Aggregate test results. by @gundermanc in
[#16581](https://github.com/google-gemini/gemini-cli/pull/16581)
- feat(admin): support admin-enforced settings for Agent Skills by
@NTaylorMullen in
[#16406](https://github.com/google-gemini/gemini-cli/pull/16406)
- fix(patch): cherry-pick cfdc4cf to release/v0.25.0-preview.0-pr-16759 to patch
version v0.25.0-preview.0 and create version 0.25.0-preview.1 by
@gemini-cli-robot in
[#16866](https://github.com/google-gemini/gemini-cli/pull/16866)
- Patch #16730 into v0.25.0 preview by @chrstnb in
[#16882](https://github.com/google-gemini/gemini-cli/pull/16882)
- fix(patch): cherry-pick 3b55581 to release/v0.25.0-preview.2-pr-16506 to patch
version v0.25.0-preview.2 and create version 0.25.0-preview.3 by
@gemini-cli-robot in
[#17098](https://github.com/google-gemini/gemini-cli/pull/17098)
**Full changelog**:
https://github.com/google-gemini/gemini-cli/compare/v0.24.5...v0.25.0
-332
View File
@@ -1,332 +0,0 @@
# Preview release: Release v0.26.0-preview.0
Released: January 21, 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).
To install the preview release:
```
npm install -g @google/gemini-cli@preview
```
## Highlights
- **Skills and Agents:** Improvements to the `activate_skill` tool and skill
management. Experimental Agent Skills support.
- **UI/UX:** Addition of a Rewind Confirmation dialog and Viewer component.
- **Extensions:** Experimental setting for extension configuration.
- **Bug Fixes and Stability:** PDF token estimation fix and improvements to
scheduled issue triage.
## What's Changed
- fix: PDF token estimation
([#16494](https://github.com/google-gemini/gemini-cli/pull/16494)) by
@korade-krushna in
[#16527](https://github.com/google-gemini/gemini-cli/pull/16527)
- chore(release): bump version to 0.26.0-nightly.20260114.bb6c57414 by
@gemini-cli-robot in
[#16604](https://github.com/google-gemini/gemini-cli/pull/16604)
- docs: clarify F12 to open debug console by @jackwotherspoon in
[#16570](https://github.com/google-gemini/gemini-cli/pull/16570)
- docs: Remove .md extension from internal links in architecture.md by
@medic-code in
[#12899](https://github.com/google-gemini/gemini-cli/pull/12899)
- Add an experimental setting for extension config by @chrstnb in
[#16506](https://github.com/google-gemini/gemini-cli/pull/16506)
- feat: add Rewind Confirmation dialog and Rewind Viewer component by @Adib234
in [#15717](https://github.com/google-gemini/gemini-cli/pull/15717)
- fix(a2a): Don't throw errors for GeminiEventType Retry and InvalidStream. by
@ehedlund in [#16541](https://github.com/google-gemini/gemini-cli/pull/16541)
- prefactor: add rootCommands as array so it can be used for policy parsing by
@abhipatel12 in
[#16640](https://github.com/google-gemini/gemini-cli/pull/16640)
- remove unnecessary \x7f key bindings by @scidomino in
[#16646](https://github.com/google-gemini/gemini-cli/pull/16646)
- docs(skills): use body-file in pr-creator skill for better reliability by
@abhipatel12 in
[#16642](https://github.com/google-gemini/gemini-cli/pull/16642)
- chore(automation): recursive labeling for workstream descendants by @bdmorgan
in [#16609](https://github.com/google-gemini/gemini-cli/pull/16609)
- feat: introduce 'skill-creator' built-in skill and CJS management tools by
@NTaylorMullen in
[#16394](https://github.com/google-gemini/gemini-cli/pull/16394)
- chore(automation): remove automated PR size and complexity labeler by
@bdmorgan in [#16648](https://github.com/google-gemini/gemini-cli/pull/16648)
- refactor(skills): replace 'project' with 'workspace' scope by @NTaylorMullen
in [#16380](https://github.com/google-gemini/gemini-cli/pull/16380)
- Docs: Update release notes for 1/13/2026 by @jkcinouye in
[#16583](https://github.com/google-gemini/gemini-cli/pull/16583)
- Simplify paste handling by @scidomino in
[#16654](https://github.com/google-gemini/gemini-cli/pull/16654)
- chore(automation): improve scheduled issue triage discovery and throughput by
@bdmorgan in [#16652](https://github.com/google-gemini/gemini-cli/pull/16652)
- fix(acp): run exit cleanup when stdin closes by @codefromthecrypt in
[#14953](https://github.com/google-gemini/gemini-cli/pull/14953)
- feat(scheduler): add types needed for event driven scheduler by @abhipatel12
in [#16641](https://github.com/google-gemini/gemini-cli/pull/16641)
- Remove unused rewind key binding by @scidomino in
[#16659](https://github.com/google-gemini/gemini-cli/pull/16659)
- Remove sequence binding by @scidomino in
[#16664](https://github.com/google-gemini/gemini-cli/pull/16664)
- feat(cli): undeprecate the --prompt flag by @alexaustin007 in
[#13981](https://github.com/google-gemini/gemini-cli/pull/13981)
- chore: update dependabot configuration by @cosmopax in
[#13507](https://github.com/google-gemini/gemini-cli/pull/13507)
- feat(config): add 'auto' alias for default model selection by @sehoon38 in
[#16661](https://github.com/google-gemini/gemini-cli/pull/16661)
- Enable & disable agents by @sehoon38 in
[#16225](https://github.com/google-gemini/gemini-cli/pull/16225)
- cleanup: Improve keybindings by @scidomino in
[#16672](https://github.com/google-gemini/gemini-cli/pull/16672)
- Add timeout for shell-utils to prevent hangs. by @jacob314 in
[#16667](https://github.com/google-gemini/gemini-cli/pull/16667)
- feat(plan): add experimental plan flag by @jerop in
[#16650](https://github.com/google-gemini/gemini-cli/pull/16650)
- feat(cli): add security consent prompts for skill installation by
@NTaylorMullen in
[#16549](https://github.com/google-gemini/gemini-cli/pull/16549)
- fix: replace 3 consecutive periods with ellipsis character by @Vist233 in
[#16587](https://github.com/google-gemini/gemini-cli/pull/16587)
- chore(automation): ensure status/need-triage is applied and never cleared
automatically by @bdmorgan in
[#16657](https://github.com/google-gemini/gemini-cli/pull/16657)
- fix: Handle colons in skill description frontmatter by @maru0804 in
[#16345](https://github.com/google-gemini/gemini-cli/pull/16345)
- refactor(core): harden skill frontmatter parsing by @NTaylorMullen in
[#16705](https://github.com/google-gemini/gemini-cli/pull/16705)
- feat(skills): add conflict detection and warnings for skill overrides by
@NTaylorMullen in
[#16709](https://github.com/google-gemini/gemini-cli/pull/16709)
- feat(scheduler): add SchedulerStateManager for reactive tool state by
@abhipatel12 in
[#16651](https://github.com/google-gemini/gemini-cli/pull/16651)
- chore(automation): enforce 'help wanted' label permissions and update
guidelines by @bdmorgan in
[#16707](https://github.com/google-gemini/gemini-cli/pull/16707)
- fix(core): resolve circular dependency via tsconfig paths by @sehoon38 in
[#16730](https://github.com/google-gemini/gemini-cli/pull/16730)
- chore/release: bump version to 0.26.0-nightly.20260115.6cb3ae4e0 by
@gemini-cli-robot in
[#16738](https://github.com/google-gemini/gemini-cli/pull/16738)
- fix(automation): correct status/need-issue label matching wildcard by
@bdmorgan in [#16727](https://github.com/google-gemini/gemini-cli/pull/16727)
- fix(automation): prevent label-enforcer loop by ignoring all bots by @bdmorgan
in [#16746](https://github.com/google-gemini/gemini-cli/pull/16746)
- Add links to supported locations and minor fixes by @g-samroberts in
[#16476](https://github.com/google-gemini/gemini-cli/pull/16476)
- feat(policy): add source tracking to policy rules by @allenhutchison in
[#16670](https://github.com/google-gemini/gemini-cli/pull/16670)
- feat(automation): enforce '🔒 maintainer only' and fix bot loop by @bdmorgan
in [#16751](https://github.com/google-gemini/gemini-cli/pull/16751)
- Make merged settings non-nullable and fix all lints related to that. by
@jacob314 in [#16647](https://github.com/google-gemini/gemini-cli/pull/16647)
- fix(core): prevent ModelInfo event emission on aborted signal by @sehoon38 in
[#16752](https://github.com/google-gemini/gemini-cli/pull/16752)
- Replace relative paths to fix website build by @chrstnb in
[#16755](https://github.com/google-gemini/gemini-cli/pull/16755)
- Restricting to localhost by @cocosheng-g in
[#16548](https://github.com/google-gemini/gemini-cli/pull/16548)
- fix(cli): add explicit dependency on color-convert by @sehoon38 in
[#16757](https://github.com/google-gemini/gemini-cli/pull/16757)
- fix(automation): robust label enforcement with permission checks by @bdmorgan
in [#16762](https://github.com/google-gemini/gemini-cli/pull/16762)
- fix(cli): prevent OOM crash by limiting file search traversal and adding
timeout by @galz10 in
[#16696](https://github.com/google-gemini/gemini-cli/pull/16696)
- fix(cli): safely handle /dev/tty access on macOS by @korade-krushna in
[#16531](https://github.com/google-gemini/gemini-cli/pull/16531)
- docs: clarify workspace test execution in GEMINI.md by @mattKorwel in
[#16764](https://github.com/google-gemini/gemini-cli/pull/16764)
- Add support for running available commands prior to MCP servers loading by
@Adib234 in [#15596](https://github.com/google-gemini/gemini-cli/pull/15596)
- feat(plan): add experimental 'plan' approval mode by @jerop in
[#16753](https://github.com/google-gemini/gemini-cli/pull/16753)
- feat(scheduler): add functional awaitConfirmation utility by @abhipatel12 in
[#16721](https://github.com/google-gemini/gemini-cli/pull/16721)
- fix(infra): update maintainer rollup label to 'workstream-rollup' by @bdmorgan
in [#16809](https://github.com/google-gemini/gemini-cli/pull/16809)
- fix(infra): use GraphQL to detect direct parents in rollup workflow by
@bdmorgan in [#16811](https://github.com/google-gemini/gemini-cli/pull/16811)
- chore(workflows): rename label-workstream-rollup workflow by @bdmorgan in
[#16818](https://github.com/google-gemini/gemini-cli/pull/16818)
- skip simple-mcp-server.test.ts by @scidomino in
[#16842](https://github.com/google-gemini/gemini-cli/pull/16842)
- Steer outer agent to use expert subagents when present by @gundermanc in
[#16763](https://github.com/google-gemini/gemini-cli/pull/16763)
- Fix race condition by awaiting scheduleToolCalls by @chrstnb in
[#16759](https://github.com/google-gemini/gemini-cli/pull/16759)
- cleanup: Organize key bindings by @scidomino in
[#16798](https://github.com/google-gemini/gemini-cli/pull/16798)
- feat(core): Add generalist agent. by @joshualitt in
[#16638](https://github.com/google-gemini/gemini-cli/pull/16638)
- perf(ui): optimize text buffer and highlighting for large inputs by
@NTaylorMullen in
[#16782](https://github.com/google-gemini/gemini-cli/pull/16782)
- fix(core): fix PTY descriptor shell leak by @galz10 in
[#16773](https://github.com/google-gemini/gemini-cli/pull/16773)
- feat(plan): enforce strict read-only policy and halt execution on violation by
@jerop in [#16849](https://github.com/google-gemini/gemini-cli/pull/16849)
- remove need-triage label from bug_report template by @sehoon38 in
[#16864](https://github.com/google-gemini/gemini-cli/pull/16864)
- fix(core): truncate large telemetry log entries by @sehoon38 in
[#16769](https://github.com/google-gemini/gemini-cli/pull/16769)
- docs(extensions): add Agent Skills support and mark feature as experimental by
@NTaylorMullen in
[#16859](https://github.com/google-gemini/gemini-cli/pull/16859)
- fix(core): surface warnings for invalid hook event names in configuration
([#16788](https://github.com/google-gemini/gemini-cli/pull/16788)) by
@sehoon38 in [#16873](https://github.com/google-gemini/gemini-cli/pull/16873)
- feat(plan): remove read_many_files from approval mode policies by @jerop in
[#16876](https://github.com/google-gemini/gemini-cli/pull/16876)
- feat(admin): implement admin controls polling and restart prompt by @skeshive
in [#16627](https://github.com/google-gemini/gemini-cli/pull/16627)
- Remove LRUCache class migrating to mnemoist by @jacob314 in
[#16872](https://github.com/google-gemini/gemini-cli/pull/16872)
- feat(settings): rename negative settings to positive naming (disable* ->
enable*) by @afarber in
[#14142](https://github.com/google-gemini/gemini-cli/pull/14142)
- refactor(cli): unify shell confirmation dialogs by @NTaylorMullen in
[#16828](https://github.com/google-gemini/gemini-cli/pull/16828)
- feat(agent): enable agent skills by default by @NTaylorMullen in
[#16736](https://github.com/google-gemini/gemini-cli/pull/16736)
- refactor(core): foundational truncation refactoring and token estimation
optimization by @NTaylorMullen in
[#16824](https://github.com/google-gemini/gemini-cli/pull/16824)
- fix(hooks): enable /hooks disable to reliably stop single hooks by
@abhipatel12 in
[#16804](https://github.com/google-gemini/gemini-cli/pull/16804)
- Don't commit unless user asks us to. by @gundermanc in
[#16902](https://github.com/google-gemini/gemini-cli/pull/16902)
- chore: remove a2a-adapter and bump @a2a-js/sdk to 0.3.8 by @adamfweidman in
[#16800](https://github.com/google-gemini/gemini-cli/pull/16800)
- fix: Show experiment values in settings UI for compressionThreshold by
@ishaanxgupta in
[#16267](https://github.com/google-gemini/gemini-cli/pull/16267)
- feat(cli): replace relative keyboard shortcuts link with web URL by
@imaliabbas in
[#16479](https://github.com/google-gemini/gemini-cli/pull/16479)
- fix(core): resolve PKCE length issue and stabilize OAuth redirect port by
@sehoon38 in [#16815](https://github.com/google-gemini/gemini-cli/pull/16815)
- Delete rewind documentation for now by @Adib234 in
[#16932](https://github.com/google-gemini/gemini-cli/pull/16932)
- Stabilize skill-creator CI and package format by @NTaylorMullen in
[#17001](https://github.com/google-gemini/gemini-cli/pull/17001)
- Stabilize the git evals by @gundermanc in
[#16989](https://github.com/google-gemini/gemini-cli/pull/16989)
- fix(core): attempt compression before context overflow check by @NTaylorMullen
in [#16914](https://github.com/google-gemini/gemini-cli/pull/16914)
- Fix inverted logic. by @gundermanc in
[#17007](https://github.com/google-gemini/gemini-cli/pull/17007)
- chore(scripts): add duplicate issue closer script and fix lint errors by
@bdmorgan in [#16997](https://github.com/google-gemini/gemini-cli/pull/16997)
- docs: update README and config guide to reference Gemini 3 by @JayadityaGit in
[#15806](https://github.com/google-gemini/gemini-cli/pull/15806)
- fix(cli): correct Homebrew installation detection by @kij in
[#14727](https://github.com/google-gemini/gemini-cli/pull/14727)
- Demote git evals to nightly run. by @gundermanc in
[#17030](https://github.com/google-gemini/gemini-cli/pull/17030)
- fix(cli): use OSC-52 clipboard copy in Windows Terminal by @Thomas-Shephard in
[#16920](https://github.com/google-gemini/gemini-cli/pull/16920)
- Fix: Process all parts in response chunks when thought is first by @pyrytakala
in [#13539](https://github.com/google-gemini/gemini-cli/pull/13539)
- fix(automation): fix jq quoting error in pr-triage.sh by @Kimsoo0119 in
[#16958](https://github.com/google-gemini/gemini-cli/pull/16958)
- refactor(core): decouple scheduler into orchestration, policy, and
confirmation by @abhipatel12 in
[#16895](https://github.com/google-gemini/gemini-cli/pull/16895)
- feat: add /introspect slash command by @NTaylorMullen in
[#17048](https://github.com/google-gemini/gemini-cli/pull/17048)
- refactor(cli): centralize tool mapping and decouple legacy scheduler by
@abhipatel12 in
[#17044](https://github.com/google-gemini/gemini-cli/pull/17044)
- fix(ui): ensure rationale renders before tool calls by @NTaylorMullen in
[#17043](https://github.com/google-gemini/gemini-cli/pull/17043)
- fix(workflows): use author_association for maintainer check by @bdmorgan in
[#17060](https://github.com/google-gemini/gemini-cli/pull/17060)
- fix return type of fireSessionStartEvent to defaultHookOutput by @ved015 in
[#16833](https://github.com/google-gemini/gemini-cli/pull/16833)
- feat(cli): add experiment gate for event-driven scheduler by @abhipatel12 in
[#17055](https://github.com/google-gemini/gemini-cli/pull/17055)
- feat(core): improve shell redirection transparency and security by
@NTaylorMullen in
[#16486](https://github.com/google-gemini/gemini-cli/pull/16486)
- fix(core): deduplicate ModelInfo emission in GeminiClient by @NTaylorMullen in
[#17075](https://github.com/google-gemini/gemini-cli/pull/17075)
- docs(themes): remove unsupported DiffModified color key by @jw409 in
[#17073](https://github.com/google-gemini/gemini-cli/pull/17073)
- fix: update currentSequenceModel when modelChanged by @adamfweidman in
[#17051](https://github.com/google-gemini/gemini-cli/pull/17051)
- feat(core): enhanced anchored iterative context compression with
self-verification by @rmedranollamas in
[#15710](https://github.com/google-gemini/gemini-cli/pull/15710)
- Fix mcp instructions by @chrstnb in
[#16439](https://github.com/google-gemini/gemini-cli/pull/16439)
- [A2A] Disable checkpointing if git is not installed by @cocosheng-g in
[#16896](https://github.com/google-gemini/gemini-cli/pull/16896)
- feat(admin): set admin.skills.enabled based on advancedFeaturesEnabled setting
by @skeshive in
[#17095](https://github.com/google-gemini/gemini-cli/pull/17095)
- Test coverage for hook exit code cases by @gundermanc in
[#17041](https://github.com/google-gemini/gemini-cli/pull/17041)
- Revert "Revert "Update extension examples"" by @chrstnb in
[#16445](https://github.com/google-gemini/gemini-cli/pull/16445)
- fix(core): Provide compact, actionable errors for agent delegation failures by
@SandyTao520 in
[#16493](https://github.com/google-gemini/gemini-cli/pull/16493)
- fix: migrate BeforeModel and AfterModel hooks to HookSystem by @ved015 in
[#16599](https://github.com/google-gemini/gemini-cli/pull/16599)
- feat(admin): apply admin settings to gemini skills/mcp/extensions commands by
@skeshive in [#17102](https://github.com/google-gemini/gemini-cli/pull/17102)
- fix(core): update telemetry token count after session resume by @psinha40898
in [#15491](https://github.com/google-gemini/gemini-cli/pull/15491)
- Demote the subagent test to nightly by @gundermanc in
[#17105](https://github.com/google-gemini/gemini-cli/pull/17105)
- feat(plan): telemetry to track adoption and usage of plan mode by @Adib234 in
[#16863](https://github.com/google-gemini/gemini-cli/pull/16863)
- feat: Add flash lite utility fallback chain by @adamfweidman in
[#17056](https://github.com/google-gemini/gemini-cli/pull/17056)
- Fixes Windows crash: "Cannot resize a pty that has already exited" by @dzammit
in [#15757](https://github.com/google-gemini/gemini-cli/pull/15757)
- feat(core): Add initial eval for generalist agent. by @joshualitt in
[#16856](https://github.com/google-gemini/gemini-cli/pull/16856)
- feat(core): unify agent enabled and disabled flags by @SandyTao520 in
[#17127](https://github.com/google-gemini/gemini-cli/pull/17127)
- fix(core): resolve auto model in default strategy by @sehoon38 in
[#17116](https://github.com/google-gemini/gemini-cli/pull/17116)
- docs: update project context and pr-creator workflow by @NTaylorMullen in
[#17119](https://github.com/google-gemini/gemini-cli/pull/17119)
- fix(cli): send gemini-cli version as mcp client version by @dsp in
[#13407](https://github.com/google-gemini/gemini-cli/pull/13407)
- fix(cli): resolve Ctrl+Enter and Ctrl+J newline issues by @imadraude in
[#17021](https://github.com/google-gemini/gemini-cli/pull/17021)
- Remove missing sidebar item by @chrstnb in
[#17145](https://github.com/google-gemini/gemini-cli/pull/17145)
- feat(core): Ensure all properties in hooks object are event names. by
@joshualitt in
[#16870](https://github.com/google-gemini/gemini-cli/pull/16870)
- fix(cli): fix newline support broken in previous PR by @scidomino in
[#17159](https://github.com/google-gemini/gemini-cli/pull/17159)
- Add interactive ValidationDialog for handling 403 VALIDATION_REQUIRED errors.
by @gsquared94 in
[#16231](https://github.com/google-gemini/gemini-cli/pull/16231)
- Add Esc-Esc to clear prompt when it's not empty by @Adib234 in
[#17131](https://github.com/google-gemini/gemini-cli/pull/17131)
- Avoid spurious warnings about unexpected renders triggered by appEvents and
coreEvents. by @jacob314 in
[#17160](https://github.com/google-gemini/gemini-cli/pull/17160)
- fix(cli): resolve home/end keybinding conflict by @scidomino in
[#17124](https://github.com/google-gemini/gemini-cli/pull/17124)
- fix(cli): display 'http' type on mcp list by @pamanta in
[#16915](https://github.com/google-gemini/gemini-cli/pull/16915)
- fix bad fallback logic external editor logic by @scidomino in
[#17166](https://github.com/google-gemini/gemini-cli/pull/17166)
- Fix bug where System scopes weren't migrated. by @jacob314 in
[#17174](https://github.com/google-gemini/gemini-cli/pull/17174)
- Fix mcp tool lookup in tool registry by @werdnum in
[#17054](https://github.com/google-gemini/gemini-cli/pull/17054)
**Full changelog**:
https://github.com/google-gemini/gemini-cli/compare/v0.25.0-preview.4...v0.26.0-preview.0
-21
View File
@@ -73,9 +73,6 @@ Slash commands provide meta-level control over the CLI itself.
- **`/copy`**
- **Description:** Copies the last output produced by Gemini CLI to your
clipboard, for easy sharing or reuse.
- **Behavior:**
- Local sessions use system clipboard tools (pbcopy/xclip/clip).
- Remote sessions (SSH/WSL) use OSC 52 and require terminal support.
- **Note:** This command requires platform-specific clipboard tools to be
installed.
- On Linux, it requires `xclip` or `xsel`. You can typically install them
@@ -167,7 +164,6 @@ Slash commands provide meta-level control over the CLI itself.
- **Note:** Only available if checkpointing is configured via
[settings](../get-started/configuration.md). See
[Checkpointing documentation](../cli/checkpointing.md) for more details.
- **`/resume`**
- **Description:** Browse and resume previous conversation sessions. Opens an
interactive session browser where you can search, filter, and select from
@@ -198,23 +194,6 @@ Slash commands provide meta-level control over the CLI itself.
modify them as desired. Changes to some settings are applied immediately,
while others require a restart.
- [**`/skills`**](./skills.md)
- **Description:** (Experimental) Manage Agent Skills, which provide on-demand
expertise and specialized workflows.
- **Sub-commands:**
- **`list`**:
- **Description:** List all discovered skills and their current status
(enabled/disabled).
- **`enable`**:
- **Description:** Enable a specific skill by name.
- **Usage:** `/skills enable <name>`
- **`disable`**:
- **Description:** Disable a specific skill by name.
- **Usage:** `/skills disable <name>`
- **`reload`**:
- **Description:** Refresh the list of discovered skills from all tiers
(workspace, user, and extensions).
- **`/stats`**
- **Description:** Display detailed statistics for the current Gemini CLI
session, including token usage, cached token savings (when available), and
+780
View File
@@ -0,0 +1,780 @@
# Gemini CLI configuration
Gemini CLI offers several ways to configure its behavior, including environment
variables, command-line arguments, and settings files. This document outlines
the different configuration methods and available settings.
## Configuration layers
Configuration is applied in the following order of precedence (lower numbers are
overridden by higher numbers):
1. **Default values:** Hardcoded defaults within the application.
2. **User settings file:** Global settings for the current user.
3. **Project settings file:** Project-specific settings.
4. **System settings file:** System-wide settings.
5. **Environment variables:** System-wide or session-specific variables,
potentially loaded from `.env` files.
6. **Command-line arguments:** Values passed when launching the CLI.
## Settings files
Gemini CLI uses `settings.json` files for persistent configuration. There are
three locations for these files:
- **User settings file:**
- **Location:** `~/.gemini/settings.json` (where `~` is your home directory).
- **Scope:** Applies to all Gemini CLI sessions for the current user.
- **Project settings file:**
- **Location:** `.gemini/settings.json` within your project's root directory.
- **Scope:** Applies only when running Gemini CLI from that specific project.
Project settings override user settings.
- **System settings file:**
- **Location:** `/etc/gemini-cli/settings.json` (Linux),
`C:\ProgramData\gemini-cli\settings.json` (Windows) or
`/Library/Application Support/GeminiCli/settings.json` (macOS). The path can
be overridden using the `GEMINI_CLI_SYSTEM_SETTINGS_PATH` environment
variable.
- **Scope:** Applies to all Gemini CLI sessions on the system, for all users.
System settings override user and project settings. May be useful for system
administrators at enterprises to have controls over users' Gemini CLI
setups.
**Note on environment variables in settings:** String values within your
`settings.json` files can reference environment variables using either
`$VAR_NAME` or `${VAR_NAME}` syntax. These variables will be automatically
resolved when the settings are loaded. For example, if you have an environment
variable `MY_API_TOKEN`, you could use it in `settings.json` like this:
`"apiKey": "$MY_API_TOKEN"`.
### The `.gemini` directory in your project
In addition to a project settings file, a project's `.gemini` directory can
contain other project-specific files related to Gemini CLI's operation, such as:
- [Custom sandbox profiles](#sandboxing) (e.g.,
`.gemini/sandbox-macos-custom.sb`, `.gemini/sandbox.Dockerfile`).
### Available settings in `settings.json`:
- **`contextFileName`** (string or array of strings):
- **Description:** Specifies the filename for context files (e.g.,
`GEMINI.md`, `AGENTS.md`). Can be a single filename or a list of accepted
filenames.
- **Default:** `GEMINI.md`
- **Example:** `"contextFileName": "AGENTS.md"`
- **`bugCommand`** (object):
- **Description:** Overrides the default URL for the `/bug` command.
- **Default:**
`"urlTemplate": "https://github.com/google-gemini/gemini-cli/issues/new?template=bug_report.yml&title={title}&info={info}"`
- **Properties:**
- **`urlTemplate`** (string): A URL that can contain `{title}` and `{info}`
placeholders.
- **Example:**
```json
"bugCommand": {
"urlTemplate": "https://bug.example.com/new?title={title}&info={info}"
}
```
- **`fileFiltering`** (object):
- **Description:** Controls git-aware file filtering behavior for @ commands
and file discovery tools.
- **Default:** `"respectGitIgnore": true, "enableRecursiveFileSearch": true`
- **Properties:**
- **`respectGitIgnore`** (boolean): Whether to respect .gitignore patterns
when discovering files. When set to `true`, git-ignored files (like
`node_modules/`, `dist/`, `.env`) are automatically excluded from @
commands and file listing operations.
- **`enableRecursiveFileSearch`** (boolean): Whether to enable searching
recursively for filenames under the current tree when completing @
prefixes in the prompt.
- **Example:**
```json
"fileFiltering": {
"respectGitIgnore": true,
"enableRecursiveFileSearch": false
}
```
- **`coreTools`** (array of strings):
- **Description:** Allows you to specify a list of core tool names that should
be made available to the model. This can be used to restrict the set of
built-in tools. See [Built-in Tools](../core/tools-api.md#built-in-tools)
for a list of core tools. You can also specify command-specific restrictions
for tools that support it, like the `ShellTool`. For example,
`"coreTools": ["ShellTool(ls -l)"]` will only allow the `ls -l` command to
be executed.
- **Default:** All tools available for use by the Gemini model.
- **Example:** `"coreTools": ["ReadFileTool", "GlobTool", "ShellTool(ls)"]`.
- **`excludeTools`** (array of strings):
- **Description:** Allows you to specify a list of core tool names that should
be excluded from the model. A tool listed in both `excludeTools` and
`coreTools` is excluded. You can also specify command-specific restrictions
for tools that support it, like the `ShellTool`. For example,
`"excludeTools": ["ShellTool(rm -rf)"]` will block the `rm -rf` command.
- **Default**: No tools excluded.
- **Example:** `"excludeTools": ["run_shell_command", "findFiles"]`.
- **Security Note:** Command-specific restrictions in `excludeTools` for
`run_shell_command` are based on simple string matching and can be easily
bypassed. This feature is **not a security mechanism** and should not be
relied upon to safely execute untrusted code. It is recommended to use
`coreTools` to explicitly select commands that can be executed.
- **`allowMCPServers`** (array of strings):
- **Description:** Allows you to specify a list of MCP server names that
should be made available to the model. This can be used to restrict the set
of MCP servers to connect to. Note that this will be ignored if
`--allowed-mcp-server-names` is set.
- **Default:** All MCP servers are available for use by the Gemini model.
- **Example:** `"allowMCPServers": ["myPythonServer"]`.
- **Security Note:** This uses simple string matching on MCP server names,
which can be modified. If you're a system administrator looking to prevent
users from bypassing this, consider configuring the `mcpServers` at the
system settings level such that the user will not be able to configure any
MCP servers of their own. This should not be used as an airtight security
mechanism.
- **`excludeMCPServers`** (array of strings):
- **Description:** Allows you to specify a list of MCP server names that
should be excluded from the model. A server listed in both
`excludeMCPServers` and `allowMCPServers` is excluded. Note that this will
be ignored if `--allowed-mcp-server-names` is set.
- **Default**: No MCP servers excluded.
- **Example:** `"excludeMCPServers": ["myNodeServer"]`.
- **Security note:** This uses simple string matching on MCP server names,
which can be modified. If you're a system administrator looking to prevent
users from bypassing this, consider configuring the `mcpServers` at the
system settings level such that the user will not be able to configure any
MCP servers of their own. This should not be used as an airtight security
mechanism.
- **`autoAccept`** (boolean):
- **Description:** Controls whether the CLI automatically accepts and executes
tool calls that are considered safe (e.g., read-only operations) without
explicit user confirmation. If set to `true`, the CLI will bypass the
confirmation prompt for tools deemed safe.
- **Default:** `false`
- **Example:** `"autoAccept": true`
- **`theme`** (string):
- **Description:** Sets the visual [theme](./themes.md) for Gemini CLI.
- **Default:** `"Default"`
- **Example:** `"theme": "GitHub"`
- **`vimMode`** (boolean):
- **Description:** Enables or disables vim mode for input editing. When
enabled, the input area supports vim-style navigation and editing commands
with NORMAL and INSERT modes. The vim mode status is displayed in the footer
and persists between sessions.
- **Default:** `false`
- **Example:** `"vimMode": true`
- **`sandbox`** (boolean or string):
- **Description:** Controls whether and how to use sandboxing for tool
execution. If set to `true`, Gemini CLI uses a pre-built
`gemini-cli-sandbox` Docker image. For more information, see
[Sandboxing](#sandboxing).
- **Default:** `false`
- **Example:** `"sandbox": "docker"`
- **`toolDiscoveryCommand`** (string):
- **Description:** Defines a custom shell command for discovering tools from
your project. The shell command must return on `stdout` a JSON array of
[function declarations](https://ai.google.dev/gemini-api/docs/function-calling#function-declarations).
Tool wrappers are optional.
- **Default:** Empty
- **Example:** `"toolDiscoveryCommand": "bin/get_tools"`
- **`toolCallCommand`** (string):
- **Description:** Defines a custom shell command for calling a specific tool
that was discovered using `toolDiscoveryCommand`. The shell command must
meet the following criteria:
- It must take function `name` (exactly as in
[function declaration](https://ai.google.dev/gemini-api/docs/function-calling#function-declarations))
as first command line argument.
- It must read function arguments as JSON on `stdin`, analogous to
[`functionCall.args`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/inference#functioncall).
- It must return function output as JSON on `stdout`, analogous to
[`functionResponse.response.content`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/inference#functionresponse).
- **Default:** Empty
- **Example:** `"toolCallCommand": "bin/call_tool"`
- **`mcpServers`** (object):
- **Description:** Configures connections to one or more Model-Context
Protocol (MCP) servers for discovering and using custom tools. Gemini CLI
attempts to connect to each configured MCP server to discover available
tools. If multiple MCP servers expose a tool with the same name, the tool
names will be prefixed with the server alias you defined in the
configuration (e.g., `serverAlias__actualToolName`) to avoid conflicts. Note
that the system might strip certain schema properties from MCP tool
definitions for compatibility.
- **Default:** Empty
- **Properties:**
- **`<SERVER_NAME>`** (object): The server parameters for the named server.
- `command` (string, required): The command to execute to start the MCP
server.
- `args` (array of strings, optional): Arguments to pass to the command.
- `env` (object, optional): Environment variables to set for the server
process.
- `cwd` (string, optional): The working directory in which to start the
server.
- `timeout` (number, optional): Timeout in milliseconds for requests to
this MCP server.
- `trust` (boolean, optional): Trust this server and bypass all tool call
confirmations.
- `includeTools` (array of strings, optional): List of tool names to
include from this MCP server. When specified, only the tools listed here
will be available from this server (whitelist behavior). If not
specified, all tools from the server are enabled by default.
- `excludeTools` (array of strings, optional): List of tool names to
exclude from this MCP server. Tools listed here will not be available to
the model, even if they are exposed by the server. **Note:**
`excludeTools` takes precedence over `includeTools` - if a tool is in
both lists, it will be excluded.
- **Example:**
```json
"mcpServers": {
"myPythonServer": {
"command": "python",
"args": ["mcp_server.py", "--port", "8080"],
"cwd": "./mcp_tools/python",
"timeout": 5000,
"includeTools": ["safe_tool", "file_reader"],
},
"myNodeServer": {
"command": "node",
"args": ["mcp_server.js"],
"cwd": "./mcp_tools/node",
"excludeTools": ["dangerous_tool", "file_deleter"]
},
"myDockerServer": {
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "API_KEY", "ghcr.io/foo/bar"],
"env": {
"API_KEY": "$MY_API_TOKEN"
}
}
}
```
- **`checkpointing`** (object):
- **Description:** Configures the checkpointing feature, which allows you to
save and restore conversation and file states. See the
[Checkpointing documentation](./checkpointing.md) for more details.
- **Default:** `{"enabled": false}`
- **Properties:**
- **`enabled`** (boolean): When `true`, the `/restore` command is available.
- **`preferredEditor`** (string):
- **Description:** Specifies the preferred editor to use for viewing diffs.
- **Default:** `vscode`
- **Example:** `"preferredEditor": "vscode"`
- **`telemetry`** (object)
- **Description:** Configures logging and metrics collection for Gemini CLI.
For more information, see [Telemetry](./telemetry.md).
- **Default:**
`{"enabled": false, "target": "local", "otlpEndpoint": "http://localhost:4317", "logPrompts": true}`
- **Properties:**
- **`enabled`** (boolean): Whether or not telemetry is enabled.
- **`target`** (string): The destination for collected telemetry. Supported
values are `local` and `gcp`.
- **`otlpEndpoint`** (string): The endpoint for the OTLP Exporter.
- **`logPrompts`** (boolean): Whether or not to include the content of user
prompts in the logs.
- **Example:**
```json
"telemetry": {
"enabled": true,
"target": "local",
"otlpEndpoint": "http://localhost:16686",
"logPrompts": false
}
```
- **`usageStatisticsEnabled`** (boolean):
- **Description:** Enables or disables the collection of usage statistics. See
[Usage Statistics](#usage-statistics) for more information.
- **Default:** `true`
- **Example:**
```json
"usageStatisticsEnabled": false
```
- **`hideTips`** (boolean):
- **Description:** Enables or disables helpful tips in the CLI interface.
- **Default:** `false`
- **Example:**
```json
"hideTips": true
```
- **`hideBanner`** (boolean):
- **Description:** Enables or disables the startup banner (ASCII art logo) in
the CLI interface.
- **Default:** `false`
- **Example:**
```json
"hideBanner": true
```
- **`maxSessionTurns`** (number):
- **Description:** Sets the maximum number of turns for a session. If the
session exceeds this limit, the CLI will stop processing and start a new
chat.
- **Default:** `-1` (unlimited)
- **Example:**
```json
"maxSessionTurns": 10
```
- **`summarizeToolOutput`** (object):
- **Description:** Enables or disables the summarization of tool output. You
can specify the token budget for the summarization using the `tokenBudget`
setting.
- Note: Currently only the `run_shell_command` tool is supported.
- **Default:** `{}` (Disabled by default)
- **Example:**
```json
"summarizeToolOutput": {
"run_shell_command": {
"tokenBudget": 2000
}
}
```
- **`excludedProjectEnvVars`** (array of strings):
- **Description:** Specifies environment variables that should be excluded
from being loaded from project `.env` files. This prevents project-specific
environment variables (like `DEBUG=true`) from interfering with gemini-cli
behavior. Variables from `.gemini/.env` files are never excluded.
- **Default:** `["DEBUG", "DEBUG_MODE"]`
- **Example:**
```json
"excludedProjectEnvVars": ["DEBUG", "DEBUG_MODE", "NODE_ENV"]
```
- **`includeDirectories`** (array of strings):
- **Description:** Specifies an array of additional absolute or relative paths
to include in the workspace context. This allows you to work with files
across multiple directories as if they were one. Paths can use `~` to refer
to the user's home directory. This setting can be combined with the
`--include-directories` command-line flag.
- **Default:** `[]`
- **Example:**
```json
"includeDirectories": [
"/path/to/another/project",
"../shared-library",
"~/common-utils"
]
```
- **`loadMemoryFromIncludeDirectories`** (boolean):
- **Description:** Controls the behavior of the `/memory refresh` command. If
set to `true`, `GEMINI.md` files should be loaded from all directories that
are added. If set to `false`, `GEMINI.md` should only be loaded from the
current directory.
- **Default:** `false`
- **Example:**
```json
"loadMemoryFromIncludeDirectories": true
```
### Example `settings.json`:
```json
{
"theme": "GitHub",
"sandbox": "docker",
"toolDiscoveryCommand": "bin/get_tools",
"toolCallCommand": "bin/call_tool",
"mcpServers": {
"mainServer": {
"command": "bin/mcp_server.py"
},
"anotherServer": {
"command": "node",
"args": ["mcp_server.js", "--verbose"]
}
},
"telemetry": {
"enabled": true,
"target": "local",
"otlpEndpoint": "http://localhost:4317",
"logPrompts": true
},
"usageStatisticsEnabled": true,
"hideTips": false,
"hideBanner": false,
"maxSessionTurns": 10,
"summarizeToolOutput": {
"run_shell_command": {
"tokenBudget": 100
}
},
"excludedProjectEnvVars": ["DEBUG", "DEBUG_MODE", "NODE_ENV"],
"includeDirectories": ["path/to/dir1", "~/path/to/dir2", "../path/to/dir3"],
"loadMemoryFromIncludeDirectories": true
}
```
## Shell history
The CLI keeps a history of shell commands you run. To avoid conflicts between
different projects, this history is stored in a project-specific directory
within your user's home folder.
- **Location:** `~/.gemini/tmp/<project_hash>/shell_history`
- `<project_hash>` is a unique identifier generated from your project's root
path.
- The history is stored in a file named `shell_history`.
## Environment variables and `.env` files
Environment variables are a common way to configure applications, especially for
sensitive information like API keys or for settings that might change between
environments.
The CLI automatically loads environment variables from an `.env` file. The
loading order is:
1. `.env` file in the current working directory.
2. If not found, it searches upwards in parent directories until it finds an
`.env` file or reaches the project root (identified by a `.git` folder) or
the home directory.
3. If still not found, it looks for `~/.env` (in the user's home directory).
**Environment variable exclusion:** Some environment variables (like `DEBUG` and
`DEBUG_MODE`) are automatically excluded from being loaded from project `.env`
files to prevent interference with gemini-cli behavior. Variables from
`.gemini/.env` files are never excluded. You can customize this behavior using
the `excludedProjectEnvVars` setting in your `settings.json` file.
- **`GEMINI_API_KEY`** (Required):
- Your API key for the Gemini API.
- **Crucial for operation.** The CLI will not function without it.
- Set this in your shell profile (e.g., `~/.bashrc`, `~/.zshrc`) or an `.env`
file.
- **`GEMINI_MODEL`**:
- Specifies the default Gemini model to use.
- Overrides the hardcoded default
- Example: `export GEMINI_MODEL="gemini-2.5-flash"`
- **`GEMINI_CLI_CUSTOM_HEADERS`**:
- Adds extra HTTP headers to Gemini API and Code Assist requests.
- Accepts a comma-separated list of `Name: value` pairs.
- Example:
`export GEMINI_CLI_CUSTOM_HEADERS="X-My-Header: foo, X-Trace-ID: abc123"`.
- **`GEMINI_API_KEY_AUTH_MECHANISM`**:
- Specifies how the API key should be sent for authentication when using
`AuthType.USE_GEMINI` or `AuthType.USE_VERTEX_AI`.
- Valid values are `x-goog-api-key` (default) or `bearer`.
- If set to `bearer`, the API key will be sent in the
`Authorization: Bearer <key>` header.
- Example: `export GEMINI_API_KEY_AUTH_MECHANISM="bearer"`
- **`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"`.
- **`GOOGLE_CLOUD_PROJECT`**:
- Your Google Cloud Project ID.
- Required for using Code Assist or Vertex AI.
- If using Vertex AI, ensure you have the necessary permissions in this
project.
- **Cloud shell note:** When running in a Cloud Shell environment, this
variable defaults to a special project allocated for Cloud Shell users. If
you have `GOOGLE_CLOUD_PROJECT` set in your global environment in Cloud
Shell, it will be overridden by this default. To use a different project in
Cloud Shell, you must define `GOOGLE_CLOUD_PROJECT` in a `.env` file.
- Example: `export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"`.
- **`GOOGLE_APPLICATION_CREDENTIALS`** (string):
- **Description:** The path to your Google Application Credentials JSON file.
- **Example:**
`export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/credentials.json"`
- **`OTLP_GOOGLE_CLOUD_PROJECT`**:
- Your Google Cloud Project ID for Telemetry in Google Cloud
- Example: `export OTLP_GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"`.
- **`GOOGLE_CLOUD_LOCATION`**:
- Your Google Cloud Project Location (e.g., us-central1).
- Required for using Vertex AI in non express mode.
- Example: `export 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.
- **`HTTP_PROXY` / `HTTPS_PROXY`**:
- Specifies the proxy server to use for outgoing HTTP/HTTPS requests.
- Example: `export HTTPS_PROXY="http://proxy.example.com:8080"`
- **`SEATBELT_PROFILE`** (macOS specific):
- Switches the Seatbelt (`sandbox-exec`) profile on macOS.
- `permissive-open`: (Default) Restricts writes to the project folder (and a
few other folders, see
`packages/cli/src/utils/sandbox-macos-permissive-open.sb`) but allows other
operations.
- `strict`: Uses a strict profile that declines operations by default.
- `<profile_name>`: Uses a custom profile. To define a custom profile, create
a file named `sandbox-macos-<profile_name>.sb` in your project's `.gemini/`
directory (e.g., `my-project/.gemini/sandbox-macos-custom.sb`).
- **`DEBUG` or `DEBUG_MODE`** (often used by underlying libraries or the CLI
itself):
- Set to `true` or `1` to enable verbose debug logging, which can be helpful
for troubleshooting.
- **Note:** These variables are automatically excluded from project `.env`
files by default to prevent interference with gemini-cli behavior. Use
`.gemini/.env` files if you need to set these for gemini-cli specifically.
- **`NO_COLOR`**:
- Set to any value to disable all color output in the CLI.
- **`CLI_TITLE`**:
- Set to a string to customize the title of the CLI.
- **`CODE_ASSIST_ENDPOINT`**:
- Specifies the endpoint for the code assist server.
- This is useful for development and testing.
- **`GEMINI_SYSTEM_MD`**:
- Overrides the base system prompt with the contents of a Markdown file.
- If set to `1` or `true`, it uses the file at `.gemini/system.md`.
- If set to a file path, it uses that file. The path can be absolute or
relative. `~` is supported for the home directory.
- The specified file must exist.
- **`GEMINI_WRITE_SYSTEM_MD`**:
- Writes the default system prompt to a file. This is useful for getting a
template to customize.
- If set to `1` or `true`, it writes to `.gemini/system.md`.
- If set to a file path, it writes to that path. The path can be absolute or
relative. `~` is supported for the home directory. **Note: This will
overwrite the file if it already exists.**
## Command-line arguments
Arguments passed directly when running the CLI can override other configurations
for that specific session.
- **`--model <model_name>`** (**`-m <model_name>`**):
- Specifies the Gemini model to use for this session.
- Example: `npm start -- --model gemini-1.5-pro-latest`
- **`--prompt <your_prompt>`** (**`-p <your_prompt>`**):
- Used to pass a prompt directly to the command. This invokes Gemini CLI in a
non-interactive mode.
- **`--prompt-interactive <your_prompt>`** (**`-i <your_prompt>`**):
- Starts an interactive session with the provided prompt as the initial input.
- The prompt is processed within the interactive session, not before it.
- Cannot be used when piping input from stdin.
- Example: `gemini -i "explain this code"`
- **`--sandbox`** (**`-s`**):
- Enables sandbox mode for this session.
- **`--sandbox-image`**:
- Sets the sandbox image URI.
- **`--debug`** (**`-d`**):
- Enables debug mode for this session, providing more verbose output.
- **`--all-files`** (**`-a`**):
- If set, recursively includes all files within the current directory as
context for the prompt.
- **`--help`** (or **`-h`**):
- Displays help information about command-line arguments.
- **`--show-memory-usage`**:
- Displays the current memory usage.
- **`--yolo`**:
- Enables YOLO mode, which automatically approves all tool calls.
- **`--telemetry`**:
- Enables [telemetry](./telemetry.md).
- **`--telemetry-target`**:
- Sets the telemetry target. See [telemetry](./telemetry.md) for more
information.
- **`--telemetry-otlp-endpoint`**:
- Sets the OTLP endpoint for telemetry. See [telemetry](./telemetry.md) for
more information.
- **`--telemetry-log-prompts`**:
- Enables logging of prompts for telemetry. See [telemetry](./telemetry.md)
for more information.
- **`--extensions <extension_name ...>`** (**`-e <extension_name ...>`**):
- Specifies a list of extensions to use for the session. If not provided, all
available extensions are used.
- Use the special term `gemini -e none` to disable all extensions.
- Example: `gemini -e my-extension -e my-other-extension`
- **`--list-extensions`** (**`-l`**):
- Lists all available extensions and exits.
- **`--include-directories <dir1,dir2,...>`**:
- Includes additional directories in the workspace for multi-directory
support.
- Can be specified multiple times or as comma-separated values.
- 5 directories can be added at maximum.
- Example: `--include-directories /path/to/project1,/path/to/project2` or
`--include-directories /path/to/project1 --include-directories /path/to/project2`
- **`--version`**:
- Displays the version of the CLI.
## Context files (hierarchical instructional context)
While not strictly configuration for the CLI's _behavior_, context files
(defaulting to `GEMINI.md` but configurable via the `contextFileName` setting)
are crucial for configuring the _instructional context_ (also referred to as
"memory") provided to the Gemini model. This powerful feature allows you to give
project-specific instructions, coding style guides, or any relevant background
information to the AI, making its responses more tailored and accurate to your
needs. The CLI includes UI elements, such as an indicator in the footer showing
the number of loaded context files, to keep you informed about the active
context.
- **Purpose:** These Markdown files contain instructions, guidelines, or context
that you want the Gemini model to be aware of during your interactions. The
system is designed to manage this instructional context hierarchically.
### Example context file content (e.g., `GEMINI.md`)
Here's a conceptual example of what a context file at the root of a TypeScript
project might contain:
```markdown
# Project: My Awesome TypeScript Library
## General Instructions:
- When generating new TypeScript code, please follow the existing coding style.
- Ensure all new functions and classes have JSDoc comments.
- Prefer functional programming paradigms where appropriate.
- All code should be compatible with TypeScript 5.0 and Node.js 20+.
## Coding Style:
- Use 2 spaces for indentation.
- Interface names should be prefixed with `I` (e.g., `IUserService`).
- Private class members should be prefixed with an underscore (`_`).
- Always use strict equality (`===` and `!==`).
## Specific Component: `src/api/client.ts`
- This file handles all outbound API requests.
- When adding new API call functions, ensure they include robust error handling
and logging.
- Use the existing `fetchWithRetry` utility for all GET requests.
## Regarding Dependencies:
- Avoid introducing new external dependencies unless absolutely necessary.
- If a new dependency is required, please state the reason.
```
This example demonstrates how you can provide general project context, specific
coding conventions, and even notes about particular files or components. The
more relevant and precise your context files are, the better the AI can assist
you. Project-specific context files are highly encouraged to establish
conventions and context.
- **Hierarchical loading and precedence:** The CLI implements a sophisticated
hierarchical memory system by loading context files (e.g., `GEMINI.md`) from
several locations. Content from files lower in this list (more specific)
typically overrides or supplements content from files higher up (more
general). The exact concatenation order and final context can be inspected
using the `/memory show` command. The typical loading order is:
1. **Global context file:**
- Location: `~/.gemini/<contextFileName>` (e.g., `~/.gemini/GEMINI.md` in
your user home directory).
- Scope: Provides default instructions for all your projects.
2. **Project root and ancestors context files:**
- Location: The CLI searches for the configured context file in the
current working directory and then in each parent directory up to either
the project root (identified by a `.git` folder) or your home directory.
- Scope: Provides context relevant to the entire project or a significant
portion of it.
3. **Sub-directory context files (contextual/local):**
- Location: The CLI also scans for the configured context file in
subdirectories _below_ the current working directory (respecting common
ignore patterns like `node_modules`, `.git`, etc.). The breadth of this
search is limited to 200 directories by default, but can be configured
with a `memoryDiscoveryMaxDirs` field in your `settings.json` file.
- Scope: Allows for highly specific instructions relevant to a particular
component, module, or subsection of your project.
- **Concatenation and UI indication:** The contents of all found context files
are concatenated (with separators indicating their origin and path) and
provided as part of the system prompt to the Gemini model. The CLI footer
displays the count of loaded context files, giving you a quick visual cue
about the active instructional context.
- **Importing content:** You can modularize your context files by importing
other Markdown files using the `@path/to/file.md` syntax. For more details,
see the [Memory Import Processor documentation](../core/memport.md).
- **Commands for memory management:**
- Use `/memory refresh` to force a re-scan and reload of all context files
from all configured locations. This updates the AI's instructional context.
- Use `/memory show` to display the combined instructional context currently
loaded, allowing you to verify the hierarchy and content being used by the
AI.
- See the [Commands documentation](./commands.md#memory) for full details on
the `/memory` command and its sub-commands (`show` and `refresh`).
By understanding and utilizing these configuration layers and the hierarchical
nature of context files, you can effectively manage the AI's memory and tailor
the Gemini CLI's responses to your specific needs and projects.
## Sandboxing
The Gemini CLI can execute potentially unsafe operations (like shell commands
and file modifications) within a sandboxed environment to protect your system.
Sandboxing is disabled by default, but you can enable it in a few ways:
- Using `--sandbox` or `-s` flag.
- Setting `GEMINI_SANDBOX` environment variable.
- Sandbox is enabled in `--yolo` mode by default.
By default, it uses a pre-built `gemini-cli-sandbox` Docker image.
For project-specific sandboxing needs, you can create a custom Dockerfile at
`.gemini/sandbox.Dockerfile` in your project's root directory. This Dockerfile
can be based on the base sandbox image:
```dockerfile
FROM gemini-cli-sandbox
# Add your custom dependencies or configurations here
# For example:
# RUN apt-get update && apt-get install -y some-package
# COPY ./my-config /app/my-config
```
When `.gemini/sandbox.Dockerfile` exists, you can use `BUILD_SANDBOX`
environment variable when running Gemini CLI to automatically build the custom
sandbox image:
```bash
BUILD_SANDBOX=1 gemini -s
```
## Usage statistics
To help us improve the Gemini CLI, we collect anonymized usage statistics. This
data helps us understand how the CLI is used, identify common issues, and
prioritize new features.
**What we collect:**
- **Tool calls:** We log the names of the tools that are called, whether they
succeed or fail, and how long they take to execute. We do not collect the
arguments passed to the tools or any data returned by them.
- **API requests:** We log the Gemini model used for each request, the duration
of the request, and whether it was successful. We do not collect the content
of the prompts or responses.
- **Session information:** We collect information about the configuration of the
CLI, such as the enabled tools and the approval mode.
**What we DON'T collect:**
- **Personally identifiable information (PII):** We do not collect any personal
information, such as your name, email address, or API keys.
- **Prompt and response content:** We do not log the content of your prompts or
the responses from the Gemini model.
- **File content:** We do not log the content of any files that are read or
written by the CLI.
**How to opt out:**
You can opt out of usage statistics collection at any time by setting the
`usageStatisticsEnabled` property to `false` in your `settings.json` file:
```json
{
"usageStatisticsEnabled": false
}
```
+9 -9
View File
@@ -50,7 +50,7 @@ Your command definition files must be written in the TOML format and use the
## Handling arguments
Custom commands support two powerful methods for handling arguments. The CLI
automatically chooses the correct method based on the content of your command's
automatically chooses the correct method based on the content of your command\'s
`prompt`.
### 1. Context-aware injection with `{{args}}`
@@ -96,13 +96,13 @@ Search Results:
"""
```
When you run `/grep-code It's complicated`:
When you run `/grep-code It\'s complicated`:
1. The CLI sees `{{args}}` used both outside and inside `!{...}`.
2. Outside: The first `{{args}}` is replaced raw with `It's complicated`.
2. Outside: The first `{{args}}` is replaced raw with `It\'s complicated`.
3. Inside: The second `{{args}}` is replaced with the escaped version (e.g., on
Linux: `"It\'s complicated"`).
4. The command executed is `grep -r "It's complicated" .`.
4. The command executed is `grep -r "It\'s complicated" .`.
5. The CLI prompts you to confirm this exact, secure command before execution.
6. The final prompt is sent.
@@ -129,13 +129,13 @@ format and behavior.
# In: <project>/.gemini/commands/changelog.toml
# Invoked via: /changelog 1.2.0 added "Support for default argument parsing."
description = "Adds a new entry to the project's CHANGELOG.md file."
description = "Adds a new entry to the project\'s CHANGELOG.md file."
prompt = """
# Task: Update Changelog
You are an expert maintainer of this software project. A user has invoked a command to add a new entry to the changelog.
**The user's raw command is appended below your instructions.**
**The user\'s raw command is appended below your instructions.**
Your task is to parse the `<version>`, `<change_type>`, and `<message>` from their input and use the `write_file` tool to correctly update the `CHANGELOG.md` file.
@@ -147,7 +147,7 @@ The command follows this format: `/changelog <version> <type> <message>`
1. Read the `CHANGELOG.md` file.
2. Find the section for the specified `<version>`.
3. Add the `<message>` under the correct `<type>` heading.
4. If the version or type section doesn't exist, create it.
4. If the version or type section doesn\'t exist, create it.
5. Adhere strictly to the "Keep a Changelog" format.
"""
```
@@ -241,7 +241,7 @@ operate on specific files.
**Example (`review.toml`):**
This command injects the content of a _fixed_ best practices file
(`docs/best-practices.md`) and uses the user's arguments to provide context for
(`docs/best-practices.md`) and uses the user\'s arguments to provide context for
the review.
```toml
@@ -293,7 +293,7 @@ practice.
description = "Asks the model to refactor the current context into a pure function."
prompt = """
Please analyze the code I've provided in the current context.
Please analyze the code I\'ve provided in the current context.
Refactor it into a pure function.
Your response should include:
+1 -77
View File
@@ -159,50 +159,6 @@ This results in the following merged configuration:
By using the system settings file, you can enforce the security and
configuration patterns described below.
### Enforcing system settings with a wrapper script
While the `GEMINI_CLI_SYSTEM_SETTINGS_PATH` environment variable provides
flexibility, a user could potentially override it to point to a different
settings file, bypassing the centrally managed configuration. To mitigate this,
enterprises can deploy a wrapper script or alias that ensures the environment
variable is always set to the corporate-controlled path.
This approach ensures that no matter how the user calls the `gemini` command,
the enterprise settings are always loaded with the highest precedence.
**Example wrapper script:**
Administrators can create a script named `gemini` and place it in a directory
that appears earlier in the user's `PATH` than the actual Gemini CLI binary
(e.g., `/usr/local/bin/gemini`).
```bash
#!/bin/bash
# Enforce the path to the corporate system settings file.
# This ensures that the company's configuration is always applied.
export GEMINI_CLI_SYSTEM_SETTINGS_PATH="/etc/gemini-cli/settings.json"
# Find the original gemini executable.
# This is a simple example; a more robust solution might be needed
# depending on the installation method.
REAL_GEMINI_PATH=$(type -aP gemini | grep -v "^$(type -P gemini)$" | head -n 1)
if [ -z "$REAL_GEMINI_PATH" ]; then
echo "Error: The original 'gemini' executable was not found." >&2
exit 1
fi
# Pass all arguments to the real Gemini CLI executable.
exec "$REAL_GEMINI_PATH" "$@"
```
By deploying this script, the `GEMINI_CLI_SYSTEM_SETTINGS_PATH` is set within
the script's environment, and the `exec` command replaces the script process
with the actual Gemini CLI process, which inherits the environment variable.
This makes it significantly more difficult for a user to bypass the enforced
settings.
## Restricting tool access
You can significantly enhance security by controlling which tools the Gemini
@@ -478,39 +434,7 @@ prompted to switch to the enforced method. In non-interactive mode, the CLI will
exit with an error if the configured authentication method does not match the
enforced one.
### Restricting logins to corporate domains
For enterprises using Google Workspace, you can enforce that users only
authenticate with their corporate Google accounts. This is a network-level
control that is configured on a proxy server, not within Gemini CLI itself. It
works by intercepting authentication requests to Google and adding a special
HTTP header.
This policy prevents users from logging in with personal Gmail accounts or other
non-corporate Google accounts.
For detailed instructions, see the Google Workspace Admin Help article on
[blocking access to consumer accounts](https://support.google.com/a/answer/1668854?hl=en#zippy=%2Cstep-choose-a-web-proxy-server%2Cstep-configure-the-network-to-block-certain-accounts).
The general steps are as follows:
1. **Intercept Requests**: Configure your web proxy to intercept all requests
to `google.com`.
2. **Add HTTP Header**: For each intercepted request, add the
`X-GoogApps-Allowed-Domains` HTTP header.
3. **Specify Domains**: The value of the header should be a comma-separated
list of your approved Google Workspace domain names.
**Example header:**
```
X-GoogApps-Allowed-Domains: my-corporate-domain.com, secondary-domain.com
```
When this header is present, Google's authentication service will only allow
logins from accounts belonging to the specified domains.
## Putting it all together: example system `settings.json`
## Putting it all together: Example system `settings.json`
Here is an example of a system `settings.json` file that combines several of the
patterns discussed above to create a secure, controlled environment for Gemini
+2 -6
View File
@@ -25,12 +25,10 @@ overview of Gemini CLI, see the [main documentation page](../index.md).
- **[Checkpointing](./checkpointing.md):** Automatically save and restore
snapshots of your session and files.
- **[Enterprise configuration](./enterprise.md):** Deploy and manage Gemini CLI
in an enterprise environment.
- **[Enterprise configuration](./enterprise.md):** Deploying and manage Gemini
CLI in an enterprise environment.
- **[Sandboxing](./sandbox.md):** Isolate tool execution in a secure,
containerized environment.
- **[Agent Skills](./skills.md):** (Experimental) Extend the CLI with
specialized expertise and procedural workflows.
- **[Telemetry](./telemetry.md):** Configure observability to monitor usage and
performance.
- **[Token caching](./token-caching.md):** Optimize API costs by caching tokens.
@@ -40,8 +38,6 @@ overview of Gemini CLI, see the [main documentation page](../index.md).
files and directories from being accessed by tools.
- **[Context files (GEMINI.md)](./gemini-md.md):** Provide persistent,
hierarchical context to the model.
- **[System prompt override](./system-prompt.md):** Replace the builtin system
instructions using `GEMINI_SYSTEM_MD`.
## Non-interactive mode
+78 -61
View File
@@ -8,50 +8,43 @@ available combinations.
#### Basic Controls
| Action | Keys |
| --------------------------------------------------------------- | ---------- |
| Confirm the current selection or choice. | `Enter` |
| Dismiss dialogs or cancel the current focus. | `Esc` |
| Cancel the current request or quit the CLI when input is empty. | `Ctrl + C` |
| Exit the CLI when the input buffer is empty. | `Ctrl + D` |
| Action | Keys |
| -------------------------------------------- | ------- |
| Confirm the current selection or choice. | `Enter` |
| Dismiss dialogs or cancel the current focus. | `Esc` |
#### Cursor Movement
| Action | Keys |
| ------------------------------------------- | ------------------------------------------------------------ |
| Move the cursor to the start of the line. | `Ctrl + A`<br />`Home (no Shift, Ctrl)` |
| Move the cursor to the end of the line. | `Ctrl + E`<br />`End (no Shift, Ctrl)` |
| Move the cursor up one line. | `Up Arrow (no Shift, Alt, Ctrl, Cmd)` |
| Move the cursor down one line. | `Down Arrow (no Shift, Alt, Ctrl, Cmd)` |
| Move the cursor one character to the left. | `Left Arrow (no Shift, Alt, Ctrl, Cmd)`<br />`Ctrl + B` |
| Move the cursor one character to the right. | `Right Arrow (no Shift, Alt, Ctrl, Cmd)`<br />`Ctrl + F` |
| Move the cursor one word to the left. | `Ctrl + Left Arrow`<br />`Alt + Left Arrow`<br />`Alt + B` |
| Move the cursor one word to the right. | `Ctrl + Right Arrow`<br />`Alt + Right Arrow`<br />`Alt + F` |
| Action | Keys |
| ----------------------------------------- | ---------------------- |
| Move the cursor to the start of the line. | `Ctrl + A`<br />`Home` |
| Move the cursor to the end of the line. | `Ctrl + E`<br />`End` |
#### Editing
| Action | Keys |
| ------------------------------------------------ | --------------------------------------------------------- |
| Delete from the cursor to the end of the line. | `Ctrl + K` |
| Delete from the cursor to the start of the line. | `Ctrl + U` |
| Clear all text in the input field. | `Ctrl + C` |
| Delete the previous word. | `Ctrl + Backspace`<br />`Alt + Backspace`<br />`Ctrl + W` |
| Delete the next word. | `Ctrl + Delete`<br />`Alt + Delete` |
| Delete the character to the left. | `Backspace`<br />`Ctrl + H` |
| Delete the character to the right. | `Delete`<br />`Ctrl + D` |
| Undo the most recent text edit. | `Ctrl + Z (no Shift)` |
| Redo the most recent undone text edit. | `Shift + Ctrl + Z` |
| Action | Keys |
| ------------------------------------------------ | ----------------------------------------- |
| Delete from the cursor to the end of the line. | `Ctrl + K` |
| Delete from the cursor to the start of the line. | `Ctrl + U` |
| Clear all text in the input field. | `Ctrl + C` |
| Delete the previous word. | `Ctrl + Backspace`<br />`Cmd + Backspace` |
#### Screen Control
| Action | Keys |
| -------------------------------------------- | ---------- |
| Clear the terminal screen and redraw the UI. | `Ctrl + L` |
#### Scrolling
| Action | Keys |
| ------------------------ | --------------------------------- |
| Scroll content up. | `Shift + Up Arrow` |
| Scroll content down. | `Shift + Down Arrow` |
| Scroll to the top. | `Ctrl + Home`<br />`Shift + Home` |
| Scroll to the bottom. | `Ctrl + End`<br />`Shift + End` |
| Scroll up by one page. | `Page Up` |
| Scroll down by one page. | `Page Down` |
| Action | Keys |
| ------------------------ | -------------------- |
| Scroll content up. | `Shift + Up Arrow` |
| Scroll content down. | `Shift + Down Arrow` |
| Scroll to the top. | `Home` |
| Scroll to the bottom. | `End` |
| Scroll up by one page. | `Page Up` |
| Scroll down by one page. | `Page Down` |
#### History & Search
@@ -60,9 +53,8 @@ available combinations.
| Show the previous entry in history. | `Ctrl + P (no Shift)` |
| Show the next entry in history. | `Ctrl + N (no Shift)` |
| Start reverse search through history. | `Ctrl + R` |
| Submit the selected reverse-search match. | `Enter (no Ctrl)` |
| Insert the selected reverse-search match. | `Enter (no Ctrl)` |
| Accept a suggestion while reverse searching. | `Tab` |
| Browse and rewind previous interactions. | `Double Esc` |
#### Navigation
@@ -85,41 +77,66 @@ available combinations.
#### Text Input
| Action | Keys |
| ---------------------------------------------- | ----------------------------------------------------------------------------------------- |
| Submit the current prompt. | `Enter (no Shift, Alt, Ctrl, Cmd)` |
| Insert a newline without submitting. | `Ctrl + Enter`<br />`Cmd + Enter`<br />`Alt + Enter`<br />`Shift + Enter`<br />`Ctrl + J` |
| Open the current prompt in an external editor. | `Ctrl + X` |
| Paste from the clipboard. | `Ctrl + V`<br />`Cmd + V`<br />`Alt + V` |
| Action | Keys |
| ------------------------------------ | ------------------------------------------------------------------------------------------- |
| Submit the current prompt. | `Enter (no Ctrl, no Shift, no Cmd, not Paste)` |
| Insert a newline without submitting. | `Ctrl + Enter`<br />`Cmd + Enter`<br />`Paste + Enter`<br />`Shift + Enter`<br />`Ctrl + J` |
#### External Tools
| Action | Keys |
| ---------------------------------------------- | ---------- |
| Open the current prompt in an external editor. | `Ctrl + X` |
| Paste from the clipboard. | `Ctrl + V` |
#### App Controls
| Action | Keys |
| ----------------------------------------------------------------------------------------------------- | ---------------- |
| Toggle detailed error information. | `F12` |
| Toggle the full TODO list. | `Ctrl + T` |
| Show IDE context details. | `Ctrl + G` |
| Toggle Markdown rendering. | `Alt + M` |
| Toggle copy mode when in alternate buffer mode. | `Ctrl + S` |
| Toggle YOLO (auto-approval) mode for tool calls. | `Ctrl + Y` |
| Cycle through approval modes: default (prompt), auto_edit (auto-approve edits), and plan (read-only). | `Shift + Tab` |
| Expand a height-constrained response to show additional lines when not in alternate buffer mode. | `Ctrl + S` |
| Focus the shell input from the gemini input. | `Tab (no Shift)` |
| Focus the Gemini input from the shell input. | `Tab` |
| Clear the terminal screen and redraw the UI. | `Ctrl + L` |
| Restart the application. | `R` |
| Action | Keys |
| ----------------------------------------------------------------- | ---------- |
| Toggle detailed error information. | `F12` |
| Toggle the full TODO list. | `Ctrl + T` |
| Toggle IDE context details. | `Ctrl + G` |
| Toggle Markdown rendering. | `Cmd + M` |
| Toggle copy mode when the terminal is using the alternate buffer. | `Ctrl + S` |
| Expand a height-constrained response to show additional lines. | `Ctrl + S` |
| Toggle focus between the shell and Gemini input. | `Ctrl + F` |
#### Session Control
| Action | Keys |
| -------------------------------------------- | ---------- |
| Cancel the current request or quit the CLI. | `Ctrl + C` |
| Exit the CLI when the input buffer is empty. | `Ctrl + D` |
<!-- KEYBINDINGS-AUTOGEN:END -->
## Additional context-specific shortcuts
- `Option+B/F/M` (macOS only): Are interpreted as `Cmd+B/F/M` even if your
terminal isn't configured to send Meta with Option.
- `Ctrl+Y`: Toggle YOLO (auto-approval) mode for tool calls.
- `Shift+Tab`: Toggle Auto Edit (auto-accept edits) mode.
- `Option+M` (macOS): Entering `µ` with Option+M also toggles Markdown
rendering, matching `Cmd+M`.
- `!` on an empty prompt: Enter or exit shell mode.
- `\` (at end of a line) + `Enter`: Insert a newline without leaving single-line
mode.
- `Esc` pressed twice quickly: Clear the input prompt if it is not empty,
otherwise browse and rewind previous interactions.
- `Ctrl+Delete` / `Meta+Delete`: Delete the word to the right of the cursor.
- `Ctrl+B` or `Left Arrow`: Move the cursor one character to the left while
editing text.
- `Ctrl+F` or `Right Arrow`: Move the cursor one character to the right; with an
embedded shell attached, `Ctrl+F` still toggles focus.
- `Ctrl+D` or `Delete`: Remove the character immediately to the right of the
cursor.
- `Ctrl+H` or `Backspace`: Remove the character immediately to the left of the
cursor.
- `Ctrl+Left Arrow` / `Meta+Left Arrow` / `Meta+B`: Move one word to the left.
- `Ctrl+Right Arrow` / `Meta+Right Arrow` / `Meta+F`: Move one word to the
right.
- `Ctrl+W`: Delete the word to the left of the cursor (in addition to
`Ctrl+Backspace` / `Cmd+Backspace`).
- `Ctrl+Z` / `Ctrl+Shift+Z`: Undo or redo the most recent text edit.
- `Meta+Enter`: Open the current input in an external editor (alias for
`Ctrl+X`).
- `Esc` pressed twice quickly: Clear the current input buffer.
- `Up Arrow` / `Down Arrow`: When the cursor is at the top or bottom of a
single-line input, navigate backward or forward through prompt history.
- `Number keys (1-9, multi-digit)` inside selection dialogs: Jump directly to
+15 -16
View File
@@ -1,4 +1,4 @@
# Model routing
## Model routing
Gemini CLI includes a model routing feature that automatically switches to a
fallback model in case of a model failure. This feature is enabled by default
@@ -6,25 +6,24 @@ and provides resilience when the primary model is unavailable.
## How it works
Model routing is managed by the `ModelAvailabilityService`, which monitors model
health and automatically routes requests to available models based on defined
policies.
Model routing is not based on prompt complexity, but is a fallback mechanism.
Here's how it works:
1. **Model failure:** If the currently selected model fails (e.g., due to quota
or server errors), the CLI will initiate the fallback process.
1. **Model failure:** If the currently selected model fails to respond (for
example, due to a server error or other issue), the CLI will initiate the
fallback process.
2. **User consent:** Depending on the failure and the model's policy, the CLI
may prompt you to switch to a fallback model (by default always prompts
you).
2. **User consent:** The CLI will prompt you to ask if you want to switch to
the fallback model. This is handled by the `fallbackModelHandler`.
Some internal utility calls (such as prompt completion and classification)
use a silent fallback chain for `gemini-2.5-flash-lite` and will fall back
to `gemini-2.5-flash` and `gemini-2.5-pro` without prompting or changing the
configured model.
3. **Fallback activation:** If you consent, the CLI will activate the fallback
mode by calling `config.setFallbackMode(true)`.
3. **Model switch:** If approved, or if the policy allows for silent fallback,
the CLI will use an available fallback model for the current turn or the
remainder of the session.
4. **Model switch:** On the next request, the CLI will use the
`DEFAULT_GEMINI_FLASH_MODEL` as the fallback model. This is handled by the
`resolveModel` function in
`packages/cli/src/zed-integration/zedIntegration.ts` which checks if
`isInFallbackMode()` is true.
### Model selection precedence
+28 -32
View File
@@ -1,13 +1,8 @@
# Gemini CLI model selection (`/model` command)
Select your Gemini CLI model. The `/model` command lets you configure the model
used by Gemini CLI, giving you more control over your results. Use **Pro**
models for complex tasks and reasoning, **Flash** models for high speed results,
or the (recommended) **Auto** setting to choose the best model for your tasks.
> **Note:** The `/model` command (and the `--model` flag) does not override the
> model used by sub-agents. Consequently, even when using the `/model` flag you
> may see other models used in your model usage reports.
Select your Gemini CLI model. The `/model` command opens a dialog where you can
configure the model used by Gemini CLI, giving you more control over your
results.
## How to use the `/model` command
@@ -17,45 +12,46 @@ Use the following command in Gemini CLI:
/model
```
Running this command will open a dialog with your options:
Running this command will open a dialog with your model 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 (recommended) | Let the system choose the best model for your task. | gemini-3-pro-preview (if enabled), gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite |
| Pro | For complex tasks that require deep reasoning and creativity. | gemini-3-pro-preview (if enabled), gemini-2.5-pro |
| Flash | For tasks that need a balance of speed and reasoning. | gemini-2.5-flash |
| Flash-Lite | For simple tasks that need to be done quickly. | gemini-2.5-flash-lite |
We recommend selecting one of the above **Auto** options. However, you can
select **Manual** to select a specific model from those available.
### Gemini 3 Pro and preview features
### 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 Pro on Gemini CLI](../get-started/gemini-3.md).
> **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).
To enable Gemini 3 Pro (if available), enable
[**Preview features** by using the `settings` command](../cli/settings.md). Once
enabled, Gemini CLI will attempt to use Gemini 3 Pro when you select **Auto** or
**Pro**. Both **Auto** and **Pro** will try to use Gemini 3 Pro before falling
back to Gemini 2.5 Pro.
You can also use the `--model` flag to specify a particular Gemini model on
startup. For more details, refer to the
[configuration documentation](../get-started/configuration.md).
[configuration documentation](./configuration.md).
Changes to these settings will be applied to all subsequent interactions with
Gemini CLI.
## Best practices for model selection
- **Default to Auto.** For most users, the _Auto_ option model provides a
balance between speed and performance, automatically selecting the correct
model based on the complexity of the task. Example: Developing a web
application could include a mix of complex tasks (building architecture and
scaffolding the project) and simple tasks (generating CSS).
- **Default to Auto (recommended).** For most users, the _Auto (recommended)_
model provides a balance between speed and performance, automatically
selecting the correct model based on the complexity of the task. Example:
Developing a web application could include a mix of complex tasks (building
architecture and scaffolding the project) and simple tasks (generating CSS).
- **Switch to Pro if you aren't getting the results you want.** If you think you
need your model to be a little "smarter," you can manually select Pro. Pro
will provide you with the highest levels of reasoning and creativity. Example:
A complex or multi-stage debugging task.
need your model to be a little "smarter," use Pro. Pro will provide you with
the highest levels of reasoning and creativity. Example: A complex or
multi-stage debugging task.
- **Switch to Flash or Flash-Lite if you need faster results.** If you need a
simple response quickly, Flash or Flash-Lite is the best option. Example:
+1 -1
View File
@@ -11,7 +11,7 @@ Before using sandboxing, you need to install and set up the Gemini CLI:
npm install -g @google/gemini-cli
```
To verify the installation:
To verify the installation
```bash
gemini --version
+52 -70
View File
@@ -18,51 +18,45 @@ Note: Workspace settings override user settings.
Here is a list of all the available settings, grouped by category and ordered as
they appear in the UI.
<!-- SETTINGS-AUTOGEN:START -->
### General
| UI Label | Setting | Description | Default |
| ------------------------------- | ---------------------------------- | ------------------------------------------------------------- | ------- |
| Preview Features (e.g., models) | `general.previewFeatures` | Enable preview features (e.g., preview models). | `false` |
| Vim Mode | `general.vimMode` | Enable Vim keybindings | `false` |
| Enable Auto Update | `general.enableAutoUpdate` | Enable automatic updates. | `true` |
| Enable Prompt Completion | `general.enablePromptCompletion` | Enable AI-powered prompt completion suggestions while typing. | `false` |
| Debug Keystroke Logging | `general.debugKeystrokeLogging` | Enable debug logging of keystrokes to the console. | `false` |
| Enable Session Cleanup | `general.sessionRetention.enabled` | Enable automatic session cleanup | `false` |
| UI Label | Setting | Description | Default |
| ------------------------------- | ---------------------------------- | ---------------------------------------------------------------------------- | ----------- |
| Preview Features (e.g., models) | `general.previewFeatures` | Enable preview features (e.g., preview models). | `false` |
| Vim Mode | `general.vimMode` | Enable Vim keybindings. | `false` |
| Disable Auto Update | `general.disableAutoUpdate` | Disable automatic updates. | `false` |
| Enable Prompt Completion | `general.enablePromptCompletion` | Enable AI-powered prompt completion suggestions while typing. | `false` |
| Debug Keystroke Logging | `general.debugKeystrokeLogging` | Enable debug logging of keystrokes to the console. | `false` |
| Session Retention | `general.sessionRetention` | Settings for automatic session cleanup. This feature is disabled by default. | `undefined` |
| Enable Session Cleanup | `general.sessionRetention.enabled` | Enable automatic session cleanup. | `false` |
### Output
| UI Label | Setting | Description | Default |
| ------------- | --------------- | ------------------------------------------------------ | -------- |
| Output Format | `output.format` | The format of the CLI output. Can be `text` or `json`. | `"text"` |
| UI Label | Setting | Description | Default |
| ------------- | --------------- | ------------------------------------------------------ | ------- |
| Output Format | `output.format` | The format of the CLI output. Can be `text` or `json`. | `text` |
### UI
| UI Label | Setting | Description | Default |
| ------------------------------ | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| Hide Window Title | `ui.hideWindowTitle` | Hide the window title bar | `false` |
| Show Thoughts in Title | `ui.showStatusInTitle` | Show Gemini CLI model thoughts in the terminal window title during the working phase | `false` |
| Dynamic Window Title | `ui.dynamicWindowTitle` | Update the terminal window title with current status icons (Ready: ◇, Action Required: ✋, Working: ✦) | `true` |
| Show Home Directory Warning | `ui.showHomeDirectoryWarning` | Show a warning when running Gemini CLI in the home directory. | `true` |
| Hide Tips | `ui.hideTips` | Hide helpful tips in the UI | `false` |
| Hide Banner | `ui.hideBanner` | Hide the application banner | `false` |
| Hide Context Summary | `ui.hideContextSummary` | Hide the context summary (GEMINI.md, MCP servers) above the input. | `false` |
| Hide CWD | `ui.footer.hideCWD` | Hide the current working directory path in the footer. | `false` |
| Hide Sandbox Status | `ui.footer.hideSandboxStatus` | Hide the sandbox status indicator in the footer. | `false` |
| Hide Model Info | `ui.footer.hideModelInfo` | Hide the model name and context usage in the footer. | `false` |
| Hide Context Window Percentage | `ui.footer.hideContextPercentage` | Hides the context window remaining percentage. | `true` |
| Hide Footer | `ui.hideFooter` | Hide the footer from the UI | `false` |
| Show Memory Usage | `ui.showMemoryUsage` | Display memory usage information in the UI | `false` |
| Show Line Numbers | `ui.showLineNumbers` | Show line numbers in the chat. | `true` |
| Show Citations | `ui.showCitations` | Show citations for generated text in the chat. | `false` |
| Show Model Info In Chat | `ui.showModelInfoInChat` | Show the model name in the chat for each model turn. | `false` |
| Use Alternate Screen Buffer | `ui.useAlternateBuffer` | Use an alternate screen buffer for the UI, preserving shell history. | `false` |
| Use Background Color | `ui.useBackgroundColor` | Whether to use background colors in the UI. | `true` |
| 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` |
| Enable Loading Phrases | `ui.accessibility.enableLoadingPhrases` | Enable loading phrases during operations. | `true` |
| Screen Reader Mode | `ui.accessibility.screenReader` | Render output in plain-text to be more screen reader accessible | `false` |
| UI Label | Setting | Description | Default |
| ------------------------------ | ---------------------------------------- | -------------------------------------------------------------------- | ------- |
| Hide Window Title | `ui.hideWindowTitle` | Hide the window title bar. | `false` |
| Show Status in Title | `ui.showStatusInTitle` | Show Gemini CLI status and thoughts in the terminal window title. | `false` |
| Hide Tips | `ui.hideTips` | Hide helpful tips in the UI. | `false` |
| Hide Banner | `ui.hideBanner` | Hide the application banner. | `false` |
| Hide Context Summary | `ui.hideContextSummary` | Hide the context summary (GEMINI.md, MCP servers) above the input. | `false` |
| Hide CWD | `ui.footer.hideCWD` | Hide the current working directory path in the footer. | `false` |
| Hide Sandbox Status | `ui.footer.hideSandboxStatus` | Hide the sandbox status indicator in the footer. | `false` |
| Hide Model Info | `ui.footer.hideModelInfo` | Hide the model name and context usage in the footer. | `false` |
| Hide Context Window Percentage | `ui.footer.hideContextPercentage` | Hides the context window remaining percentage. | `true` |
| Hide Footer | `ui.hideFooter` | Hide the footer from the UI. | `false` |
| Show Memory Usage | `ui.showMemoryUsage` | Display memory usage information in the UI. | `false` |
| Show Line Numbers | `ui.showLineNumbers` | Show line numbers in the chat. | `false` |
| Show Citations | `ui.showCitations` | Show citations for generated text in the chat. | `false` |
| Use Full Width | `ui.useFullWidth` | Use the entire width of the terminal for output. | `true` |
| Use Alternate Screen Buffer | `ui.useAlternateBuffer` | Use an alternate screen buffer for the UI, preserving shell history. | `true` |
| Disable Loading Phrases | `ui.accessibility.disableLoadingPhrases` | Disable loading phrases for accessibility. | `false` |
| Screen Reader Mode | `ui.accessibility.screenReader` | Render output in plain-text to be more screen reader accessible. | `false` |
### IDE
@@ -75,7 +69,7 @@ they appear in the UI.
| UI Label | Setting | Description | Default |
| ----------------------- | ---------------------------- | -------------------------------------------------------------------------------------- | ------- |
| Max Session Turns | `model.maxSessionTurns` | Maximum number of user/model/tool turns to keep in a session. -1 means unlimited. | `-1` |
| Compression Threshold | `model.compressionThreshold` | The fraction of context usage at which to trigger context compression (e.g. 0.2, 0.3). | `0.5` |
| Compression Threshold | `model.compressionThreshold` | The fraction of context usage at which to trigger context compression (e.g. 0.2, 0.3). | `0.2` |
| Skip Next Speaker Check | `model.skipNextSpeakerCheck` | Skip the next speaker check. | `true` |
### Context
@@ -87,44 +81,32 @@ they appear in the UI.
| Respect .gitignore | `context.fileFiltering.respectGitIgnore` | Respect .gitignore files when searching. | `true` |
| Respect .geminiignore | `context.fileFiltering.respectGeminiIgnore` | Respect .geminiignore files when searching. | `true` |
| Enable Recursive File Search | `context.fileFiltering.enableRecursiveFileSearch` | Enable recursive file search functionality when completing @ references in the prompt. | `true` |
| Enable Fuzzy Search | `context.fileFiltering.enableFuzzySearch` | Enable fuzzy search when searching for files. | `true` |
| Disable Fuzzy Search | `context.fileFiltering.disableFuzzySearch` | Disable fuzzy search when searching for files. | `false` |
### Tools
| UI Label | Setting | Description | Default |
| -------------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- |
| Enable Interactive Shell | `tools.shell.enableInteractiveShell` | Use node-pty for an interactive shell experience. Fallback to child_process still applies. | `true` |
| Show Color | `tools.shell.showColor` | Show color in shell output. | `false` |
| Auto Accept | `tools.autoAccept` | Automatically accept and execute tool calls that are considered safe (e.g., read-only operations). | `false` |
| Approval Mode | `tools.approvalMode` | The default approval mode for tool execution. 'default' prompts for approval, 'auto_edit' auto-approves edit tools, and 'plan' is read-only mode. 'yolo' is not supported yet. | `"default"` |
| Use Ripgrep | `tools.useRipgrep` | Use ripgrep for file content search instead of the fallback implementation. Provides faster search performance. | `true` |
| Enable Tool Output Truncation | `tools.enableToolOutputTruncation` | Enable truncation of large tool outputs. | `true` |
| Tool Output Truncation Threshold | `tools.truncateToolOutputThreshold` | Truncate tool output if it is larger than this many characters. Set to -1 to disable. | `4000000` |
| Tool Output Truncation Lines | `tools.truncateToolOutputLines` | The number of lines to keep when truncating tool output. | `1000` |
| Disable LLM Correction | `tools.disableLLMCorrection` | Disable LLM-based error correction for edit tools. When enabled, tools will fail immediately if exact string matches are not found, instead of attempting to self-correct. | `true` |
| UI Label | Setting | Description | Default |
| -------------------------------- | ------------------------------------ | --------------------------------------------------------------------------------------------------------------- | ------- |
| Enable Interactive Shell | `tools.shell.enableInteractiveShell` | Use node-pty for an interactive shell experience. Fallback to child_process still applies. | `true` |
| Show Color | `tools.shell.showColor` | Show color in shell output. | `false` |
| Auto Accept | `tools.autoAccept` | Automatically accept and execute tool calls that are considered safe (e.g., read-only operations). | `false` |
| Use Ripgrep | `tools.useRipgrep` | Use ripgrep for file content search instead of the fallback implementation. Provides faster search performance. | `true` |
| Enable Tool Output Truncation | `tools.enableToolOutputTruncation` | Enable truncation of large tool outputs. | `true` |
| Tool Output Truncation Threshold | `tools.truncateToolOutputThreshold` | Truncate tool output if it is larger than this many characters. Set to -1 to disable. | `10000` |
| Tool Output Truncation Lines | `tools.truncateToolOutputLines` | The number of lines to keep when truncating tool output. | `100` |
| Enable Message Bus Integration | `tools.enableMessageBusIntegration` | Enable policy-based tool confirmation via message bus integration. | `false` |
### Security
| UI Label | Setting | Description | Default |
| ------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------- | ------- |
| Disable YOLO Mode | `security.disableYoloMode` | Disable YOLO mode, even if enabled by a flag. | `false` |
| Allow Permanent Tool Approval | `security.enablePermanentToolApproval` | Enable the "Allow for all future sessions" option in tool confirmation dialogs. | `false` |
| Blocks extensions from Git | `security.blockGitExtensions` | Blocks installing and loading extensions from Git. | `false` |
| Folder Trust | `security.folderTrust.enabled` | Setting to track whether Folder trust is enabled. | `false` |
| Enable Environment Variable Redaction | `security.environmentVariableRedaction.enabled` | Enable redaction of environment variables that may contain secrets. | `false` |
| UI Label | Setting | Description | Default |
| -------------------------- | ------------------------------ | -------------------------------------------------- | ------- |
| Disable YOLO Mode | `security.disableYoloMode` | Disable YOLO mode, even if enabled by a flag. | `false` |
| Blocks extensions from Git | `security.blockGitExtensions` | Blocks installing and loading extensions from Git. | `false` |
| Folder Trust | `security.folderTrust.enabled` | Setting to track whether Folder trust is enabled. | `false` |
### Experimental
| UI Label | Setting | Description | Default |
| ---------------- | ---------------------------- | ----------------------------------------------------------------------------------- | ------- |
| Agent Skills | `experimental.skills` | Enable Agent Skills (experimental). | `false` |
| Use OSC 52 Paste | `experimental.useOSC52Paste` | Use OSC 52 sequence for pasting instead of clipboardy (useful for remote sessions). | `false` |
| Plan | `experimental.plan` | Enable planning features (Plan Mode and tools). | `false` |
### HooksConfig
| UI Label | Setting | Description | Default |
| ------------------ | --------------------------- | ------------------------------------------------ | ------- |
| Hook Notifications | `hooksConfig.notifications` | Show visual indicators when hooks are executing. | `true` |
<!-- SETTINGS-AUTOGEN:END -->
| UI Label | Setting | Description | Default |
| ----------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------ | ------- |
| Enable Codebase Investigator | `experimental.codebaseInvestigatorSettings.enabled` | Enable the Codebase Investigator agent. | `true` |
| Codebase Investigator Max Num Turns | `experimental.codebaseInvestigatorSettings.maxNumTurns` | Maximum number of turns for the Codebase Investigator agent. | `10` |
-188
View File
@@ -1,188 +0,0 @@
# Agent Skills
_Note: This is an experimental feature enabled via `experimental.skills`. You
can also search for "Skills" within the `/settings` interactive UI to toggle
this and manage other skill-related settings._
Agent Skills allow you to extend Gemini CLI with specialized expertise,
procedural workflows, and task-specific resources. Based on the
[Agent Skills](https://agentskills.io) open standard, a "skill" is a
self-contained directory that packages instructions and assets into a
discoverable capability.
## Overview
Unlike general context files ([`GEMINI.md`](./gemini-md.md)), which provide
persistent workspace-wide background, Skills represent **on-demand expertise**.
This allows Gemini to maintain a vast library of specialized capabilities—such
as security auditing, cloud deployments, or codebase migrations—without
cluttering the model's immediate context window.
Gemini autonomously decides when to employ a skill based on your request and the
skill's description. When a relevant skill is identified, the model "pulls in"
the full instructions and resources required to complete the task using the
`activate_skill` tool.
## Key Benefits
- **Shared Expertise:** Package complex workflows (like a specific team's PR
review process) into a folder that anyone can use.
- **Repeatable Workflows:** Ensure complex multi-step tasks are performed
consistently by providing a procedural framework.
- **Resource Bundling:** Include scripts, templates, or example data alongside
instructions so the agent has everything it needs.
- **Progressive Disclosure:** Only skill metadata (name and description) is
loaded initially. Detailed instructions and resources are only disclosed when
the model explicitly activates the skill, saving context tokens.
## Skill Discovery Tiers
Gemini CLI discovers skills from three primary locations:
1. **Workspace Skills** (`.gemini/skills/`): Workspace-specific skills that are
typically committed to version control and shared with the team.
2. **User Skills** (`~/.gemini/skills/`): Personal skills available across all
your workspaces.
3. **Extension Skills**: Skills bundled within installed
[extensions](../extensions/index.md).
**Precedence:** If multiple skills share the same name, higher-precedence
locations override lower ones: **Workspace > User > Extension**.
## Managing Skills
### In an Interactive Session
Use the `/skills` slash command to view and manage available expertise:
- `/skills list` (default): Shows all discovered skills and their status.
- `/skills disable <name>`: Prevents a specific skill from being used.
- `/skills enable <name>`: Re-enables a disabled skill.
- `/skills reload`: Refreshes the list of discovered skills from all tiers.
_Note: `/skills disable` and `/skills enable` default to the `user` scope. Use
`--scope workspace` to manage workspace-specific settings._
### From the Terminal
The `gemini skills` command provides management utilities:
```bash
# List all discovered skills
gemini skills list
# Install a skill from a Git repository, local directory, or zipped skill file (.skill)
# Uses the user scope by default (~/.gemini/skills)
gemini skills install https://github.com/user/repo.git
gemini skills install /path/to/local/skill
gemini skills install /path/to/local/my-expertise.skill
# Install a specific skill from a monorepo or subdirectory using --path
gemini skills install https://github.com/my-org/my-skills.git --path skills/frontend-design
# Install to the workspace scope (.gemini/skills)
gemini skills install /path/to/skill --scope workspace
# Uninstall a skill by name
gemini skills uninstall my-expertise --scope workspace
# Enable a skill (globally)
gemini skills enable my-expertise
# Disable a skill. Can use --scope to specify workspace or user (defaults to workspace)
gemini skills disable my-expertise --scope workspace
```
## Creating a Skill
A skill is a directory containing a `SKILL.md` file at its root. This file uses
YAML frontmatter for metadata and Markdown for instructions.
### Folder Structure
Skills are self-contained directories. At a minimum, a skill requires a
`SKILL.md` file, but can include other resources:
```text
my-skill/
├── SKILL.md (Required) Instructions and metadata
├── scripts/ (Optional) Executable scripts/tools
├── references/ (Optional) Static documentation and examples
└── assets/ (Optional) Templates and binary resources
```
### Basic Structure (SKILL.md)
```markdown
---
name: <unique-name>
description: <what the skill does and when Gemini should use it>
---
<your instructions for how the agent should behave / use the skill>
```
- **`name`**: A unique identifier (lowercase, alphanumeric, and dashes).
- **`description`**: The most critical field. Gemini uses this to decide when
the skill is relevant. Be specific about the expertise provided.
- **Body**: Everything below the second `---` is injected as expert procedural
guidance for the model.
### Example: Team Code Reviewer
Create `~/.gemini/skills/code-reviewer/SKILL.md`:
```markdown
---
name: code-reviewer
description:
Expertise in reviewing code for style, security, and performance. Use when the
user asks for "feedback," a "review," or to "check" their changes.
---
# Code Reviewer
You are an expert code reviewer. When reviewing code, follow this workflow:
1. **Analyze**: Review the staged changes or specific files provided. Ensure
that the changes are scoped properly and represent minimal changes required
to address the issue.
2. **Style**: Ensure code follows the workspace's conventions and idiomatic
patterns as described in the `GEMINI.md` file.
3. **Security**: Flag any potential security vulnerabilities.
4. **Tests**: Verify that new logic has corresponding test coverage and that
the test coverage adequately validates the changes.
Provide your feedback as a concise bulleted list of "Strengths" and
"Opportunities."
```
### Resource Conventions
While you can structure your skill directory however you like, the Agent Skills
standard encourages these conventions:
- **`scripts/`**: Executable scripts (bash, python, node) the agent can run.
- **`references/`**: Static documentation, schemas, or example data for the
agent to consult.
- **`assets/`**: Code templates, boilerplate, or binary resources.
When a skill is activated, Gemini CLI provides the model with a tree view of the
entire skill directory, allowing it to discover and utilize these assets.
## How it Works (Security & Privacy)
1. **Discovery**: At the start of a session, Gemini CLI scans the discovery
tiers and injects the name and description of all enabled skills into the
system prompt.
2. **Activation**: When Gemini identifies a task matching a skill's
description, it calls the `activate_skill` tool.
3. **Consent**: You will see a confirmation prompt in the UI detailing the
skill's name, purpose, and the directory path it will gain access to.
4. **Injection**: Upon your approval:
- The `SKILL.md` body and folder structure is added to the conversation
history.
- The skill's directory is added to the agent's allowed file paths, granting
it permission to read any bundled assets.
5. **Execution**: The model proceeds with the specialized expertise active. It
is instructed to prioritize the skill's procedural guidance within reason.
-125
View File
@@ -1,125 +0,0 @@
# System Prompt Override (GEMINI_SYSTEM_MD)
The core system instructions that guide Gemini CLI can be completely replaced
with your own Markdown file. This feature is controlled via the
`GEMINI_SYSTEM_MD` environment variable.
## Overview
The `GEMINI_SYSTEM_MD` variable instructs the CLI to use an external Markdown
file for its system prompt, completely overriding the built-in default. This is
a full replacement, not a merge. If you use a custom file, none of the original
core instructions will apply unless you include them yourself.
This feature is intended for advanced users who need to enforce strict,
project-specific behavior or create a customized persona.
> Tip: You can export the current default system prompt to a file first, review
> it, and then selectively modify or replace it (see
> [“Export the default prompt”](#export-the-default-prompt-recommended)).
## How to enable
You can set the environment variable temporarily in your shell, or persist it
via a `.gemini/.env` file. See
[Persisting Environment Variables](../get-started/authentication.md#persisting-environment-variables).
- Use the project default path (`.gemini/system.md`):
- `GEMINI_SYSTEM_MD=true` or `GEMINI_SYSTEM_MD=1`
- The CLI reads `./.gemini/system.md` (relative to your current project
directory).
- Use a custom file path:
- `GEMINI_SYSTEM_MD=/absolute/path/to/my-system.md`
- Relative paths are supported and resolved from the current working
directory.
- Tilde expansion is supported (e.g., `~/my-system.md`).
- Disable the override (use builtin prompt):
- `GEMINI_SYSTEM_MD=false` or `GEMINI_SYSTEM_MD=0` or unset the variable.
If the override is enabled but the target file does not exist, the CLI will
error with: `missing system prompt file '<path>'`.
## Quick examples
- Oneoff session using a project file:
- `GEMINI_SYSTEM_MD=1 gemini`
- Persist for a project using `.gemini/.env`:
- Create `.gemini/system.md`, then add to `.gemini/.env`:
- `GEMINI_SYSTEM_MD=1`
- Use a custom file under your home directory:
- `GEMINI_SYSTEM_MD=~/prompts/SYSTEM.md gemini`
## UI indicator
When `GEMINI_SYSTEM_MD` is active, the CLI shows a `|⌐■_■|` indicator in the UI
to signal custom systemprompt mode.
## Variable Substitution
When using a custom system prompt file, you can use the following variables to
dynamically include built-in content:
- `${AgentSkills}`: Injects a complete section (including header) of all
available agent skills.
- `${SubAgents}`: Injects a complete section (including header) of available
sub-agents.
- `${AvailableTools}`: Injects a bulleted list of all currently enabled tool
names.
- Tool Name Variables: Injects the actual name of a tool using the pattern:
`${toolName}_ToolName` (e.g., `${write_file_ToolName}`,
`${run_shell_command_ToolName}`).
This pattern is generated dynamically for all available tools.
### Example
```markdown
# Custom System Prompt
You are a helpful assistant. ${AgentSkills}
${SubAgents}
## Tooling
The following tools are available to you: ${AvailableTools}
You can use ${write_file_ToolName} to save logs.
```
## Export the default prompt (recommended)
Before overriding, export the current default prompt so you can review required
safety and workflow rules.
- Write the builtin prompt to the project default path:
- `GEMINI_WRITE_SYSTEM_MD=1 gemini`
- Or write to a custom path:
- `GEMINI_WRITE_SYSTEM_MD=~/prompts/DEFAULT_SYSTEM.md gemini`
This creates the file and writes the current builtin system prompt to it.
## Best practices: SYSTEM.md vs GEMINI.md
- SYSTEM.md (firmware):
- Nonnegotiable operational rules: safety, tooluse protocols, approvals, and
mechanics that keep the CLI reliable.
- Stable across tasks and projects (or per project when needed).
- GEMINI.md (strategy):
- Persona, goals, methodologies, and project/domain context.
- Evolves per task; relies on SYSTEM.md for safe execution.
Keep SYSTEM.md minimal but complete for safety and tool operation. Keep
GEMINI.md focused on highlevel guidance and project specifics.
## Troubleshooting
- Error: `missing system prompt file '…'`
- Ensure the referenced path exists and is readable.
- For `GEMINI_SYSTEM_MD=1|true`, create `./.gemini/system.md` in your project.
- Override not taking effect
- Confirm the variable is loaded (use `.gemini/.env` or export in your shell).
- Paths are resolved from the current working directory; try an absolute path.
- Restore defaults
- Unset `GEMINI_SYSTEM_MD` or set it to `0`/`false`.
+12 -76
View File
@@ -8,17 +8,14 @@ Learn how to enable and setup OpenTelemetry for Gemini CLI.
- [Configuration](#configuration)
- [Google Cloud telemetry](#google-cloud-telemetry)
- [Prerequisites](#prerequisites)
- [Authenticating with CLI Credentials](#authenticating-with-cli-credentials)
- [Direct export (recommended)](#direct-export-recommended)
- [Collector-based export (advanced)](#collector-based-export-advanced)
- [Monitoring Dashboards](#monitoring-dashboards)
- [Local telemetry](#local-telemetry)
- [File-based output (recommended)](#file-based-output-recommended)
- [Collector-based export (advanced)](#collector-based-export-advanced-1)
- [Logs and metrics](#logs-and-metrics)
- [Logs](#logs)
- [Sessions](#sessions)
- [Approval Mode](#approval-mode)
- [Tools](#tools)
- [Files](#files)
- [API](#api)
@@ -77,16 +74,15 @@ observability framework — Gemini CLI's observability system provides:
All telemetry behavior is controlled through your `.gemini/settings.json` file.
Environment variables can be used to override the settings in the file.
| Setting | Environment Variable | Description | Values | Default |
| -------------- | -------------------------------- | --------------------------------------------------- | ----------------- | ----------------------- |
| `enabled` | `GEMINI_TELEMETRY_ENABLED` | Enable or disable telemetry | `true`/`false` | `false` |
| `target` | `GEMINI_TELEMETRY_TARGET` | Where to send telemetry data | `"gcp"`/`"local"` | `"local"` |
| `otlpEndpoint` | `GEMINI_TELEMETRY_OTLP_ENDPOINT` | OTLP collector endpoint | URL string | `http://localhost:4317` |
| `otlpProtocol` | `GEMINI_TELEMETRY_OTLP_PROTOCOL` | OTLP transport protocol | `"grpc"`/`"http"` | `"grpc"` |
| `outfile` | `GEMINI_TELEMETRY_OUTFILE` | Save telemetry to file (overrides `otlpEndpoint`) | file path | - |
| `logPrompts` | `GEMINI_TELEMETRY_LOG_PROMPTS` | Include prompts in telemetry logs | `true`/`false` | `true` |
| `useCollector` | `GEMINI_TELEMETRY_USE_COLLECTOR` | Use external OTLP collector (advanced) | `true`/`false` | `false` |
| `useCliAuth` | `GEMINI_TELEMETRY_USE_CLI_AUTH` | Use CLI credentials for telemetry (GCP target only) | `true`/`false` | `false` |
| Setting | Environment Variable | Description | Values | Default |
| -------------- | -------------------------------- | ------------------------------------------------- | ----------------- | ----------------------- |
| `enabled` | `GEMINI_TELEMETRY_ENABLED` | Enable or disable telemetry | `true`/`false` | `false` |
| `target` | `GEMINI_TELEMETRY_TARGET` | Where to send telemetry data | `"gcp"`/`"local"` | `"local"` |
| `otlpEndpoint` | `GEMINI_TELEMETRY_OTLP_ENDPOINT` | OTLP collector endpoint | URL string | `http://localhost:4317` |
| `otlpProtocol` | `GEMINI_TELEMETRY_OTLP_PROTOCOL` | OTLP transport protocol | `"grpc"`/`"http"` | `"grpc"` |
| `outfile` | `GEMINI_TELEMETRY_OUTFILE` | Save telemetry to file (overrides `otlpEndpoint`) | file path | - |
| `logPrompts` | `GEMINI_TELEMETRY_LOG_PROMPTS` | Include prompts in telemetry logs | `true`/`false` | `true` |
| `useCollector` | `GEMINI_TELEMETRY_USE_COLLECTOR` | Use external OTLP collector (advanced) | `true`/`false` | `false` |
**Note on boolean environment variables:** For the boolean settings (`enabled`,
`logPrompts`, `useCollector`), setting the corresponding environment variable to
@@ -134,34 +130,6 @@ Before using either method below, complete these steps:
--project="$OTLP_GOOGLE_CLOUD_PROJECT"
```
### Authenticating with CLI Credentials
By default, the telemetry collector for Google Cloud uses Application Default
Credentials (ADC). However, you can configure it to use the same OAuth
credentials that you use to log in to the Gemini CLI. This is useful in
environments where you don't have ADC set up.
To enable this, set the `useCliAuth` property in your `telemetry` settings to
`true`:
```json
{
"telemetry": {
"enabled": true,
"target": "gcp",
"useCliAuth": true
}
}
```
**Important:**
- This setting requires the use of **Direct Export** (in-process exporters).
- It **cannot** be used with `useCollector: true`. If you enable both, telemetry
will be disabled and an error will be logged.
- The CLI will automatically use your credentials to authenticate with Google
Cloud Trace, Metrics, and Logging APIs.
### Direct export (recommended)
Sends telemetry directly to Google Cloud services. No collector needed.
@@ -216,24 +184,6 @@ forward data to Google Cloud.
- Open `~/.gemini/tmp/<projectHash>/otel/collector-gcp.log` to view local
collector logs.
### Monitoring Dashboards
Gemini CLI provides a pre-configured
[Google Cloud Monitoring](https://cloud.google.com/monitoring) dashboard to
visualize your telemetry.
This dashboard can be found under **Google Cloud Monitoring Dashboard
Templates** as "**Gemini CLI Monitoring**".
![Gemini CLI Monitoring Dashboard Overview](/docs/assets/monitoring-dashboard-overview.png)
![Gemini CLI Monitoring Dashboard Metrics](/docs/assets/monitoring-dashboard-metrics.png)
![Gemini CLI Monitoring Dashboard Logs](/docs/assets/monitoring-dashboard-logs.png)
To learn more, check out this blog post:
[Instant insights: Gemini CLIs new pre-configured monitoring dashboards](https://cloud.google.com/blog/topics/developers-practitioners/instant-insights-gemini-clis-new-pre-configured-monitoring-dashboards/).
## Local telemetry
For local development and debugging, you can capture telemetry data locally:
@@ -316,23 +266,9 @@ Captures startup configuration and user prompt submissions.
- `prompt` (string; excluded if `telemetry.logPrompts` is `false`)
- `auth_type` (string)
#### Approval Mode
Tracks changes and duration of approval modes.
- `approval_mode_switch`: Approval mode was changed.
- **Attributes**:
- `from_mode` (string)
- `to_mode` (string)
- `approval_mode_duration`: Duration spent in an approval mode.
- **Attributes**:
- `mode` (string)
- `duration_ms` (int)
#### Tools
Captures tool executions, output truncation, and Edit behavior.
Captures tool executions, output truncation, and Smart Edit behavior.
- `gemini_cli.tool_call`: Emitted for each tool (function) call.
- **Attributes**:
@@ -360,11 +296,11 @@ Captures tool executions, output truncation, and Edit behavior.
- `lines` (int)
- `prompt_id` (string)
- `gemini_cli.edit_strategy`: Edit strategy chosen.
- `gemini_cli.smart_edit_strategy`: Smart Edit strategy chosen.
- **Attributes**:
- `strategy` (string)
- `gemini_cli.edit_correction`: Edit correction result.
- `gemini_cli.smart_edit_correction`: Smart Edit correction result.
- **Attributes**:
- `correction` ("success" | "failure")
+2
View File
@@ -86,6 +86,7 @@ color keys. For example:
- `Gray`
- `DiffAdded` (optional, for added lines in diffs)
- `DiffRemoved` (optional, for removed lines in diffs)
- `DiffModified` (optional, for modified lines in diffs)
You can also override individual UI text roles by adding a nested `text` object.
This object supports the keys `primary`, `secondary`, `link`, `accent`, and
@@ -156,6 +157,7 @@ custom theme defined in `settings.json`.
"Gray": "#ABB2BF",
"DiffAdded": "#A6E3A1",
"DiffRemoved": "#F38BA8",
"DiffModified": "#89B4FA",
"GradientColors": ["#4796E4", "#847ACE", "#C3677F"]
}
```
-4
View File
@@ -2,10 +2,6 @@
This page contains tutorials for interacting with Gemini CLI.
## Agent Skills
- [Getting Started with Agent Skills](./tutorials/skills-getting-started.md)
## Setting up a Model Context Protocol (MCP) server
> [!CAUTION] Before using a third-party MCP server, ensure you trust its source
@@ -1,124 +0,0 @@
# Getting Started with Agent Skills
Agent Skills allow you to extend Gemini CLI with specialized expertise. This
tutorial will guide you through creating your first skill, enabling it, and
using it in a session.
## 1. Enable Agent Skills
Agent Skills are currently an experimental feature and must be enabled in your
settings.
### Via the interactive UI
1. Start a Gemini CLI session by running `gemini`.
2. Type `/settings` to open the interactive settings dialog.
3. Search for "Skills".
4. Toggle **Agent Skills** to `true`.
5. Press `Esc` to save and exit. You may need to restart the CLI for the
changes to take effect.
### Via `settings.json`
Alternatively, you can manually edit your global settings file at
`~/.gemini/settings.json` (create it if it doesn't exist):
```json
{
"experimental": {
"skills": true
}
}
```
## 2. Create Your First Skill
A skill is a directory containing a `SKILL.md` file. Let's create an **API
Auditor** skill that helps you verify if local or remote endpoints are
responding correctly.
1. **Create the skill directory structure:**
```bash
mkdir -p .gemini/skills/api-auditor/scripts
```
2. **Create the `SKILL.md` file:** Create a file at
`.gemini/skills/api-auditor/SKILL.md` with the following content:
```markdown
---
name: api-auditor
description:
Expertise in auditing and testing API endpoints. Use when the user asks to
"check", "test", or "audit" a URL or API.
---
# API Auditor Instructions
You act as a QA engineer specialized in API reliability. When this skill is
active, you MUST:
1. **Audit**: Use the bundled `scripts/audit.js` utility to check the
status of the provided URL.
2. **Report**: Analyze the output (status codes, latency) and explain any
failures in plain English.
3. **Secure**: Remind the user if they are testing a sensitive endpoint
without an `https://` protocol.
```
3. **Create the bundled Node.js script:** Create a file at
`.gemini/skills/api-auditor/scripts/audit.js`. This script will be used by
the agent to perform the actual check:
```javascript
// .gemini/skills/api-auditor/scripts/audit.js
const url = process.argv[2];
if (!url) {
console.error('Usage: node audit.js <url>');
process.exit(1);
}
console.log(`Auditing ${url}...`);
fetch(url, { method: 'HEAD' })
.then((r) => console.log(`Result: Success (Status ${r.status})`))
.catch((e) => console.error(`Result: Failed (${e.message})`));
```
## 3. Verify the Skill is Discovered
Use the `/skills` slash command (or `gemini skills list` from your terminal) to
see if Gemini CLI has found your new skill.
In a Gemini CLI session:
```
/skills list
```
You should see `api-auditor` in the list of available skills.
## 4. Use the Skill in a Chat
Now, let's see the skill in action. Start a new session and ask a question about
an endpoint.
**User:** "Can you audit http://geminili.com"
Gemini will recognize the request matches the `api-auditor` description and will
ask for your permission to activate it.
**Model:** (After calling `activate_skill`) "I've activated the **api-auditor**
skill. I'll run the audit script now..."
Gemini will then use the `run_shell_command` tool to execute your bundled Node
script:
`node .gemini/skills/api-auditor/scripts/audit.js http://geminili.com`
## Next Steps
- Explore [Agent Skills Authoring Guide](../skills.md#creating-a-skill) to learn
about more advanced skill features.
- Learn how to share skills via [Extensions](../../extensions/index.md).
-18
View File
@@ -45,21 +45,3 @@ npm uninstall -g @google/gemini-cli
```
This command completely removes the package from your system.
## Method 3: Homebrew
If you installed the CLI globally using Homebrew (e.g.,
`brew install gemini-cli`), use the `brew uninstall` command to remove it.
```bash
brew uninstall gemini-cli
```
## Method 4: MacPorts
If you installed the CLI globally using MacPorts (e.g.,
`sudo port install gemini-cli`), use the `port uninstall` command to remove it.
```bash
sudo port uninstall gemini-cli
```
-4
View File
@@ -68,10 +68,6 @@ If you are using the default "pro" model and the CLI detects that you are being
rate-limited, it automatically switches to the "flash" model for the current
session. This allows you to continue working without interruption.
Internal utility calls that use `gemini-2.5-flash-lite` (for example, prompt
completion and classification) silently fall back to `gemini-2.5-flash` and
`gemini-2.5-pro` when quota is exhausted, without changing the configured model.
## File discovery service
The file discovery service is responsible for finding files in the project that
-2
View File
@@ -83,9 +83,7 @@ The processor automatically detects and prevents circular imports:
# file-a.md
@./file-b.md
```
```markdown
# file-b.md
@./file-a.md <!-- This will be detected and prevented -->
+3 -57
View File
@@ -1,32 +1,13 @@
# Policy engine
:::note This feature is currently in testing. To enable it, set
`tools.enableMessageBusIntegration` to `true` in your `settings.json` file. :::
The Gemini CLI includes a powerful policy engine that provides fine-grained
control over tool execution. It allows users and administrators to define rules
that determine whether a tool call should be allowed, denied, or require user
confirmation.
## Quick start
To create your first policy:
1. **Create the policy directory** if it doesn't exist:
```bash
mkdir -p ~/.gemini/policies
```
2. **Create a new policy file** (e.g., `~/.gemini/policies/my-rules.toml`). You
can use any filename ending in `.toml`; all such files in this directory
will be loaded and combined:
```toml
[[rule]]
toolName = "run_shell_command"
commandPrefix = "git status"
decision = "allow"
priority = 100
```
3. **Run a command** that triggers the policy (e.g., ask Gemini CLI to
`git status`). The tool will now execute automatically without prompting for
confirmation.
## Core concepts
The policy engine operates on a set of rules. Each rule is a combination of
@@ -146,38 +127,6 @@ A rule matches a tool call if all of its conditions are met:
Policies are defined in `.toml` files. The CLI loads these files from Default,
User, and (if configured) Admin directories.
### Policy locations
| Tier | Type | Location |
| :-------- | :----- | :-------------------------- |
| **User** | Custom | `~/.gemini/policies/*.toml` |
| **Admin** | System | _See below (OS specific)_ |
#### System-wide policies (Admin)
Administrators can enforce system-wide policies (Tier 3) that override all user
and default settings. These policies must be placed in specific, secure
directories:
| OS | Policy Directory Path |
| :---------- | :------------------------------------------------ |
| **Linux** | `/etc/gemini-cli/policies` |
| **macOS** | `/Library/Application Support/GeminiCli/policies` |
| **Windows** | `C:\ProgramData\gemini-cli\policies` |
**Security Requirements:**
To prevent privilege escalation, the CLI enforces strict security checks on
admin directories. If checks fail, system policies are **ignored**.
- **Linux / macOS:** Must be owned by `root` (UID 0) and NOT writable by group
or others (e.g., `chmod 755`).
- **Windows:** Must be in `C:\ProgramData`. Standard users (`Users`, `Everyone`)
must NOT have `Write`, `Modify`, or `Full Control` permissions. _Tip: If you
see a security warning, use the folder properties to remove write permissions
for non-admin groups. You may need to "Disable inheritance" in Advanced
Security Settings._
### TOML rule schema
Here is a breakdown of the fields available in a TOML policy rule:
@@ -290,9 +239,6 @@ The Gemini CLI ships with a set of default policies to provide a safe
out-of-the-box experience.
- **Read-only tools** (like `read_file`, `glob`) are generally **allowed**.
- **Agent delegation** (like `delegate_to_agent`) defaults to **`ask_user`** to
ensure remote agents can prompt for confirmation, but local sub-agent actions
are executed silently and checked individually.
- **Write tools** (like `write_file`, `run_shell_command`) default to
**`ask_user`**.
- In **`yolo`** mode, a high-priority rule allows all tools.
-139
View File
@@ -1,139 +0,0 @@
# Extensions on Gemini CLI: Best practices
This guide covers best practices for developing, securing, and maintaining
Gemini CLI extensions.
## Development
Developing extensions for Gemini CLI is intended to be a lightweight, iterative
process.
### Structure your extension
While simple extensions can just be a few files, we recommend a robust structure
for complex extensions:
```
my-extension/
├── package.json
├── tsconfig.json
├── gemini-extension.json
├── src/
│ ├── index.ts
│ └── tools/
└── dist/
```
- **Use TypeScript**: We strongly recommend using TypeScript for type safety and
better tooling.
- **Separate source and build**: Keep your source code in `src` and build to
`dist`.
- **Bundle dependencies**: If your extension has many dependencies, consider
bundling them (e.g., with `esbuild` or `webpack`) to reduce install time and
potential conflicts.
### Iterate with `link`
Use `gemini extensions link` to develop locally without constantly reinstalling:
```bash
cd my-extension
gemini extensions link .
```
Changes to your code (after rebuilding) will be immediately available in the CLI
on restart.
### Use `GEMINI.md` effectively
Your `GEMINI.md` file provides context to the model. Keep it focused:
- **Do:** Explain high-level goals and how to use the provided tools.
- **Don't:** Dump your entire documentation.
- **Do:** Use clear, concise language.
## Security
When building a Gemini CLI extension, follow general security best practices
(such as least privilege and input validation) to reduce risk.
### Minimal permissions
When defining tools in your MCP server, only request the permissions necessary.
Avoid giving the model broad access (like full shell access) if a more
restricted set of tools will suffice.
If you must use powerful tools like `run_shell_command`, consider restricting
them to specific commands in your `gemini-extension.json`:
```json
{
"name": "my-safe-extension",
"excludeTools": ["run_shell_command(rm -rf *)"]
}
```
This ensures that even if the model tries to execute a dangerous command, it
will be blocked at the CLI level.
### Validate inputs
Your MCP server is running on the user's machine. Always validate inputs to your
tools to prevent arbitrary code execution or filesystem access outside the
intended scope.
```typescript
// Good: Validating paths
if (!path.resolve(inputPath).startsWith(path.resolve(allowedDir) + path.sep)) {
throw new Error('Access denied');
}
```
### Sensitive settings
If your extension requires API keys, use the `sensitive: true` option in
`gemini-extension.json`. This ensures keys are stored securely in the system
keychain and obfuscated in the UI.
```json
"settings": [
{
"name": "API Key",
"envVar": "MY_API_KEY",
"sensitive": true
}
]
```
## Releasing
You can upload your extension directly to GitHub to list it in the gallery.
Gemini CLI extensions also offers support for more complicated
[releases](releasing.md).
### Semantic versioning
Follow [Semantic Versioning](https://semver.org/).
- **Major**: Breaking changes (renaming tools, changing arguments).
- **Minor**: New features (new tools, commands).
- **Patch**: Bug fixes.
### Release Channels
Use git branches to manage release channels (e.g., `main` for stable, `dev` for
bleeding edge). This allows users to choose their stability level:
```bash
# Stable
gemini extensions install github.com/user/repo
# Dev
gemini extensions install github.com/user/repo --ref dev
```
### Clean artifacts
If you are using GitHub Releases, ensure your release artifacts only contain the
necessary files (`dist/`, `gemini-extension.json`, `package.json`). Exclude
`node_modules` (users will install them) and `src/` to keep downloads small.
@@ -8,19 +8,7 @@ file.
## Prerequisites
Before you start, make sure you have the Gemini CLI installed and a basic
understanding of Node.js.
## When to use what
Extensions offer a variety of ways to customize Gemini CLI.
| Feature | What it is | When to use it | Invoked by |
| :------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------- |
| **[MCP server](reference.md#mcp-servers)** | A standard way to expose new tools and data sources to the model. | Use this when you want the model to be able to _do_ new things, like fetching data from an internal API, querying a database, or controlling a local application. We also support MCP resources (which can replace custom commands) and system instructions (which can replace custom context) | Model |
| **[Custom commands](../cli/custom-commands.md)** | A shortcut (like `/my-cmd`) that executes a pre-defined prompt or shell command. | Use this for repetitive tasks or to save long, complex prompts that you use frequently. Great for automation. | User |
| **[Context file (`GEMINI.md`)](reference.md#contextfilename)** | A markdown file containing instructions that are loaded into the model's context at the start of every session. | Use this to define the "personality" of your extension, set coding standards, or provide essential knowledge that the model should always have. | CLI provides to model |
| **[Agent skills](../cli/skills.md)** | A specialized set of instructions and workflows that the model activates only when needed. | Use this for complex, occasional tasks (like "create a PR" or "audit security") to avoid cluttering the main context window when the skill isn't being used. | Model |
| **[Hooks](../hooks/index.md)** | A way to intercept and customize the CLI's behavior at specific lifecycle events (e.g., before/after a tool call). | Use this when you want to automate actions based on what the model is doing, like validating tool arguments, logging activity, or modifying the model's input/output. | CLI |
understanding of Node.js and TypeScript.
## Step 1: Create a new extension
@@ -38,9 +26,10 @@ This will create a new directory with the following structure:
```
my-first-extension/
├── example.js
├── example.ts
├── gemini-extension.json
── package.json
── package.json
└── tsconfig.json
```
## Step 2: Understand the extension files
@@ -54,12 +43,12 @@ and use your extension.
```json
{
"name": "mcp-server-example",
"name": "my-first-extension",
"version": "1.0.0",
"mcpServers": {
"nodeServer": {
"command": "node",
"args": ["${extensionPath}${/}example.js"],
"args": ["${extensionPath}${/}dist${/}example.js"],
"cwd": "${extensionPath}"
}
}
@@ -75,12 +64,12 @@ and use your extension.
with the absolute path to your extension's installation directory. This
allows your extension to work regardless of where it's installed.
### `example.js`
### `example.ts`
This file contains the source code for your MCP server. It's a simple Node.js
server that uses the `@modelcontextprotocol/sdk`.
```javascript
```typescript
/**
* @license
* Copyright 2025 Google LLC
@@ -129,15 +118,16 @@ await server.connect(transport);
This server defines a single tool called `fetch_posts` that fetches data from a
public API.
### `package.json`
### `package.json` and `tsconfig.json`
This is the standard configuration file for a Node.js project. It defines
dependencies and scripts.
These are standard configuration files for a TypeScript project. The
`package.json` file defines dependencies and a `build` script, and
`tsconfig.json` configures the TypeScript compiler.
## Step 3: Link your extension
## Step 3: Build and link your extension
Before you can use the extension, you need to link it to your Gemini CLI
installation for local development.
Before you can use the extension, you need to compile the TypeScript code and
link the extension to your Gemini CLI installation for local development.
1. **Install dependencies:**
@@ -146,7 +136,16 @@ installation for local development.
npm install
```
2. **Link the extension:**
2. **Build the server:**
```bash
npm run build
```
This will compile `example.ts` into `dist/example.js`, which is the file
referenced in your `gemini-extension.json`.
3. **Link the extension:**
The `link` command creates a symbolic link from the Gemini CLI extensions
directory to your development directory. This means any changes you make
@@ -213,7 +212,7 @@ need this for extensions built to expose commands and prompts.
"mcpServers": {
"nodeServer": {
"command": "node",
"args": ["${extensionPath}${/}example.js"],
"args": ["${extensionPath}${/}dist${/}example.js"],
"cwd": "${extensionPath}"
}
}
@@ -223,50 +222,14 @@ need this for extensions built to expose commands and prompts.
Restart the CLI again. The model will now have the context from your `GEMINI.md`
file in every session where the extension is active.
## (Optional) Step 6: Add an Agent Skill
## Step 6: Releasing your extension
_Note: This is an experimental feature enabled via `experimental.skills`._
[Agent Skills](../cli/skills.md) let you bundle specialized expertise and
procedural workflows. Unlike `GEMINI.md`, which provides persistent context,
skills are activated only when needed, saving context tokens.
1. Create a `skills` directory and a subdirectory for your skill:
```bash
mkdir -p skills/security-audit
```
2. Create a `skills/security-audit/SKILL.md` file:
```markdown
---
name: security-audit
description:
Expertise in auditing code for security vulnerabilities. Use when the user
asks to "check for security issues" or "audit" their changes.
---
# Security Auditor
You are an expert security researcher. When auditing code:
1. Look for common vulnerabilities (OWASP Top 10).
2. Check for hardcoded secrets or API keys.
3. Suggest remediation steps for any findings.
```
Skills bundled with your extension are automatically discovered and can be
activated by the model during a session when it identifies a relevant task.
## Step 7: Release your extension
Once you're happy with your extension, you can share it with others. The two
Once you are happy with your extension, you can share it with others. The two
primary ways of releasing extensions are via a Git repository or through GitHub
Releases. Using a public Git repository is the simplest method.
For detailed instructions on both methods, please refer to the
[Extension Releasing Guide](./releasing.md).
[Extension Releasing Guide](./extension-releasing.md).
## Conclusion
@@ -276,7 +239,6 @@ You've successfully created a Gemini CLI extension! You learned how to:
- Add custom tools with an MCP server.
- Create convenient custom commands.
- Provide persistent context to the model.
- Bundle specialized Agent Skills.
- Link your extension for local development.
From here, you can explore more advanced features and build powerful new
+260 -26
View File
@@ -1,44 +1,278 @@
# Gemini CLI extensions
Gemini CLI extensions package prompts, MCP servers, custom commands, hooks, and
agent skills into a familiar and user-friendly format. With extensions, you can
expand the capabilities of Gemini CLI and share those capabilities with others.
They are designed to be easily installable and shareable.
_This documentation is up-to-date with the v0.4.0 release._
Gemini CLI extensions package prompts, MCP servers, and custom commands into a
familiar and user-friendly format. With extensions, you can expand the
capabilities of Gemini CLI and share those capabilities with others. They are
designed to be easily installable and shareable.
To see examples of extensions, you can browse a gallery of
[Gemini CLI extensions](https://geminicli.com/extensions/browse/).
## Managing extensions
See [getting started docs](getting-started-extensions.md) for a guide on
creating your first extension.
You can verify your installed extensions and their status using the interactive
command:
See [releasing docs](extension-releasing.md) for an advanced guide on setting up
GitHub releases.
```bash
/extensions list
## Extension management
We offer a suite of extension management tools using `gemini extensions`
commands.
Note that these commands are not supported from within the CLI, although you can
list installed extensions using the `/extensions list` subcommand.
Note that all of these commands will only be reflected in active CLI sessions on
restart.
### Installing an extension
You can install an extension using `gemini extensions install` with either a
GitHub URL or a local path.
Note that we create a copy of the installed extension, so you will need to run
`gemini extensions update` to pull in changes from both locally-defined
extensions and those on GitHub.
NOTE: If you are installing an extension from GitHub, you'll need to have `git`
installed on your machine. See
[git installation instructions](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
for help.
```
gemini extensions install <source> [--ref <ref>] [--auto-update] [--pre-release] [--consent]
```
or in noninteractive mode:
- `<source>`: The github URL or local path of the extension to install.
- `--ref`: The git ref to install from.
- `--auto-update`: Enable auto-update for this extension.
- `--pre-release`: Enable pre-release versions for this extension.
- `--consent`: Acknowledge the security risks of installing an extension and
skip the confirmation prompt.
```bash
gemini extensions list
### Uninstalling an extension
To uninstall one or more extensions, run
`gemini extensions uninstall <name...>`:
```
gemini extensions uninstall gemini-cli-security gemini-cli-another-extension
```
## Installation
### Disabling an extension
To install a real extension, you can use the `extensions install` command with a
GitHub repository URL in noninteractive mode. For example:
Extensions are, by default, enabled across all workspaces. You can disable an
extension entirely or for specific workspace.
```bash
gemini extensions install https://github.com/gemini-cli-extensions/workspace
```
gemini extensions disable <name> [--scope <scope>]
```
## Next steps
- `<name>`: The name of the extension to disable.
- `--scope`: The scope to disable the extension in (`user` or `workspace`).
- [Writing extensions](writing-extensions.md): Learn how to create your first
extension.
- [Extensions reference](reference.md): Deeply understand the extension format,
commands, and configuration.
- [Best practices](best-practices.md): Learn strategies for building great
extensions.
- [Extensions releasing](releasing.md): Learn how to share your extensions with
the world.
### Enabling an extension
You can enable extensions using `gemini extensions enable <name>`. You can also
enable an extension for a specific workspace using
`gemini extensions enable <name> --scope=workspace` from within that workspace.
```
gemini extensions enable <name> [--scope <scope>]
```
- `<name>`: The name of the extension to enable.
- `--scope`: The scope to enable the extension in (`user` or `workspace`).
### Updating an extension
For extensions installed from a local path or a git repository, you can
explicitly update to the latest version (as reflected in the
`gemini-extension.json` `version` field) with `gemini extensions update <name>`.
You can update all extensions with:
```
gemini extensions update --all
```
### Create a boilerplate extension
We offer several example extensions `context`, `custom-commands`,
`exclude-tools` and `mcp-server`. You can view these examples
[here](https://github.com/google-gemini/gemini-cli/tree/main/packages/cli/src/commands/extensions/examples).
To copy one of these examples into a development directory using the type of
your choosing, run:
```
gemini extensions new <path> [template]
```
- `<path>`: The path to create the extension in.
- `[template]`: The boilerplate template to use.
### Link a local extension
The `gemini extensions link` command will create a symbolic link from the
extension installation directory to the development path.
This is useful so you don't have to run `gemini extensions update` every time
you make changes you'd like to test.
```
gemini extensions link <path>
```
- `<path>`: The path of the extension to link.
## How it works
On startup, Gemini CLI looks for extensions in `<home>/.gemini/extensions`
Extensions exist as a directory that contains a `gemini-extension.json` file.
For example:
`<home>/.gemini/extensions/my-extension/gemini-extension.json`
### `gemini-extension.json`
The `gemini-extension.json` file contains the configuration for the extension.
The file has the following structure:
```json
{
"name": "my-extension",
"version": "1.0.0",
"mcpServers": {
"my-server": {
"command": "node my-server.js"
}
},
"contextFileName": "GEMINI.md",
"excludeTools": ["run_shell_command"]
}
```
- `name`: The name of the extension. This is used to uniquely identify the
extension and for conflict resolution when extension commands have the same
name as user or project commands. The name should be lowercase or numbers and
use dashes instead of underscores or spaces. This is how users will refer to
your extension in the CLI. Note that we expect this name to match the
extension directory name.
- `version`: The version of the extension.
- `mcpServers`: A map of MCP servers to configure. The key is the name of the
server, and the value is the server configuration. These servers will be
loaded on startup just like MCP servers configured in a
[`settings.json` file](../get-started/configuration.md). If both an extension
and a `settings.json` file configure an MCP server with the same name, the
server defined in the `settings.json` file takes precedence.
- Note that all MCP server configuration options are supported except for
`trust`.
- `contextFileName`: The name of the file that contains the context for the
extension. This will be used to load the context from the extension directory.
If this property is not used but a `GEMINI.md` file is present in your
extension directory, then that file will be loaded.
- `excludeTools`: An array of tool names to exclude from the model. You can also
specify command-specific restrictions for tools that support it, like the
`run_shell_command` tool. For example,
`"excludeTools": ["run_shell_command(rm -rf)"]` will block the `rm -rf`
command. Note that this differs from the MCP server `excludeTools`
functionality, which can be listed in the MCP server config.
When Gemini CLI starts, it loads all the extensions and merges their
configurations. If there are any conflicts, the workspace configuration takes
precedence.
### Settings
_Note: This is an experimental feature. We do not yet recommend extension
authors introduce settings as part of their core flows._
Extensions can define settings that the user will be prompted to provide upon
installation. This is useful for things like API keys, URLs, or other
configuration that the extension needs to function.
To define settings, add a `settings` array to your `gemini-extension.json` file.
Each object in the array should have the following properties:
- `name`: A user-friendly name for the setting.
- `description`: A description of the setting and what it's used for.
- `envVar`: The name of the environment variable that the setting will be stored
as.
- `sensitive`: Optional boolean. If true, obfuscates the input the user provides
and stores the secret in keychain storage. **Example**
```json
{
"name": "my-api-extension",
"version": "1.0.0",
"settings": [
{
"name": "API Key",
"description": "Your API key for the service.",
"envVar": "MY_API_KEY"
}
]
}
```
When a user installs this extension, they will be prompted to enter their API
key. The value will be saved to a `.env` file in the extension's directory
(e.g., `<home>/.gemini/extensions/my-api-extension/.env`).
### Custom commands
Extensions can provide [custom commands](../cli/custom-commands.md) by placing
TOML files in a `commands/` subdirectory within the extension directory. These
commands follow the same format as user and project custom commands and use
standard naming conventions.
**Example**
An extension named `gcp` with the following structure:
```
.gemini/extensions/gcp/
├── gemini-extension.json
└── commands/
├── deploy.toml
└── gcs/
└── sync.toml
```
Would provide these commands:
- `/deploy` - Shows as `[gcp] Custom command from deploy.toml` in help
- `/gcs:sync` - Shows as `[gcp] Custom command from sync.toml` in help
### Conflict resolution
Extension commands have the lowest precedence. When a conflict occurs with user
or project commands:
1. **No conflict**: Extension command uses its natural name (e.g., `/deploy`)
2. **With conflict**: Extension command is renamed with the extension prefix
(e.g., `/gcp.deploy`)
For example, if both a user and the `gcp` extension define a `deploy` command:
- `/deploy` - Executes the user's deploy command
- `/gcp.deploy` - Executes the extension's deploy command (marked with `[gcp]`
tag)
## Variables
Gemini CLI extensions allow variable substitution in `gemini-extension.json`.
This can be useful if e.g., you need the current directory to run an MCP server
using `"cwd": "${extensionPath}${/}run.ts"`.
**Supported variables:**
| variable | description |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `${extensionPath}` | The fully-qualified path of the extension in the user's filesystem e.g., '/Users/username/.gemini/extensions/example-extension'. This will not unwrap symlinks. |
| `${workspacePath}` | The fully-qualified path of the current workspace. |
| `${/} or ${pathSeparator}` | The path separator (differs per OS). |
-312
View File
@@ -1,312 +0,0 @@
# Extensions reference
This guide covers the `gemini extensions` commands and the structure of the
`gemini-extension.json` configuration file.
## Extension management
We offer a suite of extension management tools using `gemini extensions`
commands.
Note that these commands (e.g. `gemini extensions install`) are not supported
from within the CLI's **interactive mode**, although you can list installed
extensions using the `/extensions list` slash command.
Note that all of these management operations (including updates to slash
commands) will only be reflected in active CLI sessions on **restart**.
### Installing an extension
You can install an extension using `gemini extensions install` with either a
GitHub URL or a local path.
Note that we create a copy of the installed extension, so you will need to run
`gemini extensions update` to pull in changes from both locally-defined
extensions and those on GitHub.
NOTE: If you are installing an extension from GitHub, you'll need to have `git`
installed on your machine. See
[git installation instructions](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
for help.
```
gemini extensions install <source> [--ref <ref>] [--auto-update] [--pre-release] [--consent]
```
- `<source>`: The github URL or local path of the extension to install.
- `--ref`: The git ref to install from.
- `--auto-update`: Enable auto-update for this extension.
- `--pre-release`: Enable pre-release versions for this extension.
- `--consent`: Acknowledge the security risks of installing an extension and
skip the confirmation prompt.
### Uninstalling an extension
To uninstall one or more extensions, run
`gemini extensions uninstall <name...>`:
```
gemini extensions uninstall gemini-cli-security gemini-cli-another-extension
```
### Disabling an extension
Extensions are, by default, enabled across all workspaces. You can disable an
extension entirely or for specific workspace.
```
gemini extensions disable <name> [--scope <scope>]
```
- `<name>`: The name of the extension to disable.
- `--scope`: The scope to disable the extension in (`user` or `workspace`).
### Enabling an extension
You can enable extensions using `gemini extensions enable <name>`. You can also
enable an extension for a specific workspace using
`gemini extensions enable <name> --scope=workspace` from within that workspace.
```
gemini extensions enable <name> [--scope <scope>]
```
- `<name>`: The name of the extension to enable.
- `--scope`: The scope to enable the extension in (`user` or `workspace`).
### Updating an extension
For extensions installed from a local path or a git repository, you can
explicitly update to the latest version (as reflected in the
`gemini-extension.json` `version` field) with `gemini extensions update <name>`.
You can update all extensions with:
```
gemini extensions update --all
```
### Create a boilerplate extension
We offer several example extensions `context`, `custom-commands`,
`exclude-tools` and `mcp-server`. You can view these examples
[here](https://github.com/google-gemini/gemini-cli/tree/main/packages/cli/src/commands/extensions/examples).
To copy one of these examples into a development directory using the type of
your choosing, run:
```
gemini extensions new <path> [template]
```
- `<path>`: The path to create the extension in.
- `[template]`: The boilerplate template to use.
### Link a local extension
The `gemini extensions link` command will create a symbolic link from the
extension installation directory to the development path.
This is useful so you don't have to run `gemini extensions update` every time
you make changes you'd like to test.
```
gemini extensions link <path>
```
- `<path>`: The path of the extension to link.
## Extension format
On startup, Gemini CLI looks for extensions in `<home>/.gemini/extensions`
Extensions exist as a directory that contains a `gemini-extension.json` file.
For example:
`<home>/.gemini/extensions/my-extension/gemini-extension.json`
### `gemini-extension.json`
The `gemini-extension.json` file contains the configuration for the extension.
The file has the following structure:
```json
{
"name": "my-extension",
"version": "1.0.0",
"description": "My awesome extension",
"mcpServers": {
"my-server": {
"command": "node my-server.js"
}
},
"contextFileName": "GEMINI.md",
"excludeTools": ["run_shell_command"]
}
```
- `name`: The name of the extension. This is used to uniquely identify the
extension and for conflict resolution when extension commands have the same
name as user or project commands. The name should be lowercase or numbers and
use dashes instead of underscores or spaces. This is how users will refer to
your extension in the CLI. Note that we expect this name to match the
extension directory name.
- `version`: The version of the extension.
- `description`: A short description of the extension. This will be displayed on
[geminicli.com/extensions](https://geminicli.com/extensions).
- `mcpServers`: A map of MCP servers to settings. The key is the name of the
server, and the value is the server configuration. These servers will be
loaded on startup just like MCP servers settingsd in a
[`settings.json` file](../get-started/configuration.md). If both an extension
and a `settings.json` file settings an MCP server with the same name, the
server defined in the `settings.json` file takes precedence.
- Note that all MCP server configuration options are supported except for
`trust`.
- `contextFileName`: The name of the file that contains the context for the
extension. This will be used to load the context from the extension directory.
If this property is not used but a `GEMINI.md` file is present in your
extension directory, then that file will be loaded.
- `excludeTools`: An array of tool names to exclude from the model. You can also
specify command-specific restrictions for tools that support it, like the
`run_shell_command` tool. For example,
`"excludeTools": ["run_shell_command(rm -rf)"]` will block the `rm -rf`
command. Note that this differs from the MCP server `excludeTools`
functionality, which can be listed in the MCP server config.
When Gemini CLI starts, it loads all the extensions and merges their
configurations. If there are any conflicts, the workspace configuration takes
precedence.
### Settings
_Note: This is an experimental feature. We do not yet recommend extension
authors introduce settings as part of their core flows._
Extensions can define settings that the user will be prompted to provide upon
installation. This is useful for things like API keys, URLs, or other
configuration that the extension needs to function.
To define settings, add a `settings` array to your `gemini-extension.json` file.
Each object in the array should have the following properties:
- `name`: A user-friendly name for the setting.
- `description`: A description of the setting and what it's used for.
- `envVar`: The name of the environment variable that the setting will be stored
as.
- `sensitive`: Optional boolean. If true, obfuscates the input the user provides
and stores the secret in keychain storage. **Example**
```json
{
"name": "my-api-extension",
"version": "1.0.0",
"settings": [
{
"name": "API Key",
"description": "Your API key for the service.",
"envVar": "MY_API_KEY"
}
]
}
```
When a user installs this extension, they will be prompted to enter their API
key. The value will be saved to a `.env` file in the extension's directory
(e.g., `<home>/.gemini/extensions/my-api-extension/.env`).
You can view a list of an extension's settings by running:
```
gemini extensions list
```
and you can update a given setting using:
```
gemini extensions config <extension name> [setting name] [--scope <scope>]
```
- `--scope`: The scope to set the setting in (`user` or `workspace`). This is
optional and will default to `user`.
### Custom commands
Extensions can provide [custom commands](../cli/custom-commands.md) by placing
TOML files in a `commands/` subdirectory within the extension directory. These
commands follow the same format as user and project custom commands and use
standard naming conventions.
**Example**
An extension named `gcp` with the following structure:
```
.gemini/extensions/gcp/
├── gemini-extension.json
└── commands/
├── deploy.toml
└── gcs/
└── sync.toml
```
Would provide these commands:
- `/deploy` - Shows as `[gcp] Custom command from deploy.toml` in help
- `/gcs:sync` - Shows as `[gcp] Custom command from sync.toml` in help
### Hooks
Extensions can provide [hooks](../hooks/index.md) to intercept and customize
Gemini CLI behavior at specific lifecycle events. Hooks provided by an extension
must be defined in a `hooks/hooks.json` file within the extension directory.
> [!IMPORTANT] Hooks are not defined directly in `gemini-extension.json`. The
> CLI specifically looks for the `hooks/hooks.json` file.
### Agent Skills
Extensions can bundle [Agent Skills](../cli/skills.md) to provide specialized
workflows. Skills must be placed in a `skills/` directory within the extension.
**Example**
An extension with the following structure:
```
.gemini/extensions/my-extension/
├── gemini-extension.json
└── skills/
└── security-audit/
└── SKILL.md
```
Will expose a `security-audit` skill that the model can activate.
### Conflict resolution
Extension commands have the lowest precedence. When a conflict occurs with user
or project commands:
1. **No conflict**: Extension command uses its natural name (e.g., `/deploy`)
2. **With conflict**: Extension command is renamed with the extension prefix
(e.g., `/gcp.deploy`)
For example, if both a user and the `gcp` extension define a `deploy` command:
- `/deploy` - Executes the user's deploy command
- `/gcp.deploy` - Executes the extension's deploy command (marked with `[gcp]`
tag)
## Variables
Gemini CLI extensions allow variable substitution in `gemini-extension.json`.
This can be useful if e.g., you need the current directory to run an MCP server
using an argument like `"args": ["${extensionPath}${/}dist${/}server.js"]`.
**Supported variables:**
| variable | description |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `${extensionPath}` | The fully-qualified path of the extension in the user's filesystem e.g., '/Users/username/.gemini/extensions/example-extension'. This will not unwrap symlinks. |
| `${workspacePath}` | The fully-qualified path of the current workspace. |
| `${/} or ${pathSeparator}` | The path separator (differs per OS). |
+74 -291
View File
@@ -110,13 +110,13 @@ their corresponding top-level category object in your `settings.json` file.
- **Description:** Enable Vim keybindings
- **Default:** `false`
- **`general.enableAutoUpdate`** (boolean):
- **Description:** Enable automatic updates.
- **Default:** `true`
- **`general.disableAutoUpdate`** (boolean):
- **Description:** Disable automatic updates
- **Default:** `false`
- **`general.enableAutoUpdateNotification`** (boolean):
- **Description:** Enable update notification prompts.
- **Default:** `true`
- **`general.disableUpdateNag`** (boolean):
- **Description:** Disable update notification prompts.
- **Default:** `false`
- **`general.checkpointing.enabled`** (boolean):
- **Description:** Enable session checkpointing for recovery
@@ -159,7 +159,7 @@ their corresponding top-level category object in your `settings.json` file.
#### `output`
- **`output.format`** (enum):
- **Description:** The format of the CLI output. Can be `text` or `json`.
- **Description:** The format of the CLI output.
- **Default:** `"text"`
- **Values:** `"text"`, `"json"`
@@ -180,21 +180,10 @@ their corresponding top-level category object in your `settings.json` file.
- **Requires restart:** Yes
- **`ui.showStatusInTitle`** (boolean):
- **Description:** Show Gemini CLI model thoughts in the terminal window title
during the working phase
- **Description:** Show Gemini CLI status and thoughts in the terminal window
title
- **Default:** `false`
- **`ui.dynamicWindowTitle`** (boolean):
- **Description:** Update the terminal window title with current status icons
(Ready: ◇, Action Required: ✋, Working: ✦)
- **Default:** `true`
- **`ui.showHomeDirectoryWarning`** (boolean):
- **Description:** Show a warning when running Gemini CLI in the home
directory.
- **Default:** `true`
- **Requires restart:** Yes
- **`ui.hideTips`** (boolean):
- **Description:** Hide helpful tips in the UI
- **Default:** `false`
@@ -244,16 +233,16 @@ their corresponding top-level category object in your `settings.json` file.
- **Description:** Show the model name in the chat for each model turn.
- **Default:** `false`
- **`ui.useFullWidth`** (boolean):
- **Description:** Use the entire width of the terminal for output.
- **Default:** `true`
- **`ui.useAlternateBuffer`** (boolean):
- **Description:** Use an alternate screen buffer for the UI, preserving shell
history.
- **Default:** `false`
- **Requires restart:** Yes
- **`ui.useBackgroundColor`** (boolean):
- **Description:** Whether to use background colors in the UI.
- **Default:** `true`
- **`ui.incrementalRendering`** (boolean):
- **Description:** Enable incremental rendering for the UI. This option will
reduce flickering but may cause rendering artifacts. Only supported when
@@ -261,18 +250,14 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `true`
- **Requires restart:** Yes
- **`ui.showSpinner`** (boolean):
- **Description:** Show the spinner during operations.
- **Default:** `true`
- **`ui.customWittyPhrases`** (array):
- **Description:** Custom witty phrases to display during loading. When
provided, the CLI cycles through these instead of the defaults.
- **Default:** `[]`
- **`ui.accessibility.enableLoadingPhrases`** (boolean):
- **Description:** Enable loading phrases during operations.
- **Default:** `true`
- **`ui.accessibility.disableLoadingPhrases`** (boolean):
- **Description:** Disable loading phrases for accessibility
- **Default:** `false`
- **Requires restart:** Yes
- **`ui.accessibility.screenReader`** (boolean):
@@ -284,7 +269,7 @@ their corresponding top-level category object in your `settings.json` file.
#### `ide`
- **`ide.enabled`** (boolean):
- **Description:** Enable IDE integration mode.
- **Description:** Enable IDE integration mode
- **Default:** `false`
- **Requires restart:** Yes
@@ -382,12 +367,6 @@ their corresponding top-level category object in your `settings.json` file.
"model": "gemini-3-pro-preview"
}
},
"gemini-3-flash-preview": {
"extends": "chat-base-3",
"modelConfig": {
"model": "gemini-3-flash-preview"
}
},
"gemini-2.5-pro": {
"extends": "chat-base-2.5",
"modelConfig": {
@@ -517,11 +496,6 @@ their corresponding top-level category object in your `settings.json` file.
"model": "gemini-3-pro-preview"
}
},
"chat-compression-3-flash": {
"modelConfig": {
"model": "gemini-3-flash-preview"
}
},
"chat-compression-2.5-pro": {
"modelConfig": {
"model": "gemini-2.5-pro"
@@ -550,25 +524,12 @@ their corresponding top-level category object in your `settings.json` file.
with (and override) the built-in aliases.
- **Default:** `{}`
- **`modelConfigs.customOverrides`** (array):
- **Description:** Custom model config overrides. These are merged with (and
added to) the built-in overrides.
- **Default:** `[]`
- **`modelConfigs.overrides`** (array):
- **Description:** Apply specific configuration overrides based on matches,
with a primary key of model (or alias). The most specific match will be
used.
- **Default:** `[]`
#### `agents`
- **`agents.overrides`** (object):
- **Description:** Override settings for specific agents, e.g. to disable the
agent, set a custom model config, or run config.
- **Default:** `{}`
- **Requires restart:** Yes
#### `context`
- **`context.fileName`** (string | string[]):
@@ -596,12 +557,12 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `false`
- **`context.fileFiltering.respectGitIgnore`** (boolean):
- **Description:** Respect .gitignore files when searching.
- **Description:** Respect .gitignore files when searching
- **Default:** `true`
- **Requires restart:** Yes
- **`context.fileFiltering.respectGeminiIgnore`** (boolean):
- **Description:** Respect .geminiignore files when searching.
- **Description:** Respect .geminiignore files when searching
- **Default:** `true`
- **Requires restart:** Yes
@@ -611,9 +572,9 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `true`
- **Requires restart:** Yes
- **`context.fileFiltering.enableFuzzySearch`** (boolean):
- **Description:** Enable fuzzy search when searching for files.
- **Default:** `true`
- **`context.fileFiltering.disableFuzzySearch`** (boolean):
- **Description:** Disable fuzzy search when searching for files.
- **Default:** `false`
- **Requires restart:** Yes
#### `tools`
@@ -644,23 +605,11 @@ their corresponding top-level category object in your `settings.json` file.
shell command. Defaults to 5 minutes.
- **Default:** `300`
- **`tools.shell.enableShellOutputEfficiency`** (boolean):
- **Description:** Enable shell output efficiency optimizations for better
performance.
- **Default:** `true`
- **`tools.autoAccept`** (boolean):
- **Description:** Automatically accept and execute tool calls that are
considered safe (e.g., read-only operations).
- **Default:** `false`
- **`tools.approvalMode`** (enum):
- **Description:** The default approval mode for tool execution. 'default'
prompts for approval, 'auto_edit' auto-approves edit tools, and 'plan' is
read-only mode. 'yolo' is not supported yet.
- **Default:** `"default"`
- **Values:** `"default"`, `"auto_edit"`, `"plan"`
- **`tools.core`** (array):
- **Description:** Restrict the set of built-in tools with an allowlist. Match
semantics mirror tools.allowed; see the built-in tools documentation for
@@ -714,17 +663,20 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `1000`
- **Requires restart:** Yes
- **`tools.disableLLMCorrection`** (boolean):
- **Description:** Disable LLM-based error correction for edit tools. When
enabled, tools will fail immediately if exact string matches are not found,
instead of attempting to self-correct.
- **Default:** `true`
- **`tools.enableMessageBusIntegration`** (boolean):
- **Description:** Enable policy-based tool confirmation via message bus
integration. When enabled, tools automatically respect policy engine
decisions (ALLOW/DENY/ASK_USER) without requiring individual tool
implementations.
- **Default:** `false`
- **Requires restart:** Yes
- **`tools.enableHooks`** (boolean):
- **Description:** Enables the hooks system experiment. When disabled, the
hooks system is completely deactivated regardless of other settings.
- **Default:** `true`
- **Description:** Enable the hooks system for intercepting and customizing
Gemini CLI behavior. When enabled, hooks configured in settings will execute
at appropriate lifecycle events (BeforeTool, AfterTool, BeforeModel, etc.).
Requires MessageBus integration.
- **Default:** `false`
- **Requires restart:** Yes
#### `mcp`
@@ -744,6 +696,12 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `undefined`
- **Requires restart:** Yes
#### `useSmartEdit`
- **`useSmartEdit`** (boolean):
- **Description:** Enable the smart-edit tool instead of the replace tool.
- **Default:** `true`
#### `useWriteTodos`
- **`useWriteTodos`** (boolean):
@@ -757,11 +715,6 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `false`
- **Requires restart:** Yes
- **`security.enablePermanentToolApproval`** (boolean):
- **Description:** Enable the "Allow for all future sessions" option in tool
confirmation dialogs.
- **Default:** `false`
- **`security.blockGitExtensions`** (boolean):
- **Description:** Blocks installing and loading extensions from Git.
- **Default:** `false`
@@ -772,22 +725,6 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `false`
- **Requires restart:** Yes
- **`security.environmentVariableRedaction.allowed`** (array):
- **Description:** Environment variables to always allow (bypass redaction).
- **Default:** `[]`
- **Requires restart:** Yes
- **`security.environmentVariableRedaction.blocked`** (array):
- **Description:** Environment variables to always redact.
- **Default:** `[]`
- **Requires restart:** Yes
- **`security.environmentVariableRedaction.enabled`** (boolean):
- **Description:** Enable redaction of environment variables that may contain
secrets.
- **Default:** `false`
- **Requires restart:** Yes
- **`security.auth.selectedType`** (string):
- **Description:** The currently selected authentication type.
- **Default:** `undefined`
@@ -830,150 +767,59 @@ their corresponding top-level category object in your `settings.json` file.
#### `experimental`
- **`experimental.enableAgents`** (boolean):
- **Description:** Enable local and remote subagents. Warning: Experimental
feature, uses YOLO mode for subagents
- **Default:** `false`
- **Requires restart:** Yes
- **`experimental.extensionManagement`** (boolean):
- **Description:** Enable extension management features.
- **Default:** `true`
- **Requires restart:** Yes
- **`experimental.extensionConfig`** (boolean):
- **Description:** Enable requesting and fetching of extension settings.
- **Default:** `false`
- **Requires restart:** Yes
- **`experimental.enableEventDrivenScheduler`** (boolean):
- **Description:** Enables event-driven scheduler within the CLI session.
- **Default:** `true`
- **Requires restart:** Yes
- **`experimental.extensionReloading`** (boolean):
- **Description:** Enables extension loading/unloading within the CLI session.
- **Default:** `false`
- **Requires restart:** Yes
- **`experimental.isModelAvailabilityServiceEnabled`** (boolean):
- **Description:** Enable model routing using new availability service.
- **Default:** `false`
- **Requires restart:** Yes
- **`experimental.jitContext`** (boolean):
- **Description:** Enable Just-In-Time (JIT) context loading.
- **Default:** `false`
- **Requires restart:** Yes
- **`experimental.skills`** (boolean):
- **Description:** Enable Agent Skills (experimental).
- **Default:** `false`
- **Requires restart:** Yes
- **`experimental.useOSC52Paste`** (boolean):
- **Description:** Use OSC 52 sequence for pasting instead of clipboardy
(useful for remote sessions).
- **Default:** `false`
- **`experimental.plan`** (boolean):
- **Description:** Enable planning features (Plan Mode and tools).
- **Default:** `false`
- **Requires restart:** Yes
#### `skills`
- **`skills.disabled`** (array):
- **Description:** List of disabled skills.
- **Default:** `[]`
- **Requires restart:** Yes
#### `hooksConfig`
- **`hooksConfig.enabled`** (boolean):
- **Description:** Canonical toggle for the hooks system. When disabled, no
hooks will be executed.
- **`experimental.codebaseInvestigatorSettings.enabled`** (boolean):
- **Description:** Enable the Codebase Investigator agent.
- **Default:** `true`
- **Requires restart:** Yes
- **`hooksConfig.disabled`** (array):
- **Description:** List of hook names (commands) that should be disabled.
Hooks in this list will not execute even if configured.
- **Default:** `[]`
- **`experimental.codebaseInvestigatorSettings.maxNumTurns`** (number):
- **Description:** Maximum number of turns for the Codebase Investigator
agent.
- **Default:** `10`
- **Requires restart:** Yes
- **`hooksConfig.notifications`** (boolean):
- **Description:** Show visual indicators when hooks are executing.
- **Default:** `true`
- **`experimental.codebaseInvestigatorSettings.maxTimeMinutes`** (number):
- **Description:** Maximum time for the Codebase Investigator agent (in
minutes).
- **Default:** `3`
- **Requires restart:** Yes
- **`experimental.codebaseInvestigatorSettings.thinkingBudget`** (number):
- **Description:** The thinking budget for the Codebase Investigator agent.
- **Default:** `8192`
- **Requires restart:** Yes
- **`experimental.codebaseInvestigatorSettings.model`** (string):
- **Description:** The model to use for the Codebase Investigator agent.
- **Default:** `"gemini-2.5-pro"`
- **Requires restart:** Yes
#### `hooks`
- **`hooks.BeforeTool`** (array):
- **Description:** Hooks that execute before tool execution. Can intercept,
validate, or modify tool calls.
- **Default:** `[]`
- **`hooks.AfterTool`** (array):
- **Description:** Hooks that execute after tool execution. Can process
results, log outputs, or trigger follow-up actions.
- **Default:** `[]`
- **`hooks.BeforeAgent`** (array):
- **Description:** Hooks that execute before agent loop starts. Can set up
context or initialize resources.
- **Default:** `[]`
- **`hooks.AfterAgent`** (array):
- **Description:** Hooks that execute after agent loop completes. Can perform
cleanup or summarize results.
- **Default:** `[]`
- **`hooks.Notification`** (array):
- **Description:** Hooks that execute on notification events (errors,
warnings, info). Can log or alert on specific conditions.
- **Default:** `[]`
- **`hooks.SessionStart`** (array):
- **Description:** Hooks that execute when a session starts. Can initialize
session-specific resources or state.
- **Default:** `[]`
- **`hooks.SessionEnd`** (array):
- **Description:** Hooks that execute when a session ends. Can perform cleanup
or persist session data.
- **Default:** `[]`
- **`hooks.PreCompress`** (array):
- **Description:** Hooks that execute before chat history compression. Can
back up or analyze conversation before compression.
- **Default:** `[]`
- **`hooks.BeforeModel`** (array):
- **Description:** Hooks that execute before LLM requests. Can modify prompts,
inject context, or control model parameters.
- **Default:** `[]`
- **`hooks.AfterModel`** (array):
- **Description:** Hooks that execute after LLM responses. Can process
outputs, extract information, or log interactions.
- **Default:** `[]`
- **`hooks.BeforeToolSelection`** (array):
- **Description:** Hooks that execute before tool selection. Can filter or
prioritize available tools dynamically.
- **Default:** `[]`
#### `admin`
- **`admin.secureModeEnabled`** (boolean):
- **Description:** If true, disallows yolo mode from being used.
- **Default:** `false`
- **`admin.extensions.enabled`** (boolean):
- **Description:** If false, disallows extensions from being installed or
used.
- **Default:** `true`
- **`admin.mcp.enabled`** (boolean):
- **Description:** If false, disallows MCP servers from being used.
- **Default:** `true`
- **`admin.skills.enabled`** (boolean):
- **Description:** If false, disallows agent skills from being used.
- **Default:** `true`
- **`hooks`** (object):
- **Description:** Hook configurations for intercepting and customizing agent
behavior.
- **Default:** `{}`
<!-- SETTINGS-AUTOGEN:END -->
#### `mcpServers`
@@ -1151,7 +997,7 @@ 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-2.5-flash"`
- **`GOOGLE_API_KEY`**:
- Your Google Cloud API key.
- Required for using Vertex AI in express mode.
@@ -1206,18 +1052,6 @@ the `advanced.excludedEnvVars` setting in your `settings.json` file.
- **`GEMINI_SANDBOX`**:
- Alternative to the `sandbox` setting in `settings.json`.
- Accepts `true`, `false`, `docker`, `podman`, or a custom command string.
- **`GEMINI_SYSTEM_MD`**:
- Replaces the builtin system prompt with content from a Markdown file.
- `true`/`1`: Use project default path `./.gemini/system.md`.
- Any other string: Treat as a path (relative/absolute supported, `~`
expands).
- `false`/`0` or unset: Use the builtin prompt. See
[System Prompt Override](../cli/system-prompt.md).
- **`GEMINI_WRITE_SYSTEM_MD`**:
- Writes the current builtin system prompt to a file for review.
- `true`/`1`: Write to `./.gemini/system.md`. Otherwise treat the value as a
path.
- Run the CLI once with this set to generate the file.
- **`SEATBELT_PROFILE`** (macOS specific):
- Switches the Seatbelt (`sandbox-exec`) profile on macOS.
- `permissive-open`: (Default) Restricts writes to the project folder (and a
@@ -1243,52 +1077,6 @@ the `advanced.excludedEnvVars` setting in your `settings.json` file.
- Specifies the endpoint for the code assist server.
- This is useful for development and testing.
### Environment variable redaction
To prevent accidental leakage of sensitive information, Gemini CLI automatically
redacts potential secrets from environment variables when executing tools (such
as shell commands). This "best effort" redaction applies to variables inherited
from the system or loaded from `.env` files.
**Default Redaction Rules:**
- **By Name:** Variables are redacted if their names contain sensitive terms
like `TOKEN`, `SECRET`, `PASSWORD`, `KEY`, `AUTH`, `CREDENTIAL`, `PRIVATE`, or
`CERT`.
- **By Value:** Variables are redacted if their values match known secret
patterns, such as:
- Private keys (RSA, OpenSSH, PGP, etc.)
- Certificates
- URLs containing credentials
- API keys and tokens (GitHub, Google, AWS, Stripe, Slack, etc.)
- **Specific Blocklist:** Certain variables like `CLIENT_ID`, `DB_URI`,
`DATABASE_URL`, and `CONNECTION_STRING` are always redacted by default.
**Allowlist (Never Redacted):**
- Common system variables (e.g., `PATH`, `HOME`, `USER`, `SHELL`, `TERM`,
`LANG`).
- Variables starting with `GEMINI_CLI_`.
- GitHub Action specific variables.
**Configuration:**
You can customize this behavior in your `settings.json` file:
- **`security.allowedEnvironmentVariables`**: A list of variable names to
_never_ redact, even if they match sensitive patterns.
- **`security.blockedEnvironmentVariables`**: A list of variable names to
_always_ redact, even if they don't match sensitive patterns.
```json
{
"security": {
"allowedEnvironmentVariables": ["MY_PUBLIC_KEY", "NOT_A_SECRET_TOKEN"],
"blockedEnvironmentVariables": ["INTERNAL_IP_ADDRESS"]
}
}
```
## Command-line arguments
Arguments passed directly when running the CLI can override other configurations
@@ -1296,7 +1084,7 @@ for that specific session.
- **`--model <model_name>`** (**`-m <model_name>`**):
- Specifies the Gemini model to use for this session.
- Example: `npm start -- --model gemini-3-pro-preview`
- Example: `npm start -- --model gemini-1.5-pro-latest`
- **`--prompt <your_prompt>`** (**`-p <your_prompt>`**):
- Used to pass a prompt directly to the command. This invokes Gemini CLI in a
non-interactive mode.
@@ -1319,8 +1107,7 @@ for that specific session.
- **`--sandbox`** (**`-s`**):
- Enables sandbox mode for this session.
- **`--debug`** (**`-d`**):
- Enables debug mode for this session, providing more verbose output. Open the
debug console with F12 to see the additional logging.
- Enables debug mode for this session, providing more verbose output.
- **`--help`** (or **`-h`**):
- Displays help information about command-line arguments.
@@ -1332,10 +1119,6 @@ for that specific session.
- `auto_edit`: Automatically approve edit tools (replace, write_file) while
prompting for others
- `yolo`: Automatically approve all tool calls (equivalent to `--yolo`)
- `plan`: Read-only mode for tool calls (requires experimental planning to
be enabled).
> **Note:** This mode is currently under development and not yet fully
> functional.
- Cannot be used together with `--yolo`. Use `--approval-mode=yolo` instead of
`--yolo` for the new unified approach.
- Example: `gemini --approval-mode auto_edit`
+143
View File
@@ -0,0 +1,143 @@
Note: This page will be replaced by [installation.md](installation.md).
# Gemini CLI installation, execution, and deployment
Install and run Gemini CLI. This document provides an overview of Gemini CLI's
installation methods and deployment architecture.
## How to install and/or run Gemini CLI
There are several ways to run Gemini CLI. The recommended option depends on how
you intend to use Gemini CLI.
- As a standard installation. This is the most straightforward method of using
Gemini CLI.
- In a sandbox. This method offers increased security and isolation.
- From the source. This is recommended for contributors to the project.
### 1. Standard installation (recommended for standard users)
This is the recommended way for end-users to install Gemini CLI. It involves
downloading the Gemini CLI package from the NPM registry.
- **Global install:**
```bash
npm install -g @google/gemini-cli
```
Then, run the CLI from anywhere:
```bash
gemini
```
- **NPX execution:**
```bash
# Execute the latest version from NPM without a global install
npx @google/gemini-cli
```
### 2. Run in a sandbox (Docker/Podman)
For security and isolation, Gemini CLI can be run inside a container. This is
the default way that the CLI executes tools that might have side effects.
- **Directly from the registry:** You can run the published sandbox image
directly. This is useful for environments where you only have Docker and want
to run the CLI.
```bash
# Run the published sandbox image
docker run --rm -it us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.1.1
```
- **Using the `--sandbox` flag:** If you have Gemini CLI installed locally
(using the standard installation described above), you can instruct it to run
inside the sandbox container.
```bash
gemini --sandbox -y -p "your prompt here"
```
### 3. Run from source (recommended for Gemini CLI contributors)
Contributors to the project will want to run the CLI directly from the source
code.
- **Development mode:** This method provides hot-reloading and is useful for
active development.
```bash
# From the root of the repository
npm run start
```
- **Production-like mode (Linked package):** This method simulates a global
installation by linking your local package. It's useful for testing a local
build in a production workflow.
```bash
# Link the local cli package to your global node_modules
npm link packages/cli
# Now you can run your local version using the `gemini` command
gemini
```
---
### 4. Running the latest Gemini CLI commit from GitHub
You can run the most recently committed version of Gemini CLI directly from the
GitHub repository. This is useful for testing features still in development.
```bash
# Execute the CLI directly from the main branch on GitHub
npx https://github.com/google-gemini/gemini-cli
```
## Deployment architecture
The execution methods described above are made possible by the following
architectural components and processes:
**NPM packages**
Gemini CLI project is a monorepo that publishes two core packages to the NPM
registry:
- `@google/gemini-cli-core`: The backend, handling logic and tool execution.
- `@google/gemini-cli`: The user-facing frontend.
These packages are used when performing the standard installation and when
running Gemini CLI from the source.
**Build and packaging processes**
There are two distinct build processes used, depending on the distribution
channel:
- **NPM publication:** For publishing to the NPM registry, the TypeScript source
code in `@google/gemini-cli-core` and `@google/gemini-cli` is transpiled into
standard JavaScript using the TypeScript Compiler (`tsc`). The resulting
`dist/` directory is what gets published in the NPM package. This is a
standard approach for TypeScript libraries.
- **GitHub `npx` execution:** When running the latest version of Gemini CLI
directly from GitHub, a different process is triggered by the `prepare` script
in `package.json`. This script uses `esbuild` to bundle the entire application
and its dependencies into a single, self-contained JavaScript file. This
bundle is created on-the-fly on the user's machine and is not checked into the
repository.
**Docker sandbox image**
The Docker-based execution method is supported by the `gemini-cli-sandbox`
container image. This image is published to a container registry and contains a
pre-installed, global version of Gemini CLI.
## Release process
The release process is automated through GitHub Actions. The release workflow
performs the following actions:
1. Build the NPM packages using `tsc`.
2. Publish the NPM packages to the artifact registry.
3. Create GitHub releases with bundled assets.
+1 -1
View File
@@ -24,7 +24,7 @@ Rename the photos in my "photos" directory based on their contents.
Result: Gemini will ask for permission to rename your files.
Select **Allow once** and your files will be renamed:
Select **Yes, allow once.** and your files will be renamed:
```bash
photos/yellow_flowers.png
+59 -50
View File
@@ -1,22 +1,64 @@
# Gemini 3 Pro and Gemini 3 Flash on Gemini CLI
# Gemini 3 Pro on Gemini CLI (join the waitlist)
Gemini 3 Pro and Gemini 3 Flash are available on Gemini CLI for all users!
Were excited to bring Gemini 3 Pro to Gemini CLI. For Google AI Ultra users
(Google AI Ultra for Business is not currently supported) and paid Gemini and
Vertex API key holders, Gemini 3 Pro is already available and ready to enable.
For everyone else, we're gradually expanding access
[through a waitlist](https://goo.gle/geminicli-waitlist-signup). Sign up for the
waitlist now to access Gemini 3 Pro once approved.
## How to get started with Gemini 3 on Gemini CLI
**Note:** Please wait until you have been approved to use Gemini 3 Pro to enable
**preview features**. If enabled early, the CLI will fallback to Gemini 2.5 Pro.
Get started by upgrading Gemini CLI to the latest version:
## Do I need to join the waitlist?
```bash
npm install -g @google/gemini-cli@latest
```
The following users will be **automatically granted access** to Gemini 3 Pro on
Gemini CLI:
After youve confirmed your version is 0.21.1 or later:
- Google AI Ultra subscribers (excluding Google AI Ultra for Business, which is
on the roadmap).
- Gemini API key users
[with access to Gemini 3](https://ai.google.dev/gemini-api/docs/rate-limits).
- Vertex API key users
[with access to Gemini 3](https://docs.cloud.google.com/vertex-ai/generative-ai/docs/quotas).
1. Use the `/settings` command in Gemini CLI.
2. Toggle **Preview Features** to `true`.
3. Run `/model` and select **Auto (Gemini 3)**.
For **Gemini Code Assist Enterprise users**, access is coming soon.
For more information, see [Gemini CLI model selection](../cli/model.md).
Users not automatically granted access through one of these account types will
need to join the waitlist. This includes Google AI Pro, Gemini Code Assist
standard, and free tier users.
Note: Whether youre automatically granted access or accepted from the waitlist,
youll still need to enable Gemini 3 Pro
[using the `/settings` command](../cli/settings.md).
## How to join the waitlist
Users not automatically granted access will need to join the waitlist. Follow
these instructions to sign up:
- Install Gemini CLI.
- Authenticate using the **Login with Google** option. Youll see a banner that
says “Gemini 3 is now available.” If you do not see this banner, update your
installation of Gemini CLI to the most recent version.
- Fill out this Google form:
[Access Gemini 3 in Gemini CLI](https://goo.gle/geminicli-waitlist-signup).
Provide the email address of the account you used to authenticate with Gemini
CLI.
Users will be onboarded in batches, subject to availability. When youve been
granted access to Gemini 3 Pro, youll receive an acceptance email to your
submitted email address.
## How to use Gemini 3 Pro with Gemini CLI
Once you receive your acceptance emailor if you are automatically granted
accessyou still need to enable Gemini 3 Pro within Gemini CLI.
To enable Gemini 3 Pro, use the `/settings` command in Gemini CLI and set
**Preview Features** to `true`.
For more information, see [Gemini CLI Settings](../cli/settings.md).
### Usage limits and fallback
@@ -34,12 +76,12 @@ There may be times when the Gemini 3 Pro model is overloaded. When that happens,
Gemini CLI will ask you to decide whether you want to keep trying Gemini 3 Pro
or fallback to Gemini 2.5 Pro.
> **Note:** The **Keep trying** option uses exponential backoff, in which Gemini
> CLI waits longer between each retry, when the system is busy. If the retry
> doesn't happen immediately, please wait a few minutes for the request to
> process.
**Note:** The **Keep trying** option uses exponential backoff, in which Gemini
CLI waits longer between each retry, when the system is busy. If the retry
doesn't happen immediately, please wait a few minutes for the request to
process.
### Model selection and routing types
## Model selection and routing types
When using Gemini CLI, you may want to control how your requests are routed
between models. By default, Gemini CLI uses **Auto** routing.
@@ -58,39 +100,6 @@ manage your usage limits:
To learn more about selecting a model and routing, refer to
[Gemini CLI Model Selection](../cli/model.md).
## How to enable Gemini 3 with Gemini CLI on Gemini Code Assist
If you're using Gemini Code Assist Standard or Gemini Code Assist Enterprise,
enabling Gemini 3 Pro on Gemini CLI requires configuring your release channels.
Using Gemini 3 Pro will require two steps: administrative enablement and user
enablement.
To learn more about these settings, refer to
[Configure Gemini Code Assist release channels](https://developers.google.com/gemini-code-assist/docs/configure-release-channels).
### Administrator instructions
An administrator with **Google Cloud Settings Admin** permissions must follow
these directions:
- Navigate to the Google Cloud Project you're using with Gemini CLI for Code
Assist.
- Go to **Admin for Gemini** > **Settings**.
- Under **Release channels for Gemini Code Assist in local IDEs** select
**Preview**.
- Click **Save changes**.
### User instructions
Wait for two to three minutes after your administrator has enabled **Preview**,
then:
- Open Gemini CLI.
- Use the `/settings` command.
- Set **Preview Features** to `true`.
Restart Gemini CLI and you should have access to Gemini 3.
## Need help?
If you need help, we recommend searching for an existing
-677
View File
@@ -1,677 +0,0 @@
# Hooks Best Practices
This guide covers security considerations, performance optimization, debugging
techniques, and privacy considerations for developing and deploying hooks in
Gemini CLI.
## Performance
### Keep hooks fast
Hooks run synchronously—slow hooks delay the agent loop. Optimize for speed by
using parallel operations:
```javascript
// Sequential operations are slower
const data1 = await fetch(url1).then((r) => r.json());
const data2 = await fetch(url2).then((r) => r.json());
// Prefer parallel operations for better performance
// Start requests concurrently
const p1 = fetch(url1).then((r) => r.json());
const p2 = fetch(url2).then((r) => r.json());
// Wait for all results
const [data1, data2] = await Promise.all([p1, p2]);
```
### Cache expensive operations
Store results between invocations to avoid repeated computation, especially for
hooks that run frequently (like `BeforeTool` or `AfterModel`).
```javascript
const fs = require('fs');
const path = require('path');
const CACHE_FILE = '.gemini/hook-cache.json';
function readCache() {
try {
return JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8'));
} catch {
return {};
}
}
function writeCache(data) {
fs.writeFileSync(CACHE_FILE, JSON.stringify(data, null, 2));
}
async function main() {
const cache = readCache();
const cacheKey = `tool-list-${(Date.now() / 3600000) | 0}`; // Hourly cache
if (cache[cacheKey]) {
// Write JSON to stdout
console.log(JSON.stringify(cache[cacheKey]));
return;
}
// Expensive operation
const result = await computeExpensiveResult();
cache[cacheKey] = result;
writeCache(cache);
console.log(JSON.stringify(result));
}
```
### Use appropriate events
Choose hook events that match your use case to avoid unnecessary execution.
- **`AfterAgent`**: Fires **once** per turn after the model finishes its final
response. Use this for quality validation (Retries) or final logging.
- **`AfterModel`**: Fires after **every chunk** of LLM output. Use this for
real-time redaction, PII filtering, or monitoring output as it streams.
If you only need to check the final completion, use `AfterAgent` to save
performance.
### Filter with matchers
Use specific matchers to avoid unnecessary hook execution. Instead of matching
all tools with `*`, specify only the tools you need. This saves the overhead of
spawning a process for irrelevant events.
```json
{
"matcher": "write_file|replace",
"hooks": [
{
"name": "validate-writes",
"type": "command",
"command": "./validate.sh"
}
]
}
```
### Optimize JSON parsing
For large inputs (like `AfterModel` receiving a large context), standard JSON
parsing can be slow. If you only need one field, consider streaming parsers or
lightweight extraction logic, though for most shell scripts `jq` is sufficient.
## Debugging
### The "Strict JSON" rule
The most common cause of hook failure is "polluting" the standard output.
- **stdout** is for **JSON only**.
- **stderr** is for **logs and text**.
**Good:**
```bash
#!/bin/bash
echo "Starting check..." >&2 # <--- Redirect to stderr
echo '{"decision": "allow"}'
```
### Log to files
Since hooks run in the background, writing to a dedicated log file is often the
easiest way to debug complex logic.
```bash
#!/usr/bin/env bash
LOG_FILE=".gemini/hooks/debug.log"
# Log with timestamp
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"
}
input=$(cat)
log "Received input: ${input:0:100}..."
# Hook logic here
log "Hook completed successfully"
# Always output valid JSON to stdout at the end, even if just empty
echo "{}"
```
### Use stderr for errors
Error messages on stderr are surfaced appropriately based on exit codes:
```javascript
try {
const result = dangerousOperation();
console.log(JSON.stringify({ result }));
} catch (error) {
// Write the error description to stderr so the user/agent sees it
console.error(`Hook error: ${error.message}`);
process.exit(2); // Blocking error
}
```
### Test hooks independently
Run hook scripts manually with sample JSON input to verify they behave as
expected before hooking them up to the CLI.
```bash
# Create test input
cat > test-input.json << 'EOF'
{
"session_id": "test-123",
"cwd": "/tmp/test",
"hook_event_name": "BeforeTool",
"tool_name": "write_file",
"tool_input": {
"file_path": "test.txt",
"content": "Test content"
}
}
EOF
# Test the hook
cat test-input.json | .gemini/hooks/my-hook.sh
# Check exit code
echo "Exit code: $?"
```
### Check exit codes
Gemini CLI uses exit codes for high-level flow control:
- **Exit 0 (Success)**: The hook ran successfully. The CLI parses `stdout` for
JSON decisions.
- **Exit 2 (System Block)**: A critical block occurred. `stderr` is used as the
reason.
- For **Agent/Model** events, this aborts the turn.
- For **Tool** events, this blocks the tool but allows the agent to continue.
- For **AfterAgent**, this triggers an automatic retry turn.
> **TIP**
>
> **Blocking vs. Stopping**: Use `decision: "deny"` (or Exit Code 2) to block a
> **specific action**. Use `{"continue": false}` in your JSON output to **kill
> the entire agent loop** immediately.
```bash
#!/usr/bin/env bash
set -e
# Hook logic
if process_input; then
echo '{"decision": "allow"}'
exit 0
else
echo "Critical validation failure" >&2
exit 2
fi
```
### Enable telemetry
Hook execution is logged when `telemetry.logPrompts` is enabled. You can view
these logs to debug execution flow.
```json
{
"telemetry": {
"logPrompts": true
}
}
```
### Use hook panel
The `/hooks panel` command inside the CLI shows execution status and recent
output:
```bash
/hooks panel
```
Check for:
- Hook execution counts
- Recent successes/failures
- Error messages
- Execution timing
## Development
### Start simple
Begin with basic logging hooks before implementing complex logic:
```bash
#!/usr/bin/env bash
# Simple logging hook to understand input structure
input=$(cat)
echo "$input" >> .gemini/hook-inputs.log
# Always return valid JSON
echo "{}"
```
### Documenting your hooks
Maintainability is critical for complex hook systems. Use descriptions and
comments to help yourself and others understand why a hook exists.
**Use the `description` field**: This text is displayed in the `/hooks panel` UI
and helps diagnose issues.
```json
{
"hooks": {
"BeforeTool": [
{
"matcher": "write_file|replace",
"hooks": [
{
"name": "secret-scanner",
"type": "command",
"command": "$GEMINI_PROJECT_DIR/.gemini/hooks/block-secrets.sh",
"description": "Scans code changes for API keys and secrets before writing"
}
]
}
]
}
}
```
**Add comments in hook scripts**: Explain performance expectations and
dependencies.
```javascript
#!/usr/bin/env node
/**
* RAG Tool Filter Hook
*
* Reduces the tool space by extracting keywords from the user's request.
*
* Performance: ~500ms average
* Dependencies: @google/generative-ai
*/
```
### Use JSON libraries
Parse JSON with proper libraries instead of text processing.
**Bad:**
```bash
# Fragile text parsing
tool_name=$(echo "$input" | grep -oP '"tool_name":\s*"\K[^"]+')
```
**Good:**
```bash
# Robust JSON parsing
tool_name=$(echo "$input" | jq -r '.tool_name')
```
### Make scripts executable
Always make hook scripts executable:
```bash
chmod +x .gemini/hooks/*.sh
chmod +x .gemini/hooks/*.js
```
### Version control
Commit hooks to share with your team:
```bash
git add .gemini/hooks/
git add .gemini/settings.json
```
**`.gitignore` considerations:**
```gitignore
# Ignore hook cache and logs
.gemini/hook-cache.json
.gemini/hook-debug.log
.gemini/memory/session-*.jsonl
# Keep hook scripts
!.gemini/hooks/*.sh
!.gemini/hooks/*.js
```
## Hook security
### Threat Model
Understanding where hooks come from and what they can do is critical for secure
usage.
| Hook Source | Description |
| :---------------------------- | :------------------------------------------------------------------------------------------------------------------------- |
| **System** | Configured by system administrators (e.g., `/etc/gemini-cli/settings.json`, `/Library/...`). Assumed to be the **safest**. |
| **User** (`~/.gemini/...`) | Configured by you. You are responsible for ensuring they are safe. |
| **Extensions** | You explicitly approve and install these. Security depends on the extension source (integrity). |
| **Project** (`./.gemini/...`) | **Untrusted by default.** Safest in trusted internal repos; higher risk in third-party/public repos. |
#### Project Hook Security
When you open a project with hooks defined in `.gemini/settings.json`:
1. **Detection**: Gemini CLI detects the hooks.
2. **Identification**: A unique identity is generated for each hook based on its
`name` and `command`.
3. **Warning**: If this specific hook identity has not been seen before, a
**warning** is displayed.
4. **Execution**: The hook is executed (unless specific security settings block
it).
5. **Trust**: The hook is marked as "trusted" for this project.
> **Modification detection**: If the `command` string of a project hook is
> changed (e.g., by a `git pull`), its identity changes. Gemini CLI will treat
> it as a **new, untrusted hook** and warn you again. This prevents malicious
> actors from silently swapping a verified command for a malicious one.
### Risks
| Risk | Description |
| :--------------------------- | :----------------------------------------------------------------------------------------------------------------------------------- |
| **Arbitrary Code Execution** | Hooks run as your user. They can do anything you can do (delete files, install software). |
| **Data Exfiltration** | A hook could read your input (prompts), output (code), or environment variables (`GEMINI_API_KEY`) and send them to a remote server. |
| **Prompt Injection** | Malicious content in a file or web page could trick an LLM into running a tool that triggers a hook in an unexpected way. |
### Mitigation Strategies
#### Verify the source
**Verify the source** of any project hooks or extensions before enabling them.
- For open-source projects, a quick review of the hook scripts is recommended.
- For extensions, ensure you trust the author or publisher (e.g., verified
publishers, well-known community members).
- Be cautious with obfuscated scripts or compiled binaries from unknown sources.
#### Sanitize environment
Hooks inherit the environment of the Gemini CLI process, which may include
sensitive API keys. Gemini CLI provides a
[redaction system](/docs/get-started/configuration#environment-variable-redaction)
that automatically filters variables matching sensitive patterns (e.g., `KEY`,
`TOKEN`).
> **Disabled by Default**: Environment redaction is currently **OFF by
> default**. We strongly recommend enabling it if you are running third-party
> hooks or working in sensitive environments.
**Impact on hooks:**
- **Security**: Prevents your hook scripts from accidentally leaking secrets.
- **Troubleshooting**: If your hook depends on a specific environment variable
that is being blocked, you must explicitly allow it in `settings.json`.
```json
{
"security": {
"environmentVariableRedaction": {
"enabled": true,
"allowed": ["MY_REQUIRED_TOOL_KEY"]
}
}
}
```
**System administrators:** You can enforce redaction for all users in the system
configuration.
## Troubleshooting
### Hook not executing
**Check hook name in `/hooks panel`:** Verify the hook appears in the list and
is enabled.
**Verify matcher pattern:**
```bash
# Test regex pattern
echo "write_file|replace" | grep -E "write_.*|replace"
```
**Check disabled list:** Verify the hook is not listed in your `settings.json`:
```json
{
"hooks": {
"disabled": ["my-hook-name"]
}
}
```
**Ensure script is executable**: For macOS and Linux users, verify the script
has execution permissions:
```bash
ls -la .gemini/hooks/my-hook.sh
chmod +x .gemini/hooks/my-hook.sh
```
**Verify script path:** Ensure the path in `settings.json` resolves correctly.
```bash
# Check path expansion
echo "$GEMINI_PROJECT_DIR/.gemini/hooks/my-hook.sh"
# Verify file exists
test -f "$GEMINI_PROJECT_DIR/.gemini/hooks/my-hook.sh" && echo "File exists"
```
### Hook timing out
**Check configured timeout:** The default is 60000ms (1 minute). You can
increase this in `settings.json`:
```json
{
"name": "slow-hook",
"timeout": 120000
}
```
**Optimize slow operations:** Move heavy processing to background tasks or use
caching.
### Invalid JSON output
**Validate JSON before outputting:**
```bash
#!/usr/bin/env bash
output='{"decision": "allow"}'
# Validate JSON
if echo "$output" | jq empty 2>/dev/null; then
echo "$output"
else
echo "Invalid JSON generated" >&2
exit 1
fi
```
### Environment variables not available
**Check if variable is set:**
```bash
#!/usr/bin/env bash
if [ -z "$GEMINI_PROJECT_DIR" ]; then
echo "GEMINI_PROJECT_DIR not set" >&2
exit 1
fi
```
**Debug available variables:**
```bash
env > .gemini/hook-env.log
```
## Authoring secure hooks
When writing your own hooks, follow these practices to ensure they are robust
and secure.
### Validate all inputs
Never trust data from hooks without validation. Hook inputs often come from the
LLM or user prompts, which can be manipulated.
```bash
#!/usr/bin/env bash
input=$(cat)
# Validate JSON structure
if ! echo "$input" | jq empty 2>/dev/null; then
echo "Invalid JSON input" >&2
exit 1
fi
# Validate tool_name explicitly
tool_name=$(echo "$input" | jq -r '.tool_name // empty')
if [[ "$tool_name" != "write_file" && "$tool_name" != "read_file" ]]; then
echo "Unexpected tool: $tool_name" >&2
exit 1
fi
```
### Use timeouts
Prevent denial-of-service (hanging agents) by enforcing timeouts. Gemini CLI
defaults to 60 seconds, but you should set stricter limits for fast hooks.
```json
{
"hooks": {
"BeforeTool": [
{
"matcher": "*",
"hooks": [
{
"name": "fast-validator",
"type": "command",
"command": "./hooks/validate.sh",
"timeout": 5000 // 5 seconds
}
]
}
]
}
}
```
### Limit permissions
Run hooks with minimal required permissions:
```bash
#!/usr/bin/env bash
# Don't run as root
if [ "$EUID" -eq 0 ]; then
echo "Hook should not run as root" >&2
exit 1
fi
# Check file permissions before writing
if [ -w "$file_path" ]; then
# Safe to write
else
echo "Insufficient permissions" >&2
exit 1
fi
```
### Example: Secret Scanner
Use `BeforeTool` hooks to prevent committing sensitive data. This is a powerful
pattern for enhancing security in your workflow.
```javascript
const SECRET_PATTERNS = [
/api[_-]?key\s*[:=]\s*['"]?[a-zA-Z0-9_-]{20,}['"]?/i,
/password\s*[:=]\s*['"]?[^\s'"]{8,}['"]?/i,
/secret\s*[:=]\s*['"]?[a-zA-Z0-9_-]{20,}['"]?/i,
/AKIA[0-9A-Z]{16}/, // AWS access key
/ghp_[a-zA-Z0-9]{36}/, // GitHub personal access token
/sk-[a-zA-Z0-9]{48}/, // OpenAI API key
];
function containsSecret(content) {
return SECRET_PATTERNS.some((pattern) => pattern.test(content));
}
```
## Privacy considerations
Hook inputs and outputs may contain sensitive information.
### What data is collected
Hook telemetry may include inputs (prompts, code) and outputs (decisions,
reasons) unless disabled.
### Privacy settings
**Disable PII logging:** If you are working with sensitive data, disable prompt
logging in your settings:
```json
{
"telemetry": {
"logPrompts": false
}
}
```
**Suppress Output:** Individual hooks can request their metadata be hidden from
logs and telemetry by returning `"suppressOutput": true` in their JSON response.
> **Note**
> `suppressOutput` only affects background logging. Any `systemMessage` or
> `reason` included in the JSON will still be displayed to the user in the
> terminal.
### Sensitive data in hooks
If your hooks process sensitive data:
1. **Minimize logging:** Don't write sensitive data to log files.
2. **Sanitize outputs:** Remove sensitive data before outputting JSON or writing
to stderr.
-178
View File
@@ -1,178 +0,0 @@
# Gemini CLI hooks (experimental)
Hooks are scripts or programs that Gemini CLI executes at specific points in the
agentic loop, allowing you to intercept and customize behavior without modifying
the CLI's source code.
## Availability
> **Experimental Feature**: Hooks are currently enabled by default only in the
> **Preview** and **Nightly** release channels.
If you are on the Stable channel, you must explicitly enable the hooks system in
your `settings.json`:
```json
{
"hooksConfig": {
"enabled": true
}
}
```
- **[Writing hooks guide](/docs/hooks/writing-hooks)**: A tutorial on creating
your first hook with comprehensive examples.
- **[Hooks reference](/docs/hooks/reference)**: The definitive technical
specification of I/O schemas and exit codes.
- **[Best practices](/docs/hooks/best-practices)**: Guidelines on security,
performance, and debugging.
## What are hooks?
Hooks run synchronously as part of the agent loop—when a hook event fires,
Gemini CLI waits for all matching hooks to complete before continuing.
With hooks, you can:
- **Add context:** Inject relevant information (like git history) before the
model processes a request.
- **Validate actions:** Review tool arguments and block potentially dangerous
operations.
- **Enforce policies:** Implement security scanners and compliance checks.
- **Log interactions:** Track tool usage and model responses for auditing.
- **Optimize behavior:** Dynamically filter available tools or adjust model
parameters.
## Core concepts
### Hook events
Hooks are triggered by specific events in Gemini CLI's lifecycle.
| Event | When It Fires | Impact | Common Use Cases |
| --------------------- | ---------------------------------------------- | ---------------------- | -------------------------------------------- |
| `SessionStart` | When a session begins (startup, resume, clear) | Inject Context | Initialize resources, load context |
| `SessionEnd` | When a session ends (exit, clear) | Advisory | Clean up, save state |
| `BeforeAgent` | After user submits prompt, before planning | Block Turn / Context | Add context, validate prompts, block turns |
| `AfterAgent` | When agent loop ends | Retry / Halt | Review output, force retry or halt execution |
| `BeforeModel` | Before sending request to LLM | Block Turn / Mock | Modify prompts, swap models, mock responses |
| `AfterModel` | After receiving LLM response | Block Turn / Redact | Filter/redact responses, log interactions |
| `BeforeToolSelection` | Before LLM selects tools | Filter Tools | Filter available tools, optimize selection |
| `BeforeTool` | Before a tool executes | Block Tool / Rewrite | Validate arguments, block dangerous ops |
| `AfterTool` | After a tool executes | Block Result / Context | Process results, run tests, hide results |
| `PreCompress` | Before context compression | Advisory | Save state, notify user |
| `Notification` | When a system notification occurs | Advisory | Forward to desktop alerts, logging |
### Global mechanics
Understanding these core principles is essential for building robust hooks.
#### Strict JSON requirements (The "Golden Rule")
Hooks communicate via `stdin` (Input) and `stdout` (Output).
1. **Silence is Mandatory**: Your script **must not** print any plain text to
`stdout` other than the final JSON object. **Even a single `echo` or `print`
call before the JSON will break parsing.**
2. **Pollution = Failure**: If `stdout` contains non-JSON text, parsing will
fail. The CLI will default to "Allow" and treat the entire output as a
`systemMessage`.
3. **Debug via Stderr**: Use `stderr` for **all** logging and debugging (e.g.,
`echo "debug" >&2`). Gemini CLI captures `stderr` but never attempts to parse
it as JSON.
#### Exit codes
Gemini CLI uses exit codes to determine the high-level outcome of a hook
execution:
| Exit Code | Label | Behavioral Impact |
| --------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **0** | **Success** | The `stdout` is parsed as JSON. **Preferred code** for all logic, including intentional blocks (e.g., `{"decision": "deny"}`). |
| **2** | **System Block** | **Critical Block**. The target action (tool, turn, or stop) is aborted. `stderr` is used as the rejection reason. High severity; used for security stops or script failures. |
| **Other** | **Warning** | Non-fatal failure. A warning is shown, but the interaction proceeds using original parameters. |
#### Matchers
You can filter which specific tools or triggers fire your hook using the
`matcher` field.
- **Tool events** (`BeforeTool`, `AfterTool`): Matchers are **Regular
Expressions**. (e.g., `"write_.*"`).
- **Lifecycle events**: Matchers are **Exact Strings**. (e.g., `"startup"`).
- **Wildcards**: `"*"` or `""` (empty string) matches all occurrences.
## Configuration
Hooks are configured in `settings.json`. Gemini CLI merges configurations from
multiple layers in the following order of precedence (highest to lowest):
1. **Project settings**: `.gemini/settings.json` in the current directory.
2. **User settings**: `~/.gemini/settings.json`.
3. **System settings**: `/etc/gemini-cli/settings.json`.
4. **Extensions**: Hooks defined by installed extensions.
### Configuration schema
```json
{
"hooks": {
"BeforeTool": [
{
"matcher": "write_file|replace",
"hooks": [
{
"name": "security-check",
"type": "command",
"command": "$GEMINI_PROJECT_DIR/.gemini/hooks/security.sh",
"timeout": 5000
}
]
}
]
}
}
```
#### Hook configuration fields
| Field | Type | Required | Description |
| :------------ | :----- | :-------- | :------------------------------------------------------------------- |
| `type` | string | **Yes** | The execution engine. Currently only `"command"` is supported. |
| `command` | string | **Yes\*** | The shell command to execute. (Required when `type` is `"command"`). |
| `name` | string | No | A friendly name for identifying the hook in logs and CLI commands. |
| `timeout` | number | No | Execution timeout in milliseconds (default: 60000). |
| `description` | string | No | A brief explanation of the hook's purpose. |
---
### Environment variables
Hooks are executed with a sanitized environment.
- `GEMINI_PROJECT_DIR`: The absolute path to the project root.
- `GEMINI_SESSION_ID`: The unique ID for the current session.
- `GEMINI_CWD`: The current working directory.
- `CLAUDE_PROJECT_DIR`: (Alias) Provided for compatibility.
## Security and risks
> **Warning: Hooks execute arbitrary code with your user privileges.** By
> configuring hooks, you are allowing scripts to run shell commands on your
> machine.
**Project-level hooks** are particularly risky when opening untrusted projects.
Gemini CLI **fingerprints** project hooks. If a hook's name or command changes
(e.g., via `git pull`), it is treated as a **new, untrusted hook** and you will
be warned before it executes.
See [Security Considerations](/docs/hooks/best-practices#using-hooks-securely)
for a detailed threat model.
## Managing hooks
Use the CLI commands to manage hooks without editing JSON manually:
- **View hooks:** `/hooks panel`
- **Enable/Disable all:** `/hooks enable-all` or `/hooks disable-all`
- **Toggle individual:** `/hooks enable <name>` or `/hooks disable <name>`
-322
View File
@@ -1,322 +0,0 @@
# Hooks reference
This document provides the technical specification for Gemini CLI hooks,
including JSON schemas and API details.
## Global hook mechanics
- **Communication**: `stdin` for Input (JSON), `stdout` for Output (JSON), and
`stderr` for logs and feedback.
- **Exit codes**:
- `0`: Success. `stdout` is parsed as JSON. **Preferred for all logic.**
- `2`: System Block. The action is blocked; `stderr` is used as the rejection
reason.
- `Other`: Warning. A non-fatal failure occurred; the CLI continues with a
warning.
- **Silence is Mandatory**: Your script **must not** print any plain text to
`stdout` other than the final JSON.
---
## Configuration schema
Hooks are defined in `settings.json` within the `hooks` object. Each event
(e.g., `BeforeTool`) contains an array of **hook definitions**.
### Hook definition
| Field | Type | Required | Description |
| :----------- | :-------- | :------- | :-------------------------------------------------------------------------------------- |
| `matcher` | `string` | No | A regex (for tools) or exact string (for lifecycle) to filter when the hook runs. |
| `sequential` | `boolean` | No | If `true`, hooks in this group run one after another. If `false`, they run in parallel. |
| `hooks` | `array` | **Yes** | An array of **hook configurations**. |
### Hook configuration
| Field | Type | Required | Description |
| :------------ | :------- | :-------- | :------------------------------------------------------------------- |
| `type` | `string` | **Yes** | The execution engine. Currently only `"command"` is supported. |
| `command` | `string` | **Yes\*** | The shell command to execute. (Required when `type` is `"command"`). |
| `name` | `string` | No | A friendly name for identifying the hook in logs and CLI commands. |
| `timeout` | `number` | No | Execution timeout in milliseconds (default: 60000). |
| `description` | `string` | No | A brief explanation of the hook's purpose. |
---
## Base input schema
All hooks receive these common fields via `stdin`:
```typescript
{
"session_id": string, // Unique ID for the current session
"transcript_path": string, // Absolute path to session transcript JSON
"cwd": string, // Current working directory
"hook_event_name": string, // The firing event (e.g. "BeforeTool")
"timestamp": string // ISO 8601 execution time
}
```
---
## Common output fields
Most hooks support these fields in their `stdout` JSON:
| Field | Type | Description |
| :--------------- | :-------- | :----------------------------------------------------------------------------- |
| `systemMessage` | `string` | Displayed immediately to the user in the terminal. |
| `suppressOutput` | `boolean` | If `true`, hides internal hook metadata from logs/telemetry. |
| `continue` | `boolean` | If `false`, stops the entire agent loop immediately. |
| `stopReason` | `string` | Displayed to the user when `continue` is `false`. |
| `decision` | `string` | `"allow"` or `"deny"` (alias `"block"`). Specific impact depends on the event. |
| `reason` | `string` | The feedback/error message provided when a `decision` is `"deny"`. |
---
## Tool hooks
### Matchers and tool names
For `BeforeTool` and `AfterTool` events, the `matcher` field in your settings is
compared against the name of the tool being executed.
- **Built-in Tools**: You can match any built-in tool (e.g., `read_file`,
`run_shell_command`). See the [Tools Reference](/docs/tools) for a full list
of available tool names.
- **MCP Tools**: Tools from MCP servers follow the naming pattern
`mcp__<server_name>__<tool_name>`.
- **Regex Support**: Matchers support regular expressions (e.g.,
`matcher: "read_.*"` matches all file reading tools).
### `BeforeTool`
Fires before a tool is invoked. Used for argument validation, security checks,
and parameter rewriting.
- **Input Fields**:
- `tool_name`: (`string`) The name of the tool being called.
- `tool_input`: (`object`) The raw arguments generated by the model.
- `mcp_context`: (`object`) Optional metadata for MCP-based tools.
- **Relevant Output Fields**:
- `decision`: Set to `"deny"` (or `"block"`) to prevent the tool from
executing.
- `reason`: Required if denied. This text is sent **to the agent** as a tool
error, allowing it to respond or retry.
- `hookSpecificOutput.tool_input`: An object that **merges with and
overrides** the model's arguments before execution.
- `continue`: Set to `false` to **kill the entire agent loop** immediately.
- **Exit Code 2 (Block Tool)**: Prevents execution. Uses `stderr` as the
`reason` sent to the agent. **The turn continues.**
### `AfterTool`
Fires after a tool executes. Used for result auditing, context injection, or
hiding sensitive output from the agent.
- **Input Fields**:
- `tool_name`: (`string`)
- `tool_input`: (`object`) The original arguments.
- `tool_response`: (`object`) The result containing `llmContent`,
`returnDisplay`, and optional `error`.
- `mcp_context`: (`object`)
- **Relevant Output Fields**:
- `decision`: Set to `"deny"` to hide the real tool output from the agent.
- `reason`: Required if denied. This text **replaces** the tool result sent
back to the model.
- `hookSpecificOutput.additionalContext`: Text that is **appended** to the
tool result for the agent.
- `continue`: Set to `false` to **kill the entire agent loop** immediately.
- **Exit Code 2 (Block Result)**: Hides the tool result. Uses `stderr` as the
replacement content sent to the agent. **The turn continues.**
---
## Agent hooks
### `BeforeAgent`
Fires after a user submits a prompt, but before the agent begins planning. Used
for prompt validation or injecting dynamic context.
- **Input Fields**:
- `prompt`: (`string`) The original text submitted by the user.
- **Relevant Output Fields**:
- `hookSpecificOutput.additionalContext`: Text that is **appended** to the
prompt for this turn only.
- `decision`: Set to `"deny"` to block the turn and **discard the user's
message** (it will not appear in history).
- `continue`: Set to `false` to block the turn but **save the message to
history**.
- `reason`: Required if denied or stopped.
- **Exit Code 2 (Block Turn)**: Aborts the turn and erases the prompt from
context. Same as `decision: "deny"`.
### `AfterAgent`
Fires once per turn after the model generates its final response. Primary use
case is response validation and automatic retries.
- **Input Fields**:
- `prompt`: (`string`) The user's original request.
- `prompt_response`: (`string`) The final text generated by the agent.
- `stop_hook_active`: (`boolean`) Indicates if this hook is already running as
part of a retry sequence.
- **Relevant Output Fields**:
- `decision`: Set to `"deny"` to **reject the response** and force a retry.
- `reason`: Required if denied. This text is sent **to the agent as a new
prompt** to request a correction.
- `continue`: Set to `false` to **stop the session** without retrying.
- `clearContext`: If `true`, clears conversation history (LLM memory) while
preserving UI display.
- **Exit Code 2 (Retry)**: Rejects the response and triggers an automatic retry
turn using `stderr` as the feedback prompt.
---
## Model hooks
### `BeforeModel`
Fires before sending a request to the LLM. Operates on a stable, SDK-agnostic
request format.
- **Input Fields**:
- `llm_request`: (`object`) Contains `model`, `messages`, and `config`
(generation params).
- **Relevant Output Fields**:
- `hookSpecificOutput.llm_request`: An object that **overrides** parts of the
outgoing request (e.g., changing models or temperature).
- `hookSpecificOutput.llm_response`: A **Synthetic Response** object. If
provided, the CLI skips the LLM call entirely and uses this as the response.
- `decision`: Set to `"deny"` to block the request and abort the turn.
- **Exit Code 2 (Block Turn)**: Aborts the turn and skips the LLM call. Uses
`stderr` as the error message.
### `BeforeToolSelection`
Fires before the LLM decides which tools to call. Used to filter the available
toolset or force specific tool modes.
- **Input Fields**:
- `llm_request`: (`object`) Same format as `BeforeModel`.
- **Relevant Output Fields**:
- `hookSpecificOutput.toolConfig.mode`: (`"AUTO" | "ANY" | "NONE"`)
- `"NONE"`: Disables all tools (Wins over other hooks).
- `"ANY"`: Forces at least one tool call.
- `hookSpecificOutput.toolConfig.allowedFunctionNames`: (`string[]`) Whitelist
of tool names.
- **Union Strategy**: Multiple hooks' whitelists are **combined**.
- **Limitations**: Does **not** support `decision`, `continue`, or
`systemMessage`.
### `AfterModel`
Fires immediately after an LLM response chunk is received. Used for real-time
redaction or PII filtering.
- **Input Fields**:
- `llm_request`: (`object`) The original request.
- `llm_response`: (`object`) The model's response (or a single chunk during
streaming).
- **Relevant Output Fields**:
- `hookSpecificOutput.llm_response`: An object that **replaces** the model's
response chunk.
- `decision`: Set to `"deny"` to discard the response chunk and block the
turn.
- `continue`: Set to `false` to **kill the entire agent loop** immediately.
- **Note on Streaming**: Fired for **every chunk** generated by the model.
Modifying the response only affects the current chunk.
- **Exit Code 2 (Block Response)**: Aborts the turn and discards the model's
output. Uses `stderr` as the error message.
---
## Lifecycle & system hooks
### `SessionStart`
Fires on application startup, resuming a session, or after a `/clear` command.
Used for loading initial context.
- **Input fields**:
- `source`: (`"startup" | "resume" | "clear"`)
- **Relevant output fields**:
- `hookSpecificOutput.additionalContext`: (`string`)
- **Interactive**: Injected as the first turn in history.
- **Non-interactive**: Prepended to the user's prompt.
- `systemMessage`: Shown at the start of the session.
- **Advisory only**: `continue` and `decision` fields are **ignored**. Startup
is never blocked.
### `SessionEnd`
Fires when the CLI exits or a session is cleared. Used for cleanup or final
telemetry.
- **Input Fields**:
- `reason`: (`"exit" | "clear" | "logout" | "prompt_input_exit" | "other"`)
- **Relevant Output Fields**:
- `systemMessage`: Displayed to the user during shutdown.
- **Best Effort**: The CLI **will not wait** for this hook to complete and
ignores all flow-control fields (`continue`, `decision`).
### `Notification`
Fires when the CLI emits a system alert (e.g., Tool Permissions). Used for
external logging or cross-platform alerts.
- **Input Fields**:
- `notification_type`: (`"ToolPermission"`)
- `message`: Summary of the alert.
- `details`: JSON object with alert-specific metadata (e.g., tool name, file
path).
- **Relevant Output Fields**:
- `systemMessage`: Displayed alongside the system alert.
- **Observability Only**: This hook **cannot** block alerts or grant permissions
automatically. Flow-control fields are ignored.
### `PreCompress`
Fires before the CLI summarizes history to save tokens. Used for logging or
state saving.
- **Input Fields**:
- `trigger`: (`"auto" | "manual"`)
- **Relevant Output Fields**:
- `systemMessage`: Displayed to the user before compression.
- **Advisory Only**: Fired asynchronously. It **cannot** block or modify the
compression process. Flow-control fields are ignored.
---
## Stable Model API
Gemini CLI uses these structures to ensure hooks don't break across SDK updates.
**LLMRequest**:
```typescript
{
"model": string,
"messages": Array<{
"role": "user" | "model" | "system",
"content": string // Non-text parts are filtered out for hooks
}>,
"config": { "temperature": number, ... },
"toolConfig": { "mode": string, "allowedFunctionNames": string[] }
}
```
**LLMResponse**:
```typescript
{
"candidates": Array<{
"content": { "role": "model", "parts": string[] },
"finishReason": string
}>,
"usageMetadata": { "totalTokenCount": number }
}
```
-450
View File
@@ -1,450 +0,0 @@
# Writing hooks for Gemini CLI
This guide will walk you through creating hooks for Gemini CLI, from a simple
logging hook to a comprehensive workflow assistant.
## Prerequisites
Before you start, make sure you have:
- Gemini CLI installed and configured
- Basic understanding of shell scripting or JavaScript/Node.js
- Familiarity with JSON for hook input/output
## Quick start
Let's create a simple hook that logs all tool executions to understand the
basics.
**Crucial Rule:** Always write logs to `stderr`. Write only the final JSON to
`stdout`.
### Step 1: Create your hook script
Create a directory for hooks and a simple logging script.
> **Note**:
>
> This example uses `jq` to parse JSON. If you don't have it installed, you can
> perform similar logic using Node.js or Python.
```bash
mkdir -p .gemini/hooks
cat > .gemini/hooks/log-tools.sh << 'EOF'
#!/usr/bin/env bash
# Read hook input from stdin
input=$(cat)
# Extract tool name (requires jq)
tool_name=$(echo "$input" | jq -r '.tool_name')
# Log to stderr (visible in terminal if hook fails, or captured in logs)
echo "Logging tool: $tool_name" >&2
# Log to file
echo "[$(date)] Tool executed: $tool_name" >> .gemini/tool-log.txt
# Return success (exit 0) with empty JSON
echo "{}"
exit 0
EOF
chmod +x .gemini/hooks/log-tools.sh
```
## Exit Code Strategies
There are two ways to control or block an action in Gemini CLI:
| Strategy | Exit Code | Implementation | Best For |
| :------------------------- | :-------- | :----------------------------------------------------------------- | :---------------------------------------------------------- |
| **Structured (Idiomatic)** | `0` | Return a JSON object like `{"decision": "deny", "reason": "..."}`. | Production hooks, custom user feedback, and complex logic. |
| **Emergency Brake** | `2` | Print the error message to `stderr` and exit. | Simple security gates, script errors, or rapid prototyping. |
## Practical examples
### Security: Block secrets in commits
Prevent committing files containing API keys or passwords. Note that we use
**Exit Code 0** to provide a structured denial message to the agent.
**`.gemini/hooks/block-secrets.sh`:**
```bash
#!/usr/bin/env bash
input=$(cat)
# Extract content being written
content=$(echo "$input" | jq -r '.tool_input.content // .tool_input.new_string // ""')
# Check for secrets
if echo "$content" | grep -qE 'api[_-]?key|password|secret'; then
# Log to stderr
echo "Blocked potential secret" >&2
# Return structured denial to stdout
cat <<EOF
{
"decision": "deny",
"reason": "Security Policy: Potential secret detected in content.",
"systemMessage": "🔒 Security scanner blocked operation"
}
EOF
exit 0
fi
# Allow
echo '{"decision": "allow"}'
exit 0
```
### Dynamic context injection (Git History)
Add relevant project context before each agent interaction.
**`.gemini/hooks/inject-context.sh`:**
```bash
#!/usr/bin/env bash
# Get recent git commits for context
context=$(git log -5 --oneline 2>/dev/null || echo "No git history")
# Return as JSON
cat <<EOF
{
"hookSpecificOutput": {
"hookEventName": "BeforeAgent",
"additionalContext": "Recent commits:\n$context"
}
}
EOF
```
### RAG-based Tool Filtering (BeforeToolSelection)
Use `BeforeToolSelection` to intelligently reduce the tool space. This example
uses a Node.js script to check the user's prompt and allow only relevant tools.
**`.gemini/hooks/filter-tools.js`:**
```javascript
#!/usr/bin/env node
const fs = require('fs');
async function main() {
const input = JSON.parse(fs.readFileSync(0, 'utf-8'));
const { llm_request } = input;
// Decoupled API: Access messages from llm_request
const messages = llm_request.messages || [];
const lastUserMessage = messages
.slice()
.reverse()
.find((m) => m.role === 'user');
if (!lastUserMessage) {
console.log(JSON.stringify({})); // Do nothing
return;
}
const text = lastUserMessage.content;
const allowed = ['write_todos']; // Always allow memory
// Simple keyword matching
if (text.includes('read') || text.includes('check')) {
allowed.push('read_file', 'list_directory');
}
if (text.includes('test')) {
allowed.push('run_shell_command');
}
// If we found specific intent, filter tools. Otherwise allow all.
if (allowed.length > 1) {
console.log(
JSON.stringify({
hookSpecificOutput: {
hookEventName: 'BeforeToolSelection',
toolConfig: {
mode: 'ANY', // Force usage of one of these tools (or AUTO)
allowedFunctionNames: allowed,
},
},
}),
);
} else {
console.log(JSON.stringify({}));
}
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
```
**`.gemini/settings.json`:**
```json
{
"hooks": {
"BeforeToolSelection": [
{
"matcher": "*",
"hooks": [
{
"name": "intent-filter",
"type": "command",
"command": "node .gemini/hooks/filter-tools.js"
}
]
}
]
}
}
```
> **TIP**
>
> **Union Aggregation Strategy**: `BeforeToolSelection` is unique in that it
> combines the results of all matching hooks. If you have multiple filtering
> hooks, the agent will receive the **union** of all whitelisted tools. Only
> using `mode: "NONE"` will override other hooks to disable all tools.
## Complete example: Smart Development Workflow Assistant
This comprehensive example demonstrates all hook events working together. We
will build a system that maintains memory, filters tools, and checks for
security.
### Architecture
1. **SessionStart**: Load project memories.
2. **BeforeAgent**: Inject memories into context.
3. **BeforeToolSelection**: Filter tools based on intent.
4. **BeforeTool**: Scan for secrets.
5. **AfterModel**: Record interactions.
6. **AfterAgent**: Validate final response quality (Retry).
7. **SessionEnd**: Consolidate memories.
### Configuration (`.gemini/settings.json`)
```json
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"name": "init",
"type": "command",
"command": "node .gemini/hooks/init.js"
}
]
}
],
"BeforeAgent": [
{
"matcher": "*",
"hooks": [
{
"name": "memory",
"type": "command",
"command": "node .gemini/hooks/inject-memories.js"
}
]
}
],
"BeforeToolSelection": [
{
"matcher": "*",
"hooks": [
{
"name": "filter",
"type": "command",
"command": "node .gemini/hooks/rag-filter.js"
}
]
}
],
"BeforeTool": [
{
"matcher": "write_file",
"hooks": [
{
"name": "security",
"type": "command",
"command": "node .gemini/hooks/security.js"
}
]
}
],
"AfterModel": [
{
"matcher": "*",
"hooks": [
{
"name": "record",
"type": "command",
"command": "node .gemini/hooks/record.js"
}
]
}
],
"AfterAgent": [
{
"matcher": "*",
"hooks": [
{
"name": "validate",
"type": "command",
"command": "node .gemini/hooks/validate.js"
}
]
}
],
"SessionEnd": [
{
"matcher": "exit",
"hooks": [
{
"name": "save",
"type": "command",
"command": "node .gemini/hooks/consolidate.js"
}
]
}
]
}
}
```
### Hook Scripts
> **Note**: For brevity, these scripts use `console.error` for logging and
> standard `console.log` for JSON output.
#### 1. Initialize (`init.js`)
```javascript
#!/usr/bin/env node
// Initialize DB or resources
console.error('Initializing assistant...');
// Output to user
console.log(
JSON.stringify({
systemMessage: '🧠 Smart Assistant Loaded',
}),
);
```
#### 2. Inject Memories (`inject-memories.js`)
```javascript
#!/usr/bin/env node
const fs = require('fs');
async function main() {
const input = JSON.parse(fs.readFileSync(0, 'utf-8'));
// Assume we fetch memories from a DB here
const memories = '- [Memory] Always use TypeScript for this project.';
console.log(
JSON.stringify({
hookSpecificOutput: {
hookEventName: 'BeforeAgent',
additionalContext: `\n## Relevant Memories\n${memories}`,
},
}),
);
}
main();
```
#### 3. Security Check (`security.js`)
```javascript
#!/usr/bin/env node
const fs = require('fs');
const input = JSON.parse(fs.readFileSync(0));
const content = input.tool_input.content || '';
if (content.includes('SECRET_KEY')) {
console.log(
JSON.stringify({
decision: 'deny',
reason: 'Found SECRET_KEY in content',
systemMessage: '🚨 Blocked sensitive commit',
}),
);
process.exit(0);
}
console.log(JSON.stringify({ decision: 'allow' }));
```
#### 4. Record Interaction (`record.js`)
```javascript
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const input = JSON.parse(fs.readFileSync(0));
const { llm_request, llm_response } = input;
const logFile = path.join(
process.env.GEMINI_PROJECT_DIR,
'.gemini/memory/session.jsonl',
);
fs.appendFileSync(
logFile,
JSON.stringify({
request: llm_request,
response: llm_response,
timestamp: new Date().toISOString(),
}) + '\n',
);
console.log(JSON.stringify({}));
```
#### 5. Validate Response (`validate.js`)
```javascript
#!/usr/bin/env node
const fs = require('fs');
const input = JSON.parse(fs.readFileSync(0));
const response = input.prompt_response;
// Example: Check if the agent forgot to include a summary
if (!response.includes('Summary:')) {
console.log(
JSON.stringify({
decision: 'block', // Triggers an automatic retry turn
reason: 'Your response is missing a Summary section. Please add one.',
systemMessage: '🔄 Requesting missing summary...',
}),
);
process.exit(0);
}
console.log(JSON.stringify({ decision: 'allow' }));
```
#### 6. Consolidate Memories (`consolidate.js`)
```javascript
#!/usr/bin/env node
// Logic to save final session state
console.error('Consolidating memories for session end...');
```
## Packaging as an extension
While project-level hooks are great for specific repositories, you can share
your hooks across multiple projects by packaging them as a
[Gemini CLI extension](https://www.google.com/search?q=../extensions/index.md).
This provides version control, easy distribution, and centralized management.
+2 -2
View File
@@ -128,8 +128,8 @@ editor.
You can also **modify the suggested changes** directly in the diff view before
accepting them.
If you select Allow for this session in the CLI, changes will no longer show
up in the IDE as they will be auto-accepted.
If you select Yes, allow always in the CLI, changes will no longer show up in
the IDE as they will be auto-accepted.
## Using with sandboxing
+4 -18
View File
@@ -56,8 +56,6 @@ This documentation is organized into the following sections:
commands with `/model`.
- **[Sandbox](./cli/sandbox.md):** Isolate tool execution in a secure,
containerized environment.
- **[Agent Skills](./cli/skills.md):** (Experimental) Extend the CLI with
specialized expertise and procedural workflows.
- **[Settings](./cli/settings.md):** Configure various aspects of the CLI's
behavior and appearance with `/settings`.
- **[Telemetry](./cli/telemetry.md):** Overview of telemetry in the CLI.
@@ -75,9 +73,6 @@ This documentation is organized into the following sections:
- **[Memport](./core/memport.md):** Using the Memory Import Processor.
- **[Tools API](./core/tools-api.md):** Information on how the core manages and
exposes tools.
- **[System Prompt Override](./cli/system-prompt.md):** Replace built-in system
instructions using `GEMINI_SYSTEM_MD`.
- **[Policy Engine](./core/policy-engine.md):** Use the Policy Engine for
fine-grained control over tool execution.
@@ -102,19 +97,10 @@ This documentation is organized into the following sections:
- **[Introduction: Extensions](./extensions/index.md):** How to extend the CLI
with new functionality.
- **[Writing extensions](./extensions/writing-extensions.md):** Learn how to
build your own extension.
- **[Extension releasing](./extensions/releasing.md):** How to release Gemini
CLI extensions.
### Hooks
- **[Hooks](./hooks/index.md):** Intercept and customize Gemini CLI behavior at
key lifecycle points.
- **[Writing Hooks](./hooks/writing-hooks.md):** Learn how to create your first
hook with a comprehensive example.
- **[Best Practices](./hooks/best-practices.md):** Security, performance, and
debugging guidelines for hooks.
- **[Get Started with extensions](./extensions/getting-started-extensions.md):**
Learn how to build your own extension.
- **[Extension releasing](./extensions/extension-releasing.md):** How to release
Gemini CLI extensions.
### IDE integration
+3 -3
View File
@@ -199,9 +199,9 @@ file, or case.
## Continuous integration
To ensure the integration tests are always run, a GitHub Actions workflow is
defined in `.github/workflows/chained_e2e.yml`. This workflow automatically runs
the integrations tests for pull requests against the `main` branch, or when a
pull request is added to a merge queue.
defined in `.github/workflows/e2e.yml`. This workflow automatically runs the
integrations tests for pull requests against the `main` branch, or when a pull
request is added to a merge queue.
The workflow runs the tests in different sandboxing environments to ensure
Gemini CLI is tested across each:
-3
View File
@@ -14,9 +14,6 @@ feature), while the PR is the "how" (the implementation). This separation helps
us track work, prioritize features, and maintain clear historical context. Our
automation is built around this principle.
> **Note:** Issues tagged as "🔒Maintainers only" are reserved for project
> maintainers. We will not accept pull requests related to these issues.
---
## Detailed automation workflows
+1 -1
View File
@@ -10,7 +10,7 @@ debug your code by instrumenting interesting events like model calls, tool
scheduler, tool calls, etc.
Dev traces are verbose and are specifically meant for understanding agent
behavior and debugging issues. They are disabled by default.
behaviour and debugging issues. They are disabled by default.
To enable dev traces, set the `GEMINI_DEV_TRACING=true` environment variable
when running Gemini CLI.
+1 -1
View File
@@ -33,7 +33,7 @@ nightly) or the release branch (for preview/stable).
### 2. End-to-end (E2E) tests
All workflows in `.github/workflows/chained_e2e.yml` must pass.
All workflows in `.github/workflows/e2e.yml` must pass.
- **Platforms:** **Linux, macOS and Windows**.
- **Sandboxing:** Tests must pass with both `sandbox:none` and `sandbox:docker`
+1 -1
View File
@@ -12,7 +12,7 @@ Dressing Room, which is Google's system for managing NPM packages in the
`@google/**` namespace. The packages are all named `@google/**`.
More information can be found about these systems in the
[NPM Package Overview](npm.md)
[maintainer repo guide](https://github.com/google-gemini/maintainers-gemini-cli/blob/main/npm.md)
### Package scopes
+210 -87
View File
@@ -1,52 +1,179 @@
[
{
"label": "Overview",
"items": [
{
"label": "Introduction",
"slug": "docs"
},
{
"label": "Architecture overview",
"slug": "docs/architecture"
},
{
"label": "Contribution guide",
"slug": "docs/contributing"
}
]
},
{
"label": "Get started",
"items": [
{ "label": "Overview", "slug": "docs" },
{ "label": "Quickstart", "slug": "docs/get-started" },
{ "label": "Installation", "slug": "docs/get-started/installation" },
{ "label": "Authentication", "slug": "docs/get-started/authentication" },
{ "label": "Examples", "slug": "docs/get-started/examples" },
{ "label": "Gemini 3 (preview)", "slug": "docs/get-started/gemini-3" }
{
"label": "Gemini CLI quickstart",
"slug": "docs/get-started"
},
{
"label": "Gemini 3 Pro on Gemini CLI",
"slug": "docs/get-started/gemini-3"
},
{
"label": "Authentication",
"slug": "docs/get-started/authentication"
},
{
"label": "Configuration",
"slug": "docs/get-started/configuration"
},
{
"label": "Installation",
"slug": "docs/get-started/installation"
},
{
"label": "Examples",
"slug": "docs/get-started/examples"
}
]
},
{
"label": "Use Gemini CLI",
"items": [
{ "label": "Using the CLI", "slug": "docs/cli" },
{ "label": "File management", "slug": "docs/tools/file-system" },
{ "label": "Memory management", "slug": "docs/tools/memory" },
{ "label": "Project context (GEMINI.md)", "slug": "docs/cli/gemini-md" },
{ "label": "Shell commands", "slug": "docs/tools/shell" },
{ "label": "Session management", "slug": "docs/cli/session-management" },
{ "label": "Todos", "slug": "docs/tools/todos" },
{ "label": "Web search and fetch", "slug": "docs/tools/web-search" }
]
},
{
"label": "Configuration",
"label": "CLI",
"items": [
{
"label": "Ignore files (.geminiignore)",
"slug": "docs/cli/gemini-ignore"
"label": "Introduction",
"slug": "docs/cli"
},
{ "label": "Model selection", "slug": "docs/cli/model" },
{ "label": "Settings", "slug": "docs/cli/settings" },
{ "label": "Themes", "slug": "docs/cli/themes" },
{ "label": "Token caching", "slug": "docs/cli/token-caching" },
{ "label": "Trusted folders", "slug": "docs/cli/trusted-folders" }
{
"label": "Commands",
"slug": "docs/cli/commands"
},
{
"label": "Checkpointing",
"slug": "docs/cli/checkpointing"
},
{
"label": "Custom commands",
"slug": "docs/cli/custom-commands"
},
{
"label": "Enterprise",
"slug": "docs/cli/enterprise"
},
{
"label": "Headless mode",
"slug": "docs/cli/headless"
},
{
"label": "Keyboard shortcuts",
"slug": "docs/cli/keyboard-shortcuts"
},
{
"label": "Model selection",
"slug": "docs/cli/model"
},
{
"label": "Sandbox",
"slug": "docs/cli/sandbox"
},
{
"label": "Session Management",
"slug": "docs/cli/session-management"
},
{
"label": "Settings",
"slug": "docs/cli/settings"
},
{
"label": "Telemetry",
"slug": "docs/cli/telemetry"
},
{
"label": "Themes",
"slug": "docs/cli/themes"
},
{
"label": "Token caching",
"slug": "docs/cli/token-caching"
},
{
"label": "Trusted Folders",
"slug": "docs/cli/trusted-folders"
},
{
"label": "Tutorials",
"slug": "docs/cli/tutorials"
},
{
"label": "Uninstall",
"slug": "docs/cli/uninstall"
}
]
},
{
"label": "Advanced features",
"label": "Core",
"items": [
{ "label": "Checkpointing", "slug": "docs/cli/checkpointing" },
{ "label": "Custom commands", "slug": "docs/cli/custom-commands" },
{ "label": "Enterprise features", "slug": "docs/cli/enterprise" },
{ "label": "Headless mode & scripting", "slug": "docs/cli/headless" },
{ "label": "Sandboxing", "slug": "docs/cli/sandbox" },
{ "label": "System prompt override", "slug": "docs/cli/system-prompt" },
{ "label": "Telemetry", "slug": "docs/cli/telemetry" }
{
"label": "Introduction",
"slug": "docs/core"
},
{
"label": "Tools API",
"slug": "docs/core/tools-api"
},
{
"label": "Memory Import Processor",
"slug": "docs/core/memport"
},
{
"label": "Policy Engine",
"slug": "docs/core/policy-engine"
}
]
},
{
"label": "Tools",
"items": [
{
"label": "Introduction",
"slug": "docs/tools"
},
{
"label": "File system",
"slug": "docs/tools/file-system"
},
{
"label": "Shell",
"slug": "docs/tools/shell"
},
{
"label": "Web fetch",
"slug": "docs/tools/web-fetch"
},
{
"label": "Web search",
"slug": "docs/tools/web-search"
},
{
"label": "Memory",
"slug": "docs/tools/memory"
},
{
"label": "Todos",
"slug": "docs/tools/todos"
},
{
"label": "MCP servers",
"slug": "docs/tools/mcp-server"
}
]
},
{
@@ -57,76 +184,72 @@
"slug": "docs/extensions"
},
{
"label": "Writing extensions",
"slug": "docs/extensions/writing-extensions"
"label": "Get started with extensions",
"slug": "docs/extensions/getting-started-extensions"
},
{
"label": "Extensions reference",
"slug": "docs/extensions/reference"
},
{
"label": "Best practices",
"slug": "docs/extensions/best-practices"
},
{
"label": "Extensions releasing",
"slug": "docs/extensions/releasing"
"label": "Extension releasing",
"slug": "docs/extensions/extension-releasing"
}
]
},
{
"label": "Ecosystem and extensibility",
"items": [
{ "label": "Agent skills (experimental)", "slug": "docs/cli/skills" },
{ "label": "Hooks (experimental)", "slug": "docs/hooks" },
{ "label": "IDE integration", "slug": "docs/ide-integration" },
{ "label": "MCP servers", "slug": "docs/tools/mcp-server" }
]
},
{
"label": "Tutorials",
"label": "IDE integration",
"items": [
{
"label": "Get started with extensions",
"slug": "docs/extensions/getting-started-extensions"
"label": "Introduction",
"slug": "docs/ide-integration"
},
{ "label": "How to write hooks", "slug": "docs/hooks/writing-hooks" }
]
},
{
"label": "Reference",
"items": [
{ "label": "Architecture", "slug": "docs/architecture" },
{ "label": "Command reference", "slug": "docs/cli/commands" },
{ "label": "Configuration", "slug": "docs/get-started/configuration" },
{ "label": "Keyboard shortcuts", "slug": "docs/cli/keyboard-shortcuts" },
{ "label": "Memory import processor", "slug": "docs/core/memport" },
{ "label": "Policy engine", "slug": "docs/core/policy-engine" },
{ "label": "Tools API", "slug": "docs/core/tools-api" }
]
},
{
"label": "Resources",
"items": [
{ "label": "FAQ", "slug": "docs/faq" },
{ "label": "Quota and pricing", "slug": "docs/quota-and-pricing" },
{ "label": "Release notes", "slug": "docs/changelogs/" },
{ "label": "Terms and privacy", "slug": "docs/tos-privacy" },
{ "label": "Troubleshooting", "slug": "docs/troubleshooting" },
{ "label": "Uninstall", "slug": "docs/cli/uninstall" }
{
"label": "IDE companion spec",
"slug": "docs/ide-integration/ide-companion-spec"
}
]
},
{
"label": "Development",
"items": [
{ "label": "Contribution guide", "slug": "docs/CONTRIBUTING" },
{ "label": "Integration testing", "slug": "docs/integration-tests" },
{
"label": "NPM",
"slug": "docs/npm"
},
{
"label": "Releases",
"slug": "docs/releases"
},
{
"label": "Changelog",
"slug": "docs/changelogs"
},
{
"label": "Integration tests",
"slug": "docs/integration-tests"
},
{
"label": "Issue and PR automation",
"slug": "docs/issue-and-pr-automation"
}
]
},
{
"label": "Support",
"items": [
{
"label": "FAQ",
"slug": "docs/faq"
},
{ "label": "Local development", "slug": "docs/local-development" },
{ "label": "NPM package structure", "slug": "docs/npm" }
{
"label": "Troubleshooting",
"slug": "docs/troubleshooting"
},
{
"label": "Quota and pricing",
"slug": "docs/quota-and-pricing"
},
{
"label": "Terms of service",
"slug": "docs/tos-privacy"
}
]
}
]
+6 -7
View File
@@ -36,9 +36,8 @@ glob patterns.
## 2. `read_file` (ReadFile)
`read_file` reads and returns the content of a specified file. This tool handles
text, images (PNG, JPG, GIF, WEBP, SVG, BMP), audio files (MP3, WAV, AIFF, AAC,
OGG, FLAC), and PDF files. For text files, it can read specific line ranges.
Other binary file types are generally skipped.
text, images (PNG, JPG, GIF, WEBP, SVG, BMP), and PDF files. For text files, it
can read specific line ranges. Other binary file types are generally skipped.
- **Tool name:** `read_file`
- **Display name:** ReadFile
@@ -54,16 +53,16 @@ Other binary file types are generally skipped.
- For text files: Returns the content. If `offset` and `limit` are used,
returns only that slice of lines. Indicates if content was truncated due to
line limits or line length limits.
- For image, audio, and PDF files: Returns the file content as a
base64-encoded data structure suitable for model consumption.
- For image and PDF files: Returns the file content as a base64-encoded data
structure suitable for model consumption.
- For other binary files: Attempts to identify and skip them, returning a
message indicating it's a generic binary file.
- **Output:** (`llmContent`):
- For text files: The file content, potentially prefixed with a truncation
message (e.g.,
`[File content truncated: showing lines 1-100 of 500 total lines...]\nActual file content...`).
- For image/audio/PDF files: An object containing `inlineData` with `mimeType`
and base64 `data` (e.g.,
- For image/PDF files: An object containing `inlineData` with `mimeType` and
base64 `data` (e.g.,
`{ inlineData: { mimeType: 'image/png', data: 'base64encodedstring' } }`).
- For other binary files: A message like
`Cannot display content of binary file: /path/to/data.bin`.
-3
View File
@@ -91,8 +91,5 @@ Additionally, these tools incorporate:
- **[MCP servers](./mcp-server.md)**: MCP servers act as a bridge between the
Gemini model and your local environment or other services like APIs.
- **[Agent Skills](../cli/skills.md)**: (Experimental) On-demand expertise
packages that are activated via the `activate_skill` tool to provide
specialized guidance and resources.
- **[Sandboxing](../cli/sandbox.md)**: Sandboxing isolates the model and its
changes from your environment to reduce potential risk.
+4 -55
View File
@@ -16,8 +16,8 @@ An MCP server enables the Gemini CLI to:
through standardized schema definitions.
- **Execute tools:** Call specific tools with defined arguments and receive
structured responses.
- **Access resources:** Read data from specific resources that the server
exposes (files, API payloads, reports, etc.).
- **Access resources:** Read data from specific resources (though the Gemini CLI
primarily focuses on tool execution).
With an MCP server, you can extend the Gemini CLI's capabilities to perform
actions beyond its built-in features, such as interacting with databases, APIs,
@@ -40,7 +40,6 @@ The discovery process is orchestrated by `discoverMcpTools()`, which:
4. **Sanitizes and validates** tool schemas for compatibility with the Gemini
API
5. **Registers tools** in the global tool registry with conflict resolution
6. **Fetches and registers resources** if the server exposes any
### Execution layer (`mcp-tool.ts`)
@@ -60,32 +59,6 @@ The Gemini CLI supports three MCP transport types:
- **SSE Transport:** Connects to Server-Sent Events endpoints
- **Streamable HTTP Transport:** Uses HTTP streaming for communication
## Working with MCP resources
Some MCP servers expose contextual “resources” in addition to the tools and
prompts. Gemini CLI discovers these automatically and gives you the possibility
to reference them in the chat.
### Discovery and listing
- When discovery runs, the CLI fetches each servers `resources/list` results.
- The `/mcp` command displays a Resources section alongside Tools and Prompts
for every connected server.
This returns a concise, plain-text list of URIs plus metadata.
### Referencing resources in a conversation
You can use the same `@` syntax already known for referencing local files:
```
@server://resource/path
```
Resource URIs appear in the completion menu together with filesystem paths. When
you submit the message, the CLI calls `resources/read` and injects the content
in the conversation.
## How to set up your MCP server
The Gemini CLI uses the `mcpServers` configuration in your `settings.json` file
@@ -722,8 +695,7 @@ The MCP integration tracks several states:
### Debugging tips
1. **Enable debug mode:** Run the CLI with `--debug` for verbose output (use F12
to open debug console in interactive mode)
1. **Enable debug mode:** Run the CLI with `--debug` for verbose output
2. **Check stderr:** MCP server stderr is captured and logged (INFO messages
filtered)
3. **Test isolation:** Test your MCP server independently before integrating
@@ -733,7 +705,7 @@ The MCP integration tracks several states:
## Important notes
### Security considerations
### Security sonsiderations
- **Trust settings:** The `trust` option bypasses all confirmation dialogs. Use
cautiously and only for servers you completely control
@@ -1038,29 +1010,6 @@ gemini mcp remove my-server
This will find and delete the "my-server" entry from the `mcpServers` object in
the appropriate `settings.json` file based on the scope (`-s, --scope`).
### Enabling/disabling a server (`gemini mcp enable`, `gemini mcp disable`)
Temporarily disable an MCP server without removing its configuration, or
re-enable a previously disabled server.
**Commands:**
```bash
gemini mcp enable <name> [--session]
gemini mcp disable <name> [--session]
```
**Options (flags):**
- `--session`: Apply change only for this session (not persisted to file).
Disabled servers appear in `/mcp` status as "Disabled" but won't connect or
provide tools. Enablement state is stored in
`~/.gemini/mcp-server-enablement.json`.
The same commands are available as slash commands during an active session:
`/mcp enable <name>` and `/mcp disable <name>`.
## Instructions
Gemini CLI supports
+1 -1
View File
@@ -135,7 +135,7 @@ user input, such as text editors (`vim`, `nano`), terminal-based UIs (`htop`),
and interactive version control operations (`git rebase -i`).
When an interactive command is running, you can send input to it from the Gemini
CLI. To focus on the interactive shell, press `Tab`. The terminal output,
CLI. To focus on the interactive shell, press `ctrl+f`. The terminal output,
including complex TUIs, will be rendered correctly.
## Important notes
+1 -1
View File
@@ -26,7 +26,7 @@ plan.
- **Progress tracking:** The agent updates this list as it works, marking tasks
as `completed` when done.
- **Single focus:** Only one task will be marked `in_progress` at a time,
- **Single socus:** Only one task will be marked `in_progress` at a time,
indicating exactly what the agent is currently working on.
- **Dynamic updates:** The plan may evolve as the agent discovers new
information, leading to new tasks being added or unnecessary ones being
+6 -23
View File
@@ -28,13 +28,6 @@ topics on:
- **Organizational Users:** Contact your Google Cloud administrator to be
added to your organization's Gemini Code Assist subscription.
- **Error:
`Failed to login. Message: Your current account is not eligible... because it is not currently available in your location.`**
- **Cause:** Gemini CLI does not currently support your location. For a full
list of supported locations, see the following pages:
- Gemini Code Assist for individuals:
[Available locations](https://developers.google.com/gemini-code-assist/resources/available-locations#americas)
- **Error: `Failed to login. Message: Request contains an invalid argument`**
- **Cause:** Users with Google Workspace accounts or Google Cloud accounts
associated with their Gmail accounts may not be able to activate the free
@@ -50,15 +43,9 @@ topics on:
- **Cause:** You may be on a corporate network with a firewall that intercepts
and inspects SSL/TLS traffic. This often requires a custom root CA
certificate to be trusted by Node.js.
- **Solution:** First try setting `NODE_USE_SYSTEM_CA`; if that does not
resolve the issue, set `NODE_EXTRA_CA_CERTS`.
- 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`
- 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`
- **Solution:** 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`
## Common error messages and solutions
@@ -137,15 +124,14 @@ This is especially useful for scripting and automation.
## Debugging tips
- **CLI debugging:**
- Use the `--debug` flag for more detailed output. In interactive mode, press
F12 to view the debug console.
- Use the `--verbose` flag (if available) with CLI commands for more detailed
output.
- Check the CLI logs, often found in a user-specific configuration or cache
directory.
- **Core debugging:**
- Check the server console output for error messages or stack traces.
- Increase log verbosity if configurable. For example, set the `DEBUG_MODE`
environment variable to `true` or `1`.
- Increase log verbosity if configurable.
- Use Node.js debugging tools (e.g., `node --inspect`) if you need to step
through server-side code.
@@ -168,6 +154,3 @@ guide_, consider searching the Gemini CLI
[Issue tracker on GitHub](https://github.com/google-gemini/gemini-cli/issues).
If you can't find an issue similar to yours, consider creating a new GitHub
Issue with a detailed description. Pull requests are also welcome!
> **Note:** Issues tagged as "🔒Maintainers only" are reserved for project
> maintainers. We will not accept pull requests related to these issues.
+2 -3
View File
@@ -14,8 +14,8 @@ let esbuild;
try {
esbuild = (await import('esbuild')).default;
} catch (_error) {
console.error('esbuild not available - cannot build bundle');
process.exit(1);
console.warn('esbuild not available, skipping bundle step');
process.exit(0);
}
const __filename = fileURLToPath(import.meta.url);
@@ -62,7 +62,6 @@ const external = [
'@lydell/node-pty-linux-x64',
'@lydell/node-pty-win32-arm64',
'@lydell/node-pty-win32-x64',
'keytar',
];
const baseConfig = {
+10 -66
View File
@@ -12,7 +12,7 @@ import prettierConfig from 'eslint-config-prettier';
import importPlugin from 'eslint-plugin-import';
import vitest from '@vitest/eslint-plugin';
import globals from 'globals';
import headers from 'eslint-plugin-headers';
import licenseHeader from 'eslint-plugin-license-header';
import path from 'node:path';
import url from 'node:url';
@@ -35,9 +35,6 @@ export default tseslint.config(
'package/bundle/**',
'.integration-tests/**',
'dist/**',
'evals/**',
'packages/test-utils/**',
'packages/core/src/skills/builtin/skill-creator/scripts/*.cjs',
],
},
eslint.configs.recommended,
@@ -167,43 +164,7 @@ export default tseslint.config(
'prefer-arrow-callback': 'error',
'prefer-const': ['error', { destructuring: 'all' }],
radix: 'error',
'no-console': 'error',
'default-case': 'error',
'@typescript-eslint/await-thenable': ['error'],
'@typescript-eslint/no-floating-promises': ['error'],
'@typescript-eslint/no-unnecessary-type-assertion': ['error'],
'no-restricted-imports': [
'error',
{
paths: [
{
name: 'node:os',
importNames: ['homedir', 'tmpdir'],
message:
'Please use the helpers from @google/gemini-cli-core instead of node:os homedir()/tmpdir() to ensure strict environment isolation.',
},
{
name: 'os',
importNames: ['homedir', 'tmpdir'],
message:
'Please use the helpers from @google/gemini-cli-core instead of os homedir()/tmpdir() to ensure strict environment isolation.',
},
],
},
],
},
},
{
// Allow os.homedir() in tests and paths.ts where it is used to implement the helper
files: [
'**/*.test.ts',
'**/*.test.tsx',
'packages/core/src/utils/paths.ts',
'packages/test-utils/src/**/*.ts',
'scripts/**/*.js',
],
rules: {
'no-restricted-imports': 'off',
},
},
{
@@ -245,26 +206,19 @@ export default tseslint.config(
{
files: ['./**/*.{tsx,ts,js}'],
plugins: {
headers,
'license-header': licenseHeader,
import: importPlugin,
},
rules: {
'headers/header-format': [
'license-header/header': [
'error',
{
source: 'string',
content: [
'@license',
'Copyright (year) Google LLC',
'SPDX-License-Identifier: Apache-2.0',
].join('\n'),
patterns: {
year: {
pattern: '202[5-6]',
defaultValue: '2026',
},
},
},
[
'/**',
' * @license',
' * Copyright 2025 Google LLC',
' * SPDX-License-Identifier: Apache-2.0',
' */',
],
],
'import/enforce-node-protocol-usage': ['error', 'always'],
},
@@ -304,16 +258,6 @@ export default tseslint.config(
'@typescript-eslint/no-require-imports': 'off',
},
},
// Examples should have access to standard globals like fetch
{
files: ['packages/cli/src/commands/extensions/examples/**/*.js'],
languageOptions: {
globals: {
...globals.node,
fetch: 'readonly',
},
},
},
// extra settings for scripts that we run directly with node
{
files: ['packages/vscode-ide-companion/scripts/**/*.js'],
-149
View File
@@ -1,149 +0,0 @@
# Behavioral Evals
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.
## Why Behavioral Evals?
Unlike traditional **integration tests** which verify that the system functions
correctly (e.g., "does the file writer actually write to disk?"), behavioral
evals verify that the model _chooses_ to take the correct action (e.g., "does
the model decide to write to disk when asked to save code?").
They are also distinct from broad **industry benchmarks** (like SWE-bench).
While benchmarks measure general capabilities across complex challenges, our
behavioral evals focus on specific, granular behaviors relevant to the Gemini
CLI's features.
### Key Characteristics
- **Feedback Loop**: They help us understand how changes to prompts or tools
affect the model's decision-making.
- _Did a change to the system prompt make the model less likely to use tool
X?_
- _Did a new tool definition confuse the model?_
- **Regression Testing**: They prevent regressions in model steering.
- **Non-Determinism**: Unlike unit tests, LLM behavior can be non-deterministic.
We distinguish between behaviors that should be robust (`ALWAYS_PASSES`) and
those that are generally reliable but might occasionally vary
(`USUALLY_PASSES`).
## Creating an Evaluation
Evaluations are located in the `evals` directory. Each evaluation is a Vitest
test file that uses the `evalTest` function from `evals/test-helper.ts`.
### `evalTest`
The `evalTest` function is a helper that runs a single evaluation case. It takes
two arguments:
1. `policy`: The consistency expectation for this test (`'ALWAYS_PASSES'` or
`'USUALLY_PASSES'`).
2. `evalCase`: An object defining the test case.
#### 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.
- `ALWAYS_PASSES`: Tests expected to pass 100% of the time. These are typically
trivial and test basic functionality. These run in every CI.
- `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.
#### `EvalCase` Properties
- `name`: The name of the evaluation case.
- `prompt`: The prompt to send to the model.
- `params`: An optional object with parameters to pass to the test rig (e.g.,
settings).
- `assert`: An async function that takes the test rig and the result of the run
and asserts that the result is correct.
- `log`: An optional boolean that, if set to `true`, will log the tool calls to
a file in the `evals/logs` directory.
### Example
```typescript
import { describe, expect } from 'vitest';
import { evalTest } from './test-helper.js';
describe('my_feature', () => {
evalTest('ALWAYS_PASSES', {
name: 'should do something',
prompt: 'do it',
assert: async (rig, result) => {
// assertions
},
});
});
```
## Running Evaluations
First, build the bundled Gemini CLI. You must do this after every code change.
```bash
npm run build
npm run bundle
```
### Always Passing Evals
To run the evaluations that are expected to always pass (CI safe):
```bash
npm run test:always_passing_evals
```
### All Evals
To run all evaluations, including those that may be flaky ("usually passes"):
```bash
npm run test:all_evals
```
This command sets the `RUN_EVALS` environment variable to `1`, which enables the
`USUALLY_PASSES` tests.
## Reporting
Results for evaluations are available on GitHub Actions:
- **CI Evals**: Included in the
[E2E (Chained)](https://github.com/google-gemini/gemini-cli/actions/workflows/chained_e2e.yml)
workflow. These must pass 100% for every PR.
- **Nightly Evals**: Run daily via the
[Evals: Nightly](https://github.com/google-gemini/gemini-cli/actions/workflows/evals-nightly.yml)
workflow. These track the long-term health and stability of model steering.
### Nightly Report Format
The nightly workflow executes the full evaluation suite multiple times
(currently 3 attempts) to account for non-determinism. These results are
aggregated into a **Nightly Summary** attached to the workflow run.
#### How to interpret the report:
- **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,
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.
A significant drop in the pass rate for a `USUALLY_PASSES` test—even if it
doesn't drop to 0%—often indicates that a recent change to a system prompt or
tool definition has made the model's behavior less reliable.
You may be able to investigate the regression using Gemini CLI by giving it the
link to the runs before and after the change and the name of the test and asking
it to investigate what changes may have impacted the test.
-41
View File
@@ -1,41 +0,0 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, expect } from 'vitest';
import { evalTest } from './test-helper.js';
import path from 'node:path';
import fs from 'node:fs/promises';
describe('generalist_agent', () => {
evalTest('USUALLY_PASSES', {
name: 'should be able to use generalist agent by explicitly asking the main agent to invoke it',
params: {
settings: {
agents: {
overrides: {
generalist: { enabled: true },
},
},
},
},
prompt:
'Please use the generalist agent to create a file called "generalist_test_file.txt" containing exactly the following text: success',
assert: async (rig) => {
// 1) Verify the generalist agent was invoked via delegate_to_agent
const foundToolCall = await rig.waitForToolCall('generalist');
expect(
foundToolCall,
'Expected to find a delegate_to_agent tool call for generalist agent',
).toBeTruthy();
// 2) Verify the file was created as expected
const filePath = path.join(rig.testDir!, 'generalist_test_file.txt');
const content = await fs.readFile(filePath, 'utf-8');
expect(content.trim()).toBe('success');
},
});
});
-77
View File
@@ -1,77 +0,0 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, expect } from 'vitest';
import { evalTest } from './test-helper.js';
const FILES = {
'.gitignore': 'node_modules\n',
'package.json': JSON.stringify({
name: 'test-project',
version: '1.0.0',
scripts: { test: 'echo "All tests passed!"' },
}),
'index.ts': 'const add = (a: number, b: number) => a - b;',
'index.test.ts': 'console.log("Running tests...");',
} as const;
describe('git repo eval', () => {
/**
* Ensures that the agent does not commit its changes when the user doesn't
* explicitly prompt it. This behavior was commonly observed with earlier prompts.
* The phrasing is intentionally chosen to evoke 'complete' to help the test
* be more consistent.
*/
evalTest('USUALLY_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',
files: FILES,
assert: async (rig, _result) => {
const toolLogs = rig.readToolLogs();
const commitCalls = toolLogs.filter((log) => {
if (log.toolRequest.name !== 'run_shell_command') return false;
try {
const args = JSON.parse(log.toolRequest.args);
return (
args.command &&
args.command.includes('git') &&
args.command.includes('commit')
);
} catch {
return false;
}
});
expect(commitCalls.length).toBe(0);
},
});
/**
* Ensures that the agent can commit its changes when prompted, despite being
* instructed to not do so by default.
*/
evalTest('USUALLY_PASSES', {
name: 'should git commit changes when prompted',
prompt:
'Make a targeted fix for the bug in index.ts without building, installing anything, or adding tests. Then, commit your changes.',
files: FILES,
assert: async (rig, _result) => {
const toolLogs = rig.readToolLogs();
const commitCalls = toolLogs.filter((log) => {
if (log.toolRequest.name !== 'run_shell_command') return false;
try {
const args = JSON.parse(log.toolRequest.args);
return args.command && args.command.includes('git commit');
} catch {
return false;
}
});
expect(commitCalls.length).toBeGreaterThanOrEqual(1);
},
});
});
-30
View File
@@ -1,30 +0,0 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, expect } from 'vitest';
import { evalTest } from './test-helper.js';
import { validateModelOutput } from '../integration-tests/test-helper.js';
describe('save_memory', () => {
evalTest('ALWAYS_PASSES', {
name: 'should be able to save to memory',
params: {
settings: { tools: { core: ['save_memory'] } },
},
prompt: `remember that my favorite color is blue.
what is my favorite color? tell me that and surround it with $ symbol`,
assert: async (rig, result) => {
const foundToolCall = await rig.waitForToolCall('save_memory');
expect(
foundToolCall,
'Expected to find a save_memory tool call',
).toBeTruthy();
validateModelOutput(result, 'blue', 'Save memory test');
},
});
});
-53
View File
@@ -1,53 +0,0 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe } from 'vitest';
import { evalTest } from './test-helper.js';
const AGENT_DEFINITION = `---
name: docs-agent
description: An agent with expertise in updating documentation.
tools:
- read_file
- write_file
---
You are the docs agent. Update the documentation.
`;
const INDEX_TS = 'export const add = (a: number, b: number) => a + b;';
describe('subagent eval test cases', () => {
/**
* Checks whether the outer agent reliably utilizes an expert subagent to
* accomplish a task when one is available.
*
* Note that the test is intentionally crafted to avoid the word "document"
* or "docs". We want to see the outer agent make the connection even when
* the prompt indirectly implies need of expertise.
*
* This tests the system prompt's subagent specific clauses.
*/
evalTest('USUALLY_PASSES', {
name: 'should delegate to user provided agent with relevant expertise',
params: {
settings: {
experimental: {
enableAgents: true,
},
},
},
prompt: 'Please update README.md with a description of this library.',
files: {
'.gemini/agents/test-agent.md': AGENT_DEFINITION,
'index.ts': INDEX_TS,
'README.md': 'TODO: update the README.',
},
assert: async (rig, _result) => {
await rig.expectToolCallSuccess(['docs-agent']);
},
});
});
-107
View File
@@ -1,107 +0,0 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { it } from 'vitest';
import fs from 'node:fs';
import path from 'node:path';
import { execSync } from 'node:child_process';
import { TestRig } from '@google/gemini-cli-test-utils';
import { createUnauthorizedToolError } from '@google/gemini-cli-core';
export * from '@google/gemini-cli-test-utils';
// Indicates the consistency expectation for this test.
// - ALWAYS_PASSES - Means that the test is expected to pass 100% of the time. These
// These tests are typically trivial and test basic functionality with unambiguous
// prompts. For example: "call save_memory to remember foo" should be fairly reliable.
// These are the first line of defense against regressions in key behaviors and run in
// every CI. You can run these locally with 'npm run test:always_passing_evals'.
//
// - USUALLY_PASSES - Means that the test is expected to pass most of the time but
// may have some flakiness as a result of relying on non-deterministic prompted
// behaviors and/or ambiguous prompts or complex tasks.
// For example: "Please do build changes until the very end" --> ambiguous whether
// the agent should add to memory without more explicit system prompt or user
// instructions. There are many more of these tests and they may pass less consistently.
// The pass/fail trendline of this set of tests can be used as a general measure
// of product quality. You can run these locally with 'npm run test:all_evals'.
// This may take a really long time and is not recommended.
export type EvalPolicy = 'ALWAYS_PASSES' | 'USUALLY_PASSES';
export function evalTest(policy: EvalPolicy, evalCase: EvalCase) {
const fn = async () => {
const rig = new TestRig();
try {
rig.setup(evalCase.name, evalCase.params);
if (evalCase.files) {
for (const [filePath, content] of Object.entries(evalCase.files)) {
const fullPath = path.join(rig.testDir!, filePath);
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
fs.writeFileSync(fullPath, content);
}
const execOptions = { cwd: rig.testDir!, stdio: 'inherit' as const };
execSync('git init', execOptions);
execSync('git config user.email "test@example.com"', execOptions);
execSync('git config user.name "Test User"', execOptions);
// Temporarily disable the interactive editor and git pager
// to avoid hanging the tests. It seems the the agent isn't
// consistently honoring the instructions to avoid interactive
// commands.
execSync('git config core.editor "true"', execOptions);
execSync('git config core.pager "cat"', execOptions);
execSync('git add .', execOptions);
execSync('git commit --allow-empty -m "Initial commit"', execOptions);
}
const result = await rig.run({
args: evalCase.prompt,
approvalMode: evalCase.approvalMode ?? 'yolo',
});
const unauthorizedErrorPrefix =
createUnauthorizedToolError('').split("'")[0];
if (result.includes(unauthorizedErrorPrefix)) {
throw new Error(
'Test failed due to unauthorized tool call in output: ' + result,
);
}
await evalCase.assert(rig, result);
} finally {
await logToFile(
evalCase.name,
JSON.stringify(rig.readToolLogs(), null, 2),
);
await rig.cleanup();
}
};
if (policy === 'USUALLY_PASSES' && !process.env['RUN_EVALS']) {
it.skip(evalCase.name, fn);
} else {
it(evalCase.name, fn);
}
}
export interface EvalCase {
name: string;
params?: Record<string, any>;
prompt: string;
files?: Record<string, string>;
approvalMode?: 'default' | 'auto_edit' | 'yolo' | 'plan';
assert: (rig: TestRig, result: string) => Promise<void>;
}
async function logToFile(name: string, content: string) {
const logDir = 'evals/logs';
await fs.promises.mkdir(logDir, { recursive: true });
const sanitizedName = name.replace(/[^a-z0-9]/gi, '_').toLowerCase();
const logFile = `${logDir}/${sanitizedName}.log`;
await fs.promises.writeFile(logFile, content);
}

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