From 1c65689257561a80eeacc214640428d58214cd85 Mon Sep 17 00:00:00 2001 From: gemini-cli-robot Date: Thu, 13 Nov 2025 23:54:15 +0000 Subject: [PATCH 01/13] chore(release): v0.16.0-preview.0 --- package-lock.json | 55 +++++++++------------- package.json | 4 +- packages/a2a-server/package.json | 2 +- packages/cli/package.json | 4 +- packages/core/package.json | 2 +- packages/test-utils/package.json | 2 +- packages/vscode-ide-companion/package.json | 2 +- 7 files changed, 30 insertions(+), 41 deletions(-) diff --git a/package-lock.json b/package-lock.json index ec434b94c93..3c1d1520882 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@google/gemini-cli", - "version": "0.16.0-nightly.20251113.ad1f0d99", + "version": "0.16.0-preview.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@google/gemini-cli", - "version": "0.16.0-nightly.20251113.ad1f0d99", + "version": "0.16.0-preview.0", "workspaces": [ "packages/*" ], @@ -2301,7 +2301,6 @@ "integrity": "sha512-t54CUOsFMappY1Jbzb7fetWeO0n6K0k/4+/ZpkS+3Joz8I4VcvY9OiEBFRYISqaI2fq5sCiPtAjRDOzVYG8m+Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.2", @@ -2482,7 +2481,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=8.0.0" } @@ -2516,7 +2514,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, @@ -2885,7 +2882,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/semantic-conventions": "^1.29.0" @@ -2919,7 +2915,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.1.tgz", "integrity": "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/resources": "2.0.1" @@ -2972,7 +2967,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz", "integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/resources": "2.0.1", @@ -4154,7 +4148,6 @@ "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -4442,7 +4435,6 @@ "integrity": "sha512-6sMvZePQrnZH2/cJkwRpkT7DxoAWh+g6+GFRK6bV3YQo7ogi3SX5rgF6099r5Q53Ma5qeT7LGmOmuIutF4t3lA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.35.0", "@typescript-eslint/types": "8.35.0", @@ -5210,7 +5202,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5565,7 +5556,8 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/array-includes": { "version": "3.1.9", @@ -6811,6 +6803,7 @@ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "license": "MIT", + "peer": true, "dependencies": { "safe-buffer": "5.2.1" }, @@ -7817,7 +7810,6 @@ "integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -8407,6 +8399,7 @@ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -8416,6 +8409,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", + "peer": true, "dependencies": { "ms": "2.0.0" } @@ -8425,6 +8419,7 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -8654,6 +8649,7 @@ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "license": "MIT", + "peer": true, "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", @@ -8672,6 +8668,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", + "peer": true, "dependencies": { "ms": "2.0.0" } @@ -8680,13 +8677,15 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/finalhandler/node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -9891,7 +9890,6 @@ "resolved": "https://registry.npmjs.org/@jrichman/ink/-/ink-6.4.3.tgz", "integrity": "sha512-2qm05tjtdia+d1gD7LQjPJyCPJluKDuR5B+FI3ZZXshFoU1igZBFvXs2++x9OT6d9755q+gkRPOdtH8jzx5MiQ==", "license": "MIT", - "peer": true, "dependencies": { "@alcalzone/ansi-tokenize": "^0.2.1", "ansi-escapes": "^7.0.0", @@ -12943,7 +12941,8 @@ "version": "0.1.12", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/path-type": { "version": "3.0.0", @@ -13477,7 +13476,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -13488,7 +13486,6 @@ "integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "shell-quote": "^1.6.1", "ws": "^7" @@ -15549,7 +15546,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -15714,8 +15710,7 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, - "license": "0BSD", - "peer": true + "license": "0BSD" }, "node_modules/tsx": { "version": "4.20.3", @@ -15723,7 +15718,6 @@ "integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" @@ -15908,7 +15902,6 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -16070,6 +16063,7 @@ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4.0" } @@ -16125,7 +16119,6 @@ "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -16242,7 +16235,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -16256,7 +16248,6 @@ "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", @@ -16954,7 +16945,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -16970,7 +16960,7 @@ }, "packages/a2a-server": { "name": "@google/gemini-cli-a2a-server", - "version": "0.16.0-nightly.20251113.ad1f0d99", + "version": "0.16.0-preview.0", "dependencies": { "@a2a-js/sdk": "^0.3.2", "@google-cloud/storage": "^7.16.0", @@ -17260,7 +17250,7 @@ }, "packages/cli": { "name": "@google/gemini-cli", - "version": "0.16.0-nightly.20251113.ad1f0d99", + "version": "0.16.0-preview.0", "dependencies": { "@google/gemini-cli-core": "file:../core", "@google/genai": "1.16.0", @@ -17360,7 +17350,7 @@ }, "packages/core": { "name": "@google/gemini-cli-core", - "version": "0.16.0-nightly.20251113.ad1f0d99", + "version": "0.16.0-preview.0", "dependencies": { "@google-cloud/logging": "^11.2.1", "@google-cloud/opentelemetry-cloud-monitoring-exporter": "^0.21.0", @@ -17495,7 +17485,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -17505,7 +17494,7 @@ }, "packages/test-utils": { "name": "@google/gemini-cli-test-utils", - "version": "0.16.0-nightly.20251113.ad1f0d99", + "version": "0.16.0-preview.0", "license": "Apache-2.0", "devDependencies": { "typescript": "^5.3.3" @@ -17516,7 +17505,7 @@ }, "packages/vscode-ide-companion": { "name": "gemini-cli-vscode-ide-companion", - "version": "0.16.0-nightly.20251113.ad1f0d99", + "version": "0.16.0-preview.0", "license": "LICENSE", "dependencies": { "@modelcontextprotocol/sdk": "^1.15.1", diff --git a/package.json b/package.json index 3fb9b057247..eaf8b2f82d5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli", - "version": "0.16.0-nightly.20251113.ad1f0d99", + "version": "0.16.0-preview.0", "engines": { "node": ">=20.0.0" }, @@ -14,7 +14,7 @@ "url": "git+https://github.com/google-gemini/gemini-cli.git" }, "config": { - "sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.16.0-nightly.20251113.ad1f0d99" + "sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.16.0-preview.0" }, "scripts": { "start": "cross-env NODE_ENV=development node scripts/start.js", diff --git a/packages/a2a-server/package.json b/packages/a2a-server/package.json index 869f26d4aa4..b4df21b772f 100644 --- a/packages/a2a-server/package.json +++ b/packages/a2a-server/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli-a2a-server", - "version": "0.16.0-nightly.20251113.ad1f0d99", + "version": "0.16.0-preview.0", "description": "Gemini CLI A2A Server", "repository": { "type": "git", diff --git a/packages/cli/package.json b/packages/cli/package.json index 45d61285c99..9bdcb2393ac 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli", - "version": "0.16.0-nightly.20251113.ad1f0d99", + "version": "0.16.0-preview.0", "description": "Gemini CLI", "repository": { "type": "git", @@ -25,7 +25,7 @@ "dist" ], "config": { - "sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.16.0-nightly.20251113.ad1f0d99" + "sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.16.0-preview.0" }, "dependencies": { "@google/gemini-cli-core": "file:../core", diff --git a/packages/core/package.json b/packages/core/package.json index b67080d0da0..c1f8c689221 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli-core", - "version": "0.16.0-nightly.20251113.ad1f0d99", + "version": "0.16.0-preview.0", "description": "Gemini CLI Core", "repository": { "type": "git", diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 51e3ff397f5..908aa3d1627 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli-test-utils", - "version": "0.16.0-nightly.20251113.ad1f0d99", + "version": "0.16.0-preview.0", "private": true, "main": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/vscode-ide-companion/package.json b/packages/vscode-ide-companion/package.json index 5f5e7a621bd..78990ee59ce 100644 --- a/packages/vscode-ide-companion/package.json +++ b/packages/vscode-ide-companion/package.json @@ -2,7 +2,7 @@ "name": "gemini-cli-vscode-ide-companion", "displayName": "Gemini CLI Companion", "description": "Enable Gemini CLI with direct access to your IDE workspace.", - "version": "0.16.0-nightly.20251113.ad1f0d99", + "version": "0.16.0-preview.0", "publisher": "google", "icon": "assets/icon.png", "repository": { From 3cb670fe3d4fbf96cb2667589bf28210c6c1f56f Mon Sep 17 00:00:00 2001 From: gemini-cli-robot Date: Fri, 14 Nov 2025 13:11:02 -0800 Subject: [PATCH 02/13] fix(patch): cherry-pick ba15eeb to release/v0.16.0-preview.0-pr-13083 to patch version v0.16.0-preview.0 and create version 0.16.0-preview.1 (#13093) Co-authored-by: Jacob Richman --- packages/cli/src/ui/AppContainer.tsx | 29 +++- packages/cli/src/ui/components/Composer.tsx | 4 + packages/cli/src/ui/components/Help.test.tsx | 11 ++ packages/cli/src/ui/components/Help.tsx | 12 ++ .../cli/src/ui/components/InputPrompt.tsx | 2 + .../cli/src/ui/contexts/MouseContext.test.tsx | 42 ++++++ packages/cli/src/ui/contexts/MouseContext.tsx | 18 ++- .../src/ui/contexts/ScrollProvider.test.tsx | 79 ++++++++++- .../cli/src/ui/contexts/ScrollProvider.tsx | 35 +++-- .../cli/src/ui/contexts/UIStateContext.tsx | 1 + .../ui/hooks/slashCommandProcessor.test.tsx | 133 +++++++++++++----- .../cli/src/ui/hooks/slashCommandProcessor.ts | 7 +- packages/cli/src/ui/utils/mouse.ts | 2 +- packages/cli/src/utils/events.ts | 2 + 14 files changed, 320 insertions(+), 57 deletions(-) diff --git a/packages/cli/src/ui/AppContainer.tsx b/packages/cli/src/ui/AppContainer.tsx index 01fce895f3e..5eaa6922a5a 100644 --- a/packages/cli/src/ui/AppContainer.tsx +++ b/packages/cli/src/ui/AppContainer.tsx @@ -109,7 +109,7 @@ import { disableMouseEvents, enableMouseEvents } from './utils/mouse.js'; import { useAlternateBuffer } from './hooks/useAlternateBuffer.js'; import { useSettings } from './contexts/SettingsContext.js'; -const CTRL_EXIT_PROMPT_DURATION_MS = 1000; +const WARNING_PROMPT_DURATION_MS = 1000; const QUEUE_ERROR_DISPLAY_DURATION_MS = 3000; function isToolExecuting(pendingHistoryItems: HistoryItemWithoutId[]) { @@ -892,6 +892,7 @@ Logging in with Google... Please restart Gemini CLI to continue. >(); const [showEscapePrompt, setShowEscapePrompt] = useState(false); const [showIdeRestartPrompt, setShowIdeRestartPrompt] = useState(false); + const [selectionWarning, setSelectionWarning] = useState(false); const { isFolderTrustDialogOpen, handleFolderTrustSelect, isRestarting } = useFolderTrust(settings, setIsTrustedFolder, historyManager.addItem); @@ -901,6 +902,26 @@ Logging in with Google... Please restart Gemini CLI to continue. } = useIdeTrustListener(); const isInitialMount = useRef(true); + useEffect(() => { + let timeoutId: NodeJS.Timeout; + const handleSelectionWarning = () => { + setSelectionWarning(true); + if (timeoutId) { + clearTimeout(timeoutId); + } + timeoutId = setTimeout(() => { + setSelectionWarning(false); + }, WARNING_PROMPT_DURATION_MS); + }; + appEvents.on(AppEvent.SelectionWarning, handleSelectionWarning); + return () => { + appEvents.off(AppEvent.SelectionWarning, handleSelectionWarning); + if (timeoutId) { + clearTimeout(timeoutId); + } + }; + }, []); + useEffect(() => { if (ideNeedsRestart) { // IDE trust changed, force a restart. @@ -976,7 +997,7 @@ Logging in with Google... Please restart Gemini CLI to continue. ctrlCTimerRef.current = setTimeout(() => { setCtrlCPressCount(0); ctrlCTimerRef.current = null; - }, CTRL_EXIT_PROMPT_DURATION_MS); + }, WARNING_PROMPT_DURATION_MS); } }, [ctrlCPressCount, config, setCtrlCPressCount, handleSlashCommand]); @@ -994,7 +1015,7 @@ Logging in with Google... Please restart Gemini CLI to continue. ctrlDTimerRef.current = setTimeout(() => { setCtrlDPressCount(0); ctrlDTimerRef.current = null; - }, CTRL_EXIT_PROMPT_DURATION_MS); + }, WARNING_PROMPT_DURATION_MS); } }, [ctrlDPressCount, config, setCtrlDPressCount, handleSlashCommand]); @@ -1345,6 +1366,7 @@ Logging in with Google... Please restart Gemini CLI to continue. embeddedShellFocused, showDebugProfiler, copyModeEnabled, + selectionWarning, }), [ isThemeDialogOpen, @@ -1430,6 +1452,7 @@ Logging in with Google... Please restart Gemini CLI to continue. apiKeyDefaultValue, authState, copyModeEnabled, + selectionWarning, ], ); diff --git a/packages/cli/src/ui/components/Composer.tsx b/packages/cli/src/ui/components/Composer.tsx index 6962ab21608..6654541816c 100644 --- a/packages/cli/src/ui/components/Composer.tsx +++ b/packages/cli/src/ui/components/Composer.tsx @@ -99,6 +99,10 @@ export const Composer = () => { Press Ctrl+C again to exit. + ) : uiState.selectionWarning ? ( + + Press Ctrl-S to enter selection mode to copy text. + ) : uiState.ctrlDPressedOnce ? ( Press Ctrl+D again to exit. diff --git a/packages/cli/src/ui/components/Help.test.tsx b/packages/cli/src/ui/components/Help.test.tsx index c43c1e71dfc..6a2907350b8 100644 --- a/packages/cli/src/ui/components/Help.test.tsx +++ b/packages/cli/src/ui/components/Help.test.tsx @@ -60,4 +60,15 @@ describe('Help Component', () => { expect(output).not.toContain('hidden-child'); unmount(); }); + + it('should render keyboard shortcuts', () => { + const { lastFrame, unmount } = render(); + const output = lastFrame(); + + expect(output).toContain('Keyboard Shortcuts:'); + expect(output).toContain('Ctrl+C'); + expect(output).toContain('Ctrl+S'); + expect(output).toContain('Page Up/Down'); + unmount(); + }); }); diff --git a/packages/cli/src/ui/components/Help.tsx b/packages/cli/src/ui/components/Help.tsx index a3e124d759d..385f7edfa34 100644 --- a/packages/cli/src/ui/components/Help.tsx +++ b/packages/cli/src/ui/components/Help.tsx @@ -136,6 +136,12 @@ export const Help: React.FC = ({ commands }) => ( {' '} - Clear the screen + + + Ctrl+S + {' '} + - Enter selection mode to copy text + {process.platform === 'darwin' ? 'Ctrl+X / Meta+Enter' : 'Ctrl+X'} @@ -160,6 +166,12 @@ export const Help: React.FC = ({ commands }) => ( {' '} - Cancel operation / Clear input (double press) + + + Page Up/Down + {' '} + - Scroll page up/down + Shift+Tab diff --git a/packages/cli/src/ui/components/InputPrompt.tsx b/packages/cli/src/ui/components/InputPrompt.tsx index 06bbf4da745..5e908614dbe 100644 --- a/packages/cli/src/ui/components/InputPrompt.tsx +++ b/packages/cli/src/ui/components/InputPrompt.tsx @@ -379,8 +379,10 @@ export const InputPrompt: React.FC = ({ const relY = mouseY - y; const visualRow = buffer.visualScrollRow + relY; buffer.moveToVisualPosition(visualRow, relX); + return true; } } + return false; }, [buffer], ); diff --git a/packages/cli/src/ui/contexts/MouseContext.test.tsx b/packages/cli/src/ui/contexts/MouseContext.test.tsx index fb57f525198..02247215689 100644 --- a/packages/cli/src/ui/contexts/MouseContext.test.tsx +++ b/packages/cli/src/ui/contexts/MouseContext.test.tsx @@ -11,6 +11,7 @@ import { vi, type Mock } from 'vitest'; import type React from 'react'; import { useStdin } from 'ink'; import { EventEmitter } from 'node:events'; +import { appEvents, AppEvent } from '../../utils/events.js'; // Mock the 'ink' module to control stdin vi.mock('ink', async (importOriginal) => { @@ -21,6 +22,18 @@ vi.mock('ink', async (importOriginal) => { }; }); +// Mock appEvents +vi.mock('../../utils/events.js', () => ({ + appEvents: { + emit: vi.fn(), + on: vi.fn(), + off: vi.fn(), + }, + AppEvent: { + SelectionWarning: 'selection-warning', + }, +})); + class MockStdin extends EventEmitter { isTTY = true; setRawMode = vi.fn(); @@ -47,6 +60,7 @@ describe('MouseContext', () => { wrapper = ({ children }: { children: React.ReactNode }) => ( {children} ); + vi.mocked(appEvents.emit).mockClear(); }); afterEach(() => { @@ -91,6 +105,34 @@ describe('MouseContext', () => { expect(handler).not.toHaveBeenCalled(); }); + it('should emit SelectionWarning when move event is unhandled and has coordinates', () => { + renderHook(() => useMouseContext(), { wrapper }); + + act(() => { + // Move event (32) at 10, 20 + stdin.write('\x1b[<32;10;20M'); + }); + + expect(appEvents.emit).toHaveBeenCalledWith(AppEvent.SelectionWarning); + }); + + it('should not emit SelectionWarning when move event is handled', () => { + const handler = vi.fn().mockReturnValue(true); + const { result } = renderHook(() => useMouseContext(), { wrapper }); + + act(() => { + result.current.subscribe(handler); + }); + + act(() => { + // Move event (32) at 10, 20 + stdin.write('\x1b[<32;10;20M'); + }); + + expect(handler).toHaveBeenCalled(); + expect(appEvents.emit).not.toHaveBeenCalled(); + }); + describe('SGR Mouse Events', () => { it.each([ { diff --git a/packages/cli/src/ui/contexts/MouseContext.tsx b/packages/cli/src/ui/contexts/MouseContext.tsx index 8b1b8876a87..fef5b8c343f 100644 --- a/packages/cli/src/ui/contexts/MouseContext.tsx +++ b/packages/cli/src/ui/contexts/MouseContext.tsx @@ -15,6 +15,7 @@ import { } from 'react'; import { ESC } from '../utils/input.js'; import { debugLogger } from '@google/gemini-cli-core'; +import { appEvents, AppEvent } from '../../utils/events.js'; import { isIncompleteMouseSequence, parseMouseEvent, @@ -89,8 +90,23 @@ export function MouseProvider({ let mouseBuffer = ''; const broadcast = (event: MouseEvent) => { + let handled = false; for (const handler of subscribers) { - handler(event); + if (handler(event) === true) { + handled = true; + } + } + if ( + !handled && + event.name === 'move' && + event.col >= 0 && + event.row >= 0 + ) { + // Terminal apps only receive mouse move events when the mouse is down + // so this always indicates a mouse drag that the user was expecting + // would trigger text selection but does not as we are handling mouse + // events not the terminal. + appEvents.emit(AppEvent.SelectionWarning); } }; diff --git a/packages/cli/src/ui/contexts/ScrollProvider.test.tsx b/packages/cli/src/ui/contexts/ScrollProvider.test.tsx index a741579f158..df1906b6102 100644 --- a/packages/cli/src/ui/contexts/ScrollProvider.test.tsx +++ b/packages/cli/src/ui/contexts/ScrollProvider.test.tsx @@ -16,12 +16,12 @@ import { Box, type DOMElement } from 'ink'; import type { MouseEvent } from '../hooks/useMouse.js'; // Mock useMouse hook -const mockUseMouseCallbacks = new Set<(event: MouseEvent) => void>(); +const mockUseMouseCallbacks = new Set<(event: MouseEvent) => void | boolean>(); vi.mock('../hooks/useMouse.js', async () => { // We need to import React dynamically because this factory runs before top-level imports const React = await import('react'); return { - useMouse: (callback: (event: MouseEvent) => void) => { + useMouse: (callback: (event: MouseEvent) => void | boolean) => { React.useEffect(() => { mockUseMouseCallbacks.add(callback); return () => { @@ -81,6 +81,81 @@ describe('ScrollProvider', () => { vi.useRealTimers(); }); + describe('Event Handling Status', () => { + it('returns true when scroll event is handled', () => { + const scrollBy = vi.fn(); + const getScrollState = vi.fn(() => ({ + scrollTop: 0, + scrollHeight: 100, + innerHeight: 10, + })); + + render( + + + , + ); + + let handled = false; + for (const callback of mockUseMouseCallbacks) { + if ( + callback({ + name: 'scroll-down', + col: 5, + row: 5, + shift: false, + ctrl: false, + meta: false, + }) === true + ) { + handled = true; + } + } + expect(handled).toBe(true); + }); + + it('returns false when scroll event is ignored (cannot scroll further)', () => { + const scrollBy = vi.fn(); + // Already at bottom + const getScrollState = vi.fn(() => ({ + scrollTop: 90, + scrollHeight: 100, + innerHeight: 10, + })); + + render( + + + , + ); + + let handled = false; + for (const callback of mockUseMouseCallbacks) { + if ( + callback({ + name: 'scroll-down', + col: 5, + row: 5, + shift: false, + ctrl: false, + meta: false, + }) === true + ) { + handled = true; + } + } + expect(handled).toBe(false); + }); + }); + it('calls scrollTo when clicking scrollbar track if available', async () => { const scrollBy = vi.fn(); const scrollTo = vi.fn(); diff --git a/packages/cli/src/ui/contexts/ScrollProvider.tsx b/packages/cli/src/ui/contexts/ScrollProvider.tsx index 3c272f596df..b461622fb2f 100644 --- a/packages/cli/src/ui/contexts/ScrollProvider.tsx +++ b/packages/cli/src/ui/contexts/ScrollProvider.tsx @@ -146,15 +146,16 @@ export const ScrollProvider: React.FC<{ children: React.ReactNode }> = ({ if (direction === 'up' && canScrollUp) { pendingScrollsRef.current.set(candidate.id, pendingDelta + delta); scheduleFlush(); - return; + return true; } if (direction === 'down' && canScrollDown) { pendingScrollsRef.current.set(candidate.id, pendingDelta + delta); scheduleFlush(); - return; + return true; } } + return false; }; const handleLeftPress = (mouseEvent: MouseEvent) => { @@ -238,7 +239,7 @@ export const ScrollProvider: React.FC<{ children: React.ReactNode }> = ({ id: entry.id, offset, }; - return; + return true; } } @@ -250,21 +251,27 @@ export const ScrollProvider: React.FC<{ children: React.ReactNode }> = ({ if (candidates.length > 0) { // The first candidate is the innermost one. candidates[0].flashScrollbar(); + // We don't consider just flashing the scrollbar as handling the event + // in a way that should prevent other handlers (like drag warning) + // from checking it, although for left-press it doesn't matter much. + // But returning false is safer. + return false; } + return false; }; const handleMove = (mouseEvent: MouseEvent) => { const state = dragStateRef.current; - if (!state.active || !state.id) return; + if (!state.active || !state.id) return false; const entry = scrollablesRef.current.get(state.id); if (!entry || !entry.ref.current) { state.active = false; - return; + return false; } const boundingBox = getBoundingBox(entry.ref.current); - if (!boundingBox) return; + if (!boundingBox) return false; const { y } = boundingBox; const { scrollTop, scrollHeight, innerHeight } = entry.getScrollState(); @@ -276,7 +283,7 @@ export const ScrollProvider: React.FC<{ children: React.ReactNode }> = ({ const maxScrollTop = scrollHeight - innerHeight; const maxThumbY = innerHeight - thumbHeight; - if (maxThumbY <= 0) return; + if (maxThumbY <= 0) return false; const relativeMouseY = mouseEvent.row - y; // Calculate the target thumb position based on the mouse position and the offset. @@ -295,6 +302,7 @@ export const ScrollProvider: React.FC<{ children: React.ReactNode }> = ({ } else { entry.scrollBy(targetScrollTop - scrollTop); } + return true; }; const handleLeftRelease = () => { @@ -304,22 +312,25 @@ export const ScrollProvider: React.FC<{ children: React.ReactNode }> = ({ id: null, offset: 0, }; + return true; } + return false; }; useMouse( (event: MouseEvent) => { if (event.name === 'scroll-up') { - handleScroll('up', event); + return handleScroll('up', event); } else if (event.name === 'scroll-down') { - handleScroll('down', event); + return handleScroll('down', event); } else if (event.name === 'left-press') { - handleLeftPress(event); + return handleLeftPress(event); } else if (event.name === 'move') { - handleMove(event); + return handleMove(event); } else if (event.name === 'left-release') { - handleLeftRelease(); + return handleLeftRelease(); } + return false; }, { isActive: true }, ); diff --git a/packages/cli/src/ui/contexts/UIStateContext.tsx b/packages/cli/src/ui/contexts/UIStateContext.tsx index 7812ea34316..6fc19b2808d 100644 --- a/packages/cli/src/ui/contexts/UIStateContext.tsx +++ b/packages/cli/src/ui/contexts/UIStateContext.tsx @@ -124,6 +124,7 @@ export interface UIState { showDebugProfiler: boolean; showFullTodos: boolean; copyModeEnabled: boolean; + selectionWarning: boolean; } export const UIStateContext = createContext(null); diff --git a/packages/cli/src/ui/hooks/slashCommandProcessor.test.tsx b/packages/cli/src/ui/hooks/slashCommandProcessor.test.tsx index 99afe37b103..503fc7e7188 100644 --- a/packages/cli/src/ui/hooks/slashCommandProcessor.test.tsx +++ b/packages/cli/src/ui/hooks/slashCommandProcessor.test.tsx @@ -28,8 +28,27 @@ import { } from '@google/gemini-cli-core'; import { appEvents } from '../../utils/events.js'; -const { logSlashCommand } = vi.hoisted(() => ({ +const { + logSlashCommand, + mockBuiltinLoadCommands, + mockFileLoadCommands, + mockMcpLoadCommands, + mockIdeClientGetInstance, + mockUseAlternateBuffer, +} = vi.hoisted(() => ({ logSlashCommand: vi.fn(), + mockBuiltinLoadCommands: vi.fn().mockResolvedValue([]), + mockFileLoadCommands: vi.fn().mockResolvedValue([]), + mockMcpLoadCommands: vi.fn().mockResolvedValue([]), + mockIdeClientGetInstance: vi.fn().mockResolvedValue({ + addStatusChangeListener: vi.fn(), + removeStatusChangeListener: vi.fn(), + }), + mockUseAlternateBuffer: vi.fn().mockReturnValue(false), +})); + +vi.mock('./useAlternateBuffer.js', () => ({ + useAlternateBuffer: mockUseAlternateBuffer, })); vi.mock('@google/gemini-cli-core', async (importOriginal) => { @@ -41,10 +60,7 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => { logSlashCommand, getIdeInstaller: vi.fn().mockReturnValue(null), IdeClient: { - getInstance: vi.fn().mockResolvedValue({ - addStatusChangeListener: vi.fn(), - removeStatusChangeListener: vi.fn(), - }), + getInstance: mockIdeClientGetInstance, }, }; }); @@ -65,23 +81,20 @@ vi.mock('node:process', () => { }; }); -const mockBuiltinLoadCommands = vi.fn(); vi.mock('../../services/BuiltinCommandLoader.js', () => ({ - BuiltinCommandLoader: vi.fn().mockImplementation(() => ({ + BuiltinCommandLoader: vi.fn(() => ({ loadCommands: mockBuiltinLoadCommands, })), })); -const mockFileLoadCommands = vi.fn(); vi.mock('../../services/FileCommandLoader.js', () => ({ - FileCommandLoader: vi.fn().mockImplementation(() => ({ + FileCommandLoader: vi.fn(() => ({ loadCommands: mockFileLoadCommands, })), })); -const mockMcpLoadCommands = vi.fn(); vi.mock('../../services/McpPromptLoader.js', () => ({ - McpPromptLoader: vi.fn().mockImplementation(() => ({ + McpPromptLoader: vi.fn(() => ({ loadCommands: mockMcpLoadCommands, })), })); @@ -130,6 +143,12 @@ describe('useSlashCommandProcessor', () => { mockBuiltinLoadCommands.mockResolvedValue([]); mockFileLoadCommands.mockResolvedValue([]); mockMcpLoadCommands.mockResolvedValue([]); + mockUseAlternateBuffer.mockReturnValue(false); + mockIdeClientGetInstance.mockResolvedValue({ + addStatusChangeListener: vi.fn(), + removeStatusChangeListener: vi.fn(), + }); + vi.spyOn(console, 'clear').mockImplementation(() => {}); }); afterEach(async () => { @@ -137,6 +156,7 @@ describe('useSlashCommandProcessor', () => { await unmountHook(); unmountHook = undefined; } + vi.restoreAllMocks(); }); const setupProcessorHook = async ( @@ -205,6 +225,44 @@ describe('useSlashCommandProcessor', () => { }; }; + describe('Console Clear Safety', () => { + it('should not call console.clear if alternate buffer is active', async () => { + mockUseAlternateBuffer.mockReturnValue(true); + const clearCommand = createTestCommand({ + name: 'clear', + action: async (context) => { + context.ui.clear(); + }, + }); + const result = await setupProcessorHook([clearCommand]); + + await act(async () => { + await result.current.handleSlashCommand('/clear'); + }); + + expect(mockClearItems).toHaveBeenCalled(); + expect(console.clear).not.toHaveBeenCalled(); + }); + + it('should call console.clear if alternate buffer is not active', async () => { + mockUseAlternateBuffer.mockReturnValue(false); + const clearCommand = createTestCommand({ + name: 'clear', + action: async (context) => { + context.ui.clear(); + }, + }); + const result = await setupProcessorHook([clearCommand]); + + await act(async () => { + await result.current.handleSlashCommand('/clear'); + }); + + expect(mockClearItems).toHaveBeenCalled(); + expect(console.clear).toHaveBeenCalled(); + }); + }); + describe('Initialization and Command Loading', () => { it('should initialize CommandService with all required loaders', async () => { await setupProcessorHook(); @@ -947,36 +1005,37 @@ describe('useSlashCommandProcessor', () => { describe('Slash Command Logging', () => { const mockCommandAction = vi.fn().mockResolvedValue({ type: 'handled' }); - const loggingTestCommands: SlashCommand[] = [ - createTestCommand({ - name: 'logtest', - action: vi - .fn() - .mockResolvedValue({ type: 'message', content: 'hello world' }), - }), - createTestCommand({ - name: 'logwithsub', - subCommands: [ - createTestCommand({ - name: 'sub', - action: mockCommandAction, - }), - ], - }), - createTestCommand({ - name: 'fail', - action: vi.fn().mockRejectedValue(new Error('oh no!')), - }), - createTestCommand({ - name: 'logalias', - altNames: ['la'], - action: mockCommandAction, - }), - ]; + let loggingTestCommands: SlashCommand[]; beforeEach(() => { mockCommandAction.mockClear(); vi.mocked(logSlashCommand).mockClear(); + loggingTestCommands = [ + createTestCommand({ + name: 'logtest', + action: vi + .fn() + .mockResolvedValue({ type: 'message', content: 'hello world' }), + }), + createTestCommand({ + name: 'logwithsub', + subCommands: [ + createTestCommand({ + name: 'sub', + action: mockCommandAction, + }), + ], + }), + createTestCommand({ + name: 'fail', + action: vi.fn().mockRejectedValue(new Error('oh no!')), + }), + createTestCommand({ + name: 'logalias', + altNames: ['la'], + action: mockCommandAction, + }), + ]; }); it.each([ diff --git a/packages/cli/src/ui/hooks/slashCommandProcessor.ts b/packages/cli/src/ui/hooks/slashCommandProcessor.ts index 692869f167d..47d9ea7f107 100644 --- a/packages/cli/src/ui/hooks/slashCommandProcessor.ts +++ b/packages/cli/src/ui/hooks/slashCommandProcessor.ts @@ -44,6 +44,7 @@ import { type ExtensionUpdateStatus, } from '../state/extensions.js'; import { appEvents } from '../../utils/events.js'; +import { useAlternateBuffer } from './useAlternateBuffer.js'; interface SlashCommandProcessorActions { openAuthDialog: () => void; @@ -81,6 +82,7 @@ export const useSlashCommandProcessor = ( const [commands, setCommands] = useState( undefined, ); + const alternateBuffer = useAlternateBuffer(); const [reloadTrigger, setReloadTrigger] = useState(0); const reloadCommands = useCallback(() => { @@ -196,7 +198,9 @@ export const useSlashCommandProcessor = ( addItem, clear: () => { clearItems(); - console.clear(); + if (!alternateBuffer) { + console.clear(); + } refreshStatic(); }, loadHistory, @@ -218,6 +222,7 @@ export const useSlashCommandProcessor = ( }, }), [ + alternateBuffer, config, settings, gitService, diff --git a/packages/cli/src/ui/utils/mouse.ts b/packages/cli/src/ui/utils/mouse.ts index 1632c64c74d..e9381273ba1 100644 --- a/packages/cli/src/ui/utils/mouse.ts +++ b/packages/cli/src/ui/utils/mouse.ts @@ -35,7 +35,7 @@ export interface MouseEvent { ctrl: boolean; } -export type MouseHandler = (event: MouseEvent) => void; +export type MouseHandler = (event: MouseEvent) => void | boolean; export function getMouseEventName( buttonCode: number, diff --git a/packages/cli/src/utils/events.ts b/packages/cli/src/utils/events.ts index ac714fd8e68..50415d150f2 100644 --- a/packages/cli/src/utils/events.ts +++ b/packages/cli/src/utils/events.ts @@ -13,6 +13,7 @@ export enum AppEvent { OauthDisplayMessage = 'oauth-display-message', Flicker = 'flicker', McpClientUpdate = 'mcp-client-update', + SelectionWarning = 'selection-warning', } export interface AppEvents extends ExtensionEvents { @@ -21,6 +22,7 @@ export interface AppEvents extends ExtensionEvents { [AppEvent.OauthDisplayMessage]: string[]; [AppEvent.Flicker]: never[]; [AppEvent.McpClientUpdate]: Array | never>; + [AppEvent.SelectionWarning]: never[]; } export const appEvents = new EventEmitter(); From ea4cd98e2d2b408f4b101313ee97791295ef10cf Mon Sep 17 00:00:00 2001 From: gemini-cli-robot Date: Fri, 14 Nov 2025 21:27:03 +0000 Subject: [PATCH 03/13] chore(release): v0.16.0-preview.1 --- package-lock.json | 14 +++++++------- package.json | 4 ++-- packages/a2a-server/package.json | 2 +- packages/cli/package.json | 4 ++-- packages/core/package.json | 2 +- packages/test-utils/package.json | 2 +- packages/vscode-ide-companion/package.json | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3c1d1520882..7d9e365810f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@google/gemini-cli", - "version": "0.16.0-preview.0", + "version": "0.16.0-preview.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@google/gemini-cli", - "version": "0.16.0-preview.0", + "version": "0.16.0-preview.1", "workspaces": [ "packages/*" ], @@ -16960,7 +16960,7 @@ }, "packages/a2a-server": { "name": "@google/gemini-cli-a2a-server", - "version": "0.16.0-preview.0", + "version": "0.16.0-preview.1", "dependencies": { "@a2a-js/sdk": "^0.3.2", "@google-cloud/storage": "^7.16.0", @@ -17250,7 +17250,7 @@ }, "packages/cli": { "name": "@google/gemini-cli", - "version": "0.16.0-preview.0", + "version": "0.16.0-preview.1", "dependencies": { "@google/gemini-cli-core": "file:../core", "@google/genai": "1.16.0", @@ -17350,7 +17350,7 @@ }, "packages/core": { "name": "@google/gemini-cli-core", - "version": "0.16.0-preview.0", + "version": "0.16.0-preview.1", "dependencies": { "@google-cloud/logging": "^11.2.1", "@google-cloud/opentelemetry-cloud-monitoring-exporter": "^0.21.0", @@ -17494,7 +17494,7 @@ }, "packages/test-utils": { "name": "@google/gemini-cli-test-utils", - "version": "0.16.0-preview.0", + "version": "0.16.0-preview.1", "license": "Apache-2.0", "devDependencies": { "typescript": "^5.3.3" @@ -17505,7 +17505,7 @@ }, "packages/vscode-ide-companion": { "name": "gemini-cli-vscode-ide-companion", - "version": "0.16.0-preview.0", + "version": "0.16.0-preview.1", "license": "LICENSE", "dependencies": { "@modelcontextprotocol/sdk": "^1.15.1", diff --git a/package.json b/package.json index eaf8b2f82d5..69e02e7b3dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli", - "version": "0.16.0-preview.0", + "version": "0.16.0-preview.1", "engines": { "node": ">=20.0.0" }, @@ -14,7 +14,7 @@ "url": "git+https://github.com/google-gemini/gemini-cli.git" }, "config": { - "sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.16.0-preview.0" + "sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.16.0-preview.1" }, "scripts": { "start": "cross-env NODE_ENV=development node scripts/start.js", diff --git a/packages/a2a-server/package.json b/packages/a2a-server/package.json index b4df21b772f..85f6494e1dd 100644 --- a/packages/a2a-server/package.json +++ b/packages/a2a-server/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli-a2a-server", - "version": "0.16.0-preview.0", + "version": "0.16.0-preview.1", "description": "Gemini CLI A2A Server", "repository": { "type": "git", diff --git a/packages/cli/package.json b/packages/cli/package.json index 9bdcb2393ac..7533ce9fbbb 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli", - "version": "0.16.0-preview.0", + "version": "0.16.0-preview.1", "description": "Gemini CLI", "repository": { "type": "git", @@ -25,7 +25,7 @@ "dist" ], "config": { - "sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.16.0-preview.0" + "sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.16.0-preview.1" }, "dependencies": { "@google/gemini-cli-core": "file:../core", diff --git a/packages/core/package.json b/packages/core/package.json index c1f8c689221..dc40877542f 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli-core", - "version": "0.16.0-preview.0", + "version": "0.16.0-preview.1", "description": "Gemini CLI Core", "repository": { "type": "git", diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 908aa3d1627..43c85b4754e 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli-test-utils", - "version": "0.16.0-preview.0", + "version": "0.16.0-preview.1", "private": true, "main": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/vscode-ide-companion/package.json b/packages/vscode-ide-companion/package.json index 78990ee59ce..a44df28d673 100644 --- a/packages/vscode-ide-companion/package.json +++ b/packages/vscode-ide-companion/package.json @@ -2,7 +2,7 @@ "name": "gemini-cli-vscode-ide-companion", "displayName": "Gemini CLI Companion", "description": "Enable Gemini CLI with direct access to your IDE workspace.", - "version": "0.16.0-preview.0", + "version": "0.16.0-preview.1", "publisher": "google", "icon": "assets/icon.png", "repository": { From cc608b9a972b9ab71909e479af7d124ea876f418 Mon Sep 17 00:00:00 2001 From: gemini-cli-robot Date: Fri, 14 Nov 2025 14:01:55 -0800 Subject: [PATCH 04/13] fix(patch): cherry-pick ce56b4e to release/v0.16.0-preview.1-pr-13073 to patch version v0.16.0-preview.1 and create version 0.16.0-preview.2 (#13097) Co-authored-by: Adib234 <30782825+Adib234@users.noreply.github.com> --- .../code_assist/experiments/experiments.test.ts | 16 ++++++++-------- .../src/code_assist/experiments/experiments.ts | 4 ++-- .../src/code_assist/experiments/flagNames.ts | 5 ++--- .../core/src/code_assist/experiments/types.ts | 2 +- packages/core/src/config/config.ts | 4 ++-- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/packages/core/src/code_assist/experiments/experiments.test.ts b/packages/core/src/code_assist/experiments/experiments.test.ts index 393e648ccf6..a4d9c85fcea 100644 --- a/packages/core/src/code_assist/experiments/experiments.test.ts +++ b/packages/core/src/code_assist/experiments/experiments.test.ts @@ -42,8 +42,8 @@ describe('experiments', () => { const { getExperiments } = await import('./experiments.js'); const mockApiResponse: ListExperimentsResponse = { flags: [ - { name: 'flag1', boolValue: true }, - { name: 'flag2', stringValue: 'value' }, + { flagId: 234, boolValue: true }, + { flagId: 345, stringValue: 'value' }, ], experimentIds: [123, 456], }; @@ -58,12 +58,12 @@ describe('experiments', () => { ); // Verify that the response was parsed correctly - expect(experiments.flags['flag1']).toEqual({ - name: 'flag1', + expect(experiments.flags[234]).toEqual({ + flagId: 234, boolValue: true, }); - expect(experiments.flags['flag2']).toEqual({ - name: 'flag2', + expect(experiments.flags[345]).toEqual({ + flagId: 345, stringValue: 'value', }); expect(experiments.experimentIds).toEqual([123, 456]); @@ -85,7 +85,7 @@ describe('experiments', () => { const mockApiResponse: ListExperimentsResponse = { flags: [ { boolValue: true } as Flag, // No name - { name: 'flag2', stringValue: 'value' }, + { flagId: 256, stringValue: 'value' }, ], }; vi.mocked(mockServer.listExperiments).mockResolvedValue(mockApiResponse); @@ -93,7 +93,7 @@ describe('experiments', () => { const experiments = await getExperiments(mockServer); expect(Object.keys(experiments.flags)).toHaveLength(1); - expect(experiments.flags['flag2']).toBeDefined(); + expect(experiments.flags[256]).toBeDefined(); expect(experiments.flags['undefined']).toBeUndefined(); }); diff --git a/packages/core/src/code_assist/experiments/experiments.ts b/packages/core/src/code_assist/experiments/experiments.ts index f716da9c726..9c8d42188ff 100644 --- a/packages/core/src/code_assist/experiments/experiments.ts +++ b/packages/core/src/code_assist/experiments/experiments.ts @@ -38,8 +38,8 @@ export async function getExperiments( function parseExperiments(response: ListExperimentsResponse): Experiments { const flags: Record = {}; for (const flag of response.flags ?? []) { - if (flag.name) { - flags[flag.name] = flag; + if (flag.flagId) { + flags[flag.flagId] = flag; } } return { diff --git a/packages/core/src/code_assist/experiments/flagNames.ts b/packages/core/src/code_assist/experiments/flagNames.ts index cb6b6397a4a..5425a8e833f 100644 --- a/packages/core/src/code_assist/experiments/flagNames.ts +++ b/packages/core/src/code_assist/experiments/flagNames.ts @@ -5,9 +5,8 @@ */ export const ExperimentFlags = { - CONTEXT_COMPRESSION_THRESHOLD: - 'GeminiCLIContextCompression__threshold_fraction', - USER_CACHING: 'GcliUserCaching__user_caching', + CONTEXT_COMPRESSION_THRESHOLD: 45740197, + USER_CACHING: 45740198, } as const; export type ExperimentFlagName = diff --git a/packages/core/src/code_assist/experiments/types.ts b/packages/core/src/code_assist/experiments/types.ts index 924108f347b..510f3a7cbe8 100644 --- a/packages/core/src/code_assist/experiments/types.ts +++ b/packages/core/src/code_assist/experiments/types.ts @@ -19,7 +19,7 @@ export interface ListExperimentsResponse { } export interface Flag { - name?: string; + flagId?: number; boolValue?: boolean; floatValue?: number; intValue?: string; // int64 diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts index e9bf89bd757..84e9af8bdaf 100644 --- a/packages/core/src/config/config.ts +++ b/packages/core/src/config/config.ts @@ -1425,8 +1425,8 @@ export class Config { this.experiments = experiments; const flagSummaries = Object.entries(experiments.flags ?? {}) .sort(([a], [b]) => a.localeCompare(b)) - .map(([name, flag]) => { - const summary: Record = { name }; + .map(([flagId, flag]) => { + const summary: Record = { flagId }; if (flag.boolValue !== undefined) { summary['boolValue'] = flag.boolValue; } From dcc2a499351066e221eed64d0f39072741af267e Mon Sep 17 00:00:00 2001 From: gemini-cli-robot Date: Fri, 14 Nov 2025 22:16:31 +0000 Subject: [PATCH 05/13] chore(release): v0.16.0-preview.2 --- package-lock.json | 14 +++++++------- package.json | 4 ++-- packages/a2a-server/package.json | 2 +- packages/cli/package.json | 4 ++-- packages/core/package.json | 2 +- packages/test-utils/package.json | 2 +- packages/vscode-ide-companion/package.json | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7d9e365810f..0c9a6a5b907 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@google/gemini-cli", - "version": "0.16.0-preview.1", + "version": "0.16.0-preview.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@google/gemini-cli", - "version": "0.16.0-preview.1", + "version": "0.16.0-preview.2", "workspaces": [ "packages/*" ], @@ -16960,7 +16960,7 @@ }, "packages/a2a-server": { "name": "@google/gemini-cli-a2a-server", - "version": "0.16.0-preview.1", + "version": "0.16.0-preview.2", "dependencies": { "@a2a-js/sdk": "^0.3.2", "@google-cloud/storage": "^7.16.0", @@ -17250,7 +17250,7 @@ }, "packages/cli": { "name": "@google/gemini-cli", - "version": "0.16.0-preview.1", + "version": "0.16.0-preview.2", "dependencies": { "@google/gemini-cli-core": "file:../core", "@google/genai": "1.16.0", @@ -17350,7 +17350,7 @@ }, "packages/core": { "name": "@google/gemini-cli-core", - "version": "0.16.0-preview.1", + "version": "0.16.0-preview.2", "dependencies": { "@google-cloud/logging": "^11.2.1", "@google-cloud/opentelemetry-cloud-monitoring-exporter": "^0.21.0", @@ -17494,7 +17494,7 @@ }, "packages/test-utils": { "name": "@google/gemini-cli-test-utils", - "version": "0.16.0-preview.1", + "version": "0.16.0-preview.2", "license": "Apache-2.0", "devDependencies": { "typescript": "^5.3.3" @@ -17505,7 +17505,7 @@ }, "packages/vscode-ide-companion": { "name": "gemini-cli-vscode-ide-companion", - "version": "0.16.0-preview.1", + "version": "0.16.0-preview.2", "license": "LICENSE", "dependencies": { "@modelcontextprotocol/sdk": "^1.15.1", diff --git a/package.json b/package.json index 69e02e7b3dd..419d0c13aeb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli", - "version": "0.16.0-preview.1", + "version": "0.16.0-preview.2", "engines": { "node": ">=20.0.0" }, @@ -14,7 +14,7 @@ "url": "git+https://github.com/google-gemini/gemini-cli.git" }, "config": { - "sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.16.0-preview.1" + "sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.16.0-preview.2" }, "scripts": { "start": "cross-env NODE_ENV=development node scripts/start.js", diff --git a/packages/a2a-server/package.json b/packages/a2a-server/package.json index 85f6494e1dd..6846c7538d2 100644 --- a/packages/a2a-server/package.json +++ b/packages/a2a-server/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli-a2a-server", - "version": "0.16.0-preview.1", + "version": "0.16.0-preview.2", "description": "Gemini CLI A2A Server", "repository": { "type": "git", diff --git a/packages/cli/package.json b/packages/cli/package.json index 7533ce9fbbb..9f63058f82a 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli", - "version": "0.16.0-preview.1", + "version": "0.16.0-preview.2", "description": "Gemini CLI", "repository": { "type": "git", @@ -25,7 +25,7 @@ "dist" ], "config": { - "sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.16.0-preview.1" + "sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.16.0-preview.2" }, "dependencies": { "@google/gemini-cli-core": "file:../core", diff --git a/packages/core/package.json b/packages/core/package.json index dc40877542f..0328d3fbb23 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli-core", - "version": "0.16.0-preview.1", + "version": "0.16.0-preview.2", "description": "Gemini CLI Core", "repository": { "type": "git", diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 43c85b4754e..41605ba697f 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli-test-utils", - "version": "0.16.0-preview.1", + "version": "0.16.0-preview.2", "private": true, "main": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/vscode-ide-companion/package.json b/packages/vscode-ide-companion/package.json index a44df28d673..00b5396c234 100644 --- a/packages/vscode-ide-companion/package.json +++ b/packages/vscode-ide-companion/package.json @@ -2,7 +2,7 @@ "name": "gemini-cli-vscode-ide-companion", "displayName": "Gemini CLI Companion", "description": "Enable Gemini CLI with direct access to your IDE workspace.", - "version": "0.16.0-preview.1", + "version": "0.16.0-preview.2", "publisher": "google", "icon": "assets/icon.png", "repository": { From 6f34e25892504fc6ca2f821e8f0ae05ebb2ca1cc Mon Sep 17 00:00:00 2001 From: gemini-cli-robot Date: Fri, 14 Nov 2025 17:31:09 -0800 Subject: [PATCH 06/13] fix(patch): cherry-pick ab6b229 to release/v0.16.0-preview.2-pr-13101 to patch version v0.16.0-preview.2 and create version 0.16.0-preview.3 (#13110) Co-authored-by: Jacob Richman --- packages/cli/src/ui/contexts/MouseContext.tsx | 3 +- .../ui/contexts/ScrollProvider.drag.test.tsx | 12 +++++++ .../src/ui/contexts/ScrollProvider.test.tsx | 17 ++++++++++ packages/cli/src/ui/utils/mouse.test.ts | 5 +++ packages/cli/src/ui/utils/mouse.ts | 31 ++++++++++++++++++- 5 files changed, 66 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/ui/contexts/MouseContext.tsx b/packages/cli/src/ui/contexts/MouseContext.tsx index fef5b8c343f..e8f723975fd 100644 --- a/packages/cli/src/ui/contexts/MouseContext.tsx +++ b/packages/cli/src/ui/contexts/MouseContext.tsx @@ -100,7 +100,8 @@ export function MouseProvider({ !handled && event.name === 'move' && event.col >= 0 && - event.row >= 0 + event.row >= 0 && + event.button === 'left' ) { // Terminal apps only receive mouse move events when the mouse is down // so this always indicates a mouse drag that the user was expecting diff --git a/packages/cli/src/ui/contexts/ScrollProvider.drag.test.tsx b/packages/cli/src/ui/contexts/ScrollProvider.drag.test.tsx index 542e2807eb3..2ef66df10da 100644 --- a/packages/cli/src/ui/contexts/ScrollProvider.drag.test.tsx +++ b/packages/cli/src/ui/contexts/ScrollProvider.drag.test.tsx @@ -113,6 +113,7 @@ describe('ScrollProvider Drag', () => { shift: false, ctrl: false, meta: false, + button: 'left', }); } @@ -125,6 +126,7 @@ describe('ScrollProvider Drag', () => { shift: false, ctrl: false, meta: false, + button: 'left', }); } @@ -142,6 +144,7 @@ describe('ScrollProvider Drag', () => { shift: false, ctrl: false, meta: false, + button: 'left', }); } @@ -159,6 +162,7 @@ describe('ScrollProvider Drag', () => { shift: false, ctrl: false, meta: false, + button: 'left', }); } @@ -172,6 +176,7 @@ describe('ScrollProvider Drag', () => { shift: false, ctrl: false, meta: false, + button: 'none', }); } expect(scrollBy).not.toHaveBeenCalled(); @@ -209,6 +214,7 @@ describe('ScrollProvider Drag', () => { shift: false, ctrl: false, meta: false, + button: 'left', }); } @@ -231,6 +237,7 @@ describe('ScrollProvider Drag', () => { shift: false, ctrl: false, meta: false, + button: 'left', }); } @@ -268,6 +275,7 @@ describe('ScrollProvider Drag', () => { shift: false, ctrl: false, meta: false, + button: 'left', }); } @@ -305,6 +313,7 @@ describe('ScrollProvider Drag', () => { shift: false, ctrl: false, meta: false, + button: 'left', }); } @@ -342,6 +351,7 @@ describe('ScrollProvider Drag', () => { shift: false, ctrl: false, meta: false, + button: 'left', }); } @@ -407,6 +417,7 @@ describe('ScrollProvider Drag', () => { shift: false, ctrl: false, meta: false, + button: 'left', }); } @@ -424,6 +435,7 @@ describe('ScrollProvider Drag', () => { shift: false, ctrl: false, meta: false, + button: 'left', }); } // Expect scrollTo to be called with target and duration 0 diff --git a/packages/cli/src/ui/contexts/ScrollProvider.test.tsx b/packages/cli/src/ui/contexts/ScrollProvider.test.tsx index df1906b6102..021e10e280a 100644 --- a/packages/cli/src/ui/contexts/ScrollProvider.test.tsx +++ b/packages/cli/src/ui/contexts/ScrollProvider.test.tsx @@ -110,6 +110,7 @@ describe('ScrollProvider', () => { shift: false, ctrl: false, meta: false, + button: 'none', }) === true ) { handled = true; @@ -147,6 +148,7 @@ describe('ScrollProvider', () => { shift: false, ctrl: false, meta: false, + button: 'none', }) === true ) { handled = true; @@ -193,6 +195,7 @@ describe('ScrollProvider', () => { shift: false, ctrl: false, meta: false, + button: 'left', }); } @@ -226,6 +229,7 @@ describe('ScrollProvider', () => { shift: false, ctrl: false, meta: false, + button: 'left', }); } @@ -258,6 +262,7 @@ describe('ScrollProvider', () => { shift: false, ctrl: false, meta: false, + button: 'none', }; for (const callback of mockUseMouseCallbacks) { callback(mouseEvent); @@ -303,6 +308,7 @@ describe('ScrollProvider', () => { shift: false, ctrl: false, meta: false, + button: 'none', }); callback({ name: 'scroll-down', @@ -311,6 +317,7 @@ describe('ScrollProvider', () => { shift: false, ctrl: false, meta: false, + button: 'none', }); callback({ name: 'scroll-up', @@ -319,6 +326,7 @@ describe('ScrollProvider', () => { shift: false, ctrl: false, meta: false, + button: 'none', }); } @@ -358,6 +366,7 @@ describe('ScrollProvider', () => { shift: false, ctrl: false, meta: false, + button: 'none', }); callback({ name: 'scroll-down', @@ -366,6 +375,7 @@ describe('ScrollProvider', () => { shift: false, ctrl: false, meta: false, + button: 'none', }); callback({ name: 'scroll-down', @@ -374,6 +384,7 @@ describe('ScrollProvider', () => { shift: false, ctrl: false, meta: false, + button: 'none', }); } @@ -417,6 +428,7 @@ describe('ScrollProvider', () => { shift: false, ctrl: false, meta: false, + button: 'left', }); } @@ -429,6 +441,7 @@ describe('ScrollProvider', () => { shift: false, ctrl: false, meta: false, + button: 'left', }); } @@ -441,6 +454,7 @@ describe('ScrollProvider', () => { shift: false, ctrl: false, meta: false, + button: 'left', }); } @@ -475,6 +489,7 @@ describe('ScrollProvider', () => { shift: false, ctrl: false, meta: false, + button: 'left', }); } @@ -487,6 +502,7 @@ describe('ScrollProvider', () => { shift: false, ctrl: false, meta: false, + button: 'left', }); } @@ -498,6 +514,7 @@ describe('ScrollProvider', () => { shift: false, ctrl: false, meta: false, + button: 'left', }); } diff --git a/packages/cli/src/ui/utils/mouse.test.ts b/packages/cli/src/ui/utils/mouse.test.ts index 66882a9d8d3..faec8ff4d14 100644 --- a/packages/cli/src/ui/utils/mouse.test.ts +++ b/packages/cli/src/ui/utils/mouse.test.ts @@ -27,6 +27,7 @@ describe('mouse utils', () => { shift: false, meta: false, ctrl: false, + button: 'left', }); expect(result!.length).toBe(input.length); }); @@ -43,6 +44,7 @@ describe('mouse utils', () => { shift: false, meta: false, ctrl: false, + button: 'left', }); }); @@ -58,6 +60,7 @@ describe('mouse utils', () => { shift: true, meta: true, ctrl: true, + button: 'left', }); }); @@ -67,6 +70,7 @@ describe('mouse utils', () => { const result = parseSGRMouseEvent(input); expect(result).not.toBeNull(); expect(result!.event.name).toBe('move'); + expect(result!.event.button).toBe('left'); }); it('parses SGR scroll events', () => { @@ -100,6 +104,7 @@ describe('mouse utils', () => { shift: false, meta: false, ctrl: false, + button: 'left', }); expect(result!.length).toBe(6); }); diff --git a/packages/cli/src/ui/utils/mouse.ts b/packages/cli/src/ui/utils/mouse.ts index e9381273ba1..9f49f68ee63 100644 --- a/packages/cli/src/ui/utils/mouse.ts +++ b/packages/cli/src/ui/utils/mouse.ts @@ -33,6 +33,7 @@ export interface MouseEvent { shift: boolean; meta: boolean; ctrl: boolean; + button: 'left' | 'middle' | 'right' | 'none'; } export type MouseHandler = (event: MouseEvent) => void | boolean; @@ -71,6 +72,20 @@ export function getMouseEventName( } } +function getButtonFromCode(code: number): MouseEvent['button'] { + const button = code & 3; + switch (button) { + case 0: + return 'left'; + case 1: + return 'middle'; + case 2: + return 'right'; + default: + return 'none'; + } +} + export function parseSGRMouseEvent( buffer: string, ): { event: MouseEvent; length: number } | null { @@ -98,6 +113,7 @@ export function parseSGRMouseEvent( shift, col, row, + button: getButtonFromCode(buttonCode), }, length: match[0].length, }; @@ -165,8 +181,21 @@ export function parseX11MouseEvent( } if (name) { + let button = getButtonFromCode(b); + if (name === 'left-release' && button === 'none') { + button = 'left'; + } + return { - event: { name, ctrl, meta, shift, col, row }, + event: { + name, + ctrl, + meta, + shift, + col, + row, + button, + }, length: match[0].length, }; } From a2b66aead35e3af8bc229a36d8e08e87a85f6f85 Mon Sep 17 00:00:00 2001 From: gemini-cli-robot Date: Sat, 15 Nov 2025 01:44:58 +0000 Subject: [PATCH 07/13] chore(release): v0.16.0-preview.3 --- package-lock.json | 14 +++++++------- package.json | 4 ++-- packages/a2a-server/package.json | 2 +- packages/cli/package.json | 4 ++-- packages/core/package.json | 2 +- packages/test-utils/package.json | 2 +- packages/vscode-ide-companion/package.json | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0c9a6a5b907..aec4b7e6949 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@google/gemini-cli", - "version": "0.16.0-preview.2", + "version": "0.16.0-preview.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@google/gemini-cli", - "version": "0.16.0-preview.2", + "version": "0.16.0-preview.3", "workspaces": [ "packages/*" ], @@ -16960,7 +16960,7 @@ }, "packages/a2a-server": { "name": "@google/gemini-cli-a2a-server", - "version": "0.16.0-preview.2", + "version": "0.16.0-preview.3", "dependencies": { "@a2a-js/sdk": "^0.3.2", "@google-cloud/storage": "^7.16.0", @@ -17250,7 +17250,7 @@ }, "packages/cli": { "name": "@google/gemini-cli", - "version": "0.16.0-preview.2", + "version": "0.16.0-preview.3", "dependencies": { "@google/gemini-cli-core": "file:../core", "@google/genai": "1.16.0", @@ -17350,7 +17350,7 @@ }, "packages/core": { "name": "@google/gemini-cli-core", - "version": "0.16.0-preview.2", + "version": "0.16.0-preview.3", "dependencies": { "@google-cloud/logging": "^11.2.1", "@google-cloud/opentelemetry-cloud-monitoring-exporter": "^0.21.0", @@ -17494,7 +17494,7 @@ }, "packages/test-utils": { "name": "@google/gemini-cli-test-utils", - "version": "0.16.0-preview.2", + "version": "0.16.0-preview.3", "license": "Apache-2.0", "devDependencies": { "typescript": "^5.3.3" @@ -17505,7 +17505,7 @@ }, "packages/vscode-ide-companion": { "name": "gemini-cli-vscode-ide-companion", - "version": "0.16.0-preview.2", + "version": "0.16.0-preview.3", "license": "LICENSE", "dependencies": { "@modelcontextprotocol/sdk": "^1.15.1", diff --git a/package.json b/package.json index 419d0c13aeb..88e82d937e7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli", - "version": "0.16.0-preview.2", + "version": "0.16.0-preview.3", "engines": { "node": ">=20.0.0" }, @@ -14,7 +14,7 @@ "url": "git+https://github.com/google-gemini/gemini-cli.git" }, "config": { - "sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.16.0-preview.2" + "sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.16.0-preview.3" }, "scripts": { "start": "cross-env NODE_ENV=development node scripts/start.js", diff --git a/packages/a2a-server/package.json b/packages/a2a-server/package.json index 6846c7538d2..baa940b60b8 100644 --- a/packages/a2a-server/package.json +++ b/packages/a2a-server/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli-a2a-server", - "version": "0.16.0-preview.2", + "version": "0.16.0-preview.3", "description": "Gemini CLI A2A Server", "repository": { "type": "git", diff --git a/packages/cli/package.json b/packages/cli/package.json index 9f63058f82a..8a8d306a37f 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli", - "version": "0.16.0-preview.2", + "version": "0.16.0-preview.3", "description": "Gemini CLI", "repository": { "type": "git", @@ -25,7 +25,7 @@ "dist" ], "config": { - "sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.16.0-preview.2" + "sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.16.0-preview.3" }, "dependencies": { "@google/gemini-cli-core": "file:../core", diff --git a/packages/core/package.json b/packages/core/package.json index 0328d3fbb23..74bda2ffdde 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli-core", - "version": "0.16.0-preview.2", + "version": "0.16.0-preview.3", "description": "Gemini CLI Core", "repository": { "type": "git", diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 41605ba697f..33a5006ee4a 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli-test-utils", - "version": "0.16.0-preview.2", + "version": "0.16.0-preview.3", "private": true, "main": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/vscode-ide-companion/package.json b/packages/vscode-ide-companion/package.json index 00b5396c234..884a29fb6c6 100644 --- a/packages/vscode-ide-companion/package.json +++ b/packages/vscode-ide-companion/package.json @@ -2,7 +2,7 @@ "name": "gemini-cli-vscode-ide-companion", "displayName": "Gemini CLI Companion", "description": "Enable Gemini CLI with direct access to your IDE workspace.", - "version": "0.16.0-preview.2", + "version": "0.16.0-preview.3", "publisher": "google", "icon": "assets/icon.png", "repository": { From 47642b2e3b0b49504244eb606e6aab4e992952ee Mon Sep 17 00:00:00 2001 From: gemini-cli-robot Date: Sat, 15 Nov 2025 10:54:34 -0800 Subject: [PATCH 08/13] fix(patch): cherry-pick d03496b to release/v0.16.0-preview.3-pr-13099 to patch version v0.16.0-preview.3 and create version 0.16.0-preview.4 (#13143) Co-authored-by: Tommaso Sciortino --- packages/cli/src/ui/AppContainer.tsx | 22 ++++++++++++++----- packages/cli/src/ui/components/Composer.tsx | 6 ++--- .../cli/src/ui/contexts/KeypressContext.tsx | 10 +++++++-- .../cli/src/ui/contexts/UIStateContext.tsx | 2 +- packages/cli/src/utils/events.ts | 2 ++ 5 files changed, 29 insertions(+), 13 deletions(-) diff --git a/packages/cli/src/ui/AppContainer.tsx b/packages/cli/src/ui/AppContainer.tsx index 5eaa6922a5a..538203f0187 100644 --- a/packages/cli/src/ui/AppContainer.tsx +++ b/packages/cli/src/ui/AppContainer.tsx @@ -892,7 +892,7 @@ Logging in with Google... Please restart Gemini CLI to continue. >(); const [showEscapePrompt, setShowEscapePrompt] = useState(false); const [showIdeRestartPrompt, setShowIdeRestartPrompt] = useState(false); - const [selectionWarning, setSelectionWarning] = useState(false); + const [warningMessage, setWarningMessage] = useState(null); const { isFolderTrustDialogOpen, handleFolderTrustSelect, isRestarting } = useFolderTrust(settings, setIsTrustedFolder, historyManager.addItem); @@ -904,18 +904,28 @@ Logging in with Google... Please restart Gemini CLI to continue. useEffect(() => { let timeoutId: NodeJS.Timeout; - const handleSelectionWarning = () => { - setSelectionWarning(true); + + const handleWarning = (message: string) => { + setWarningMessage(message); if (timeoutId) { clearTimeout(timeoutId); } timeoutId = setTimeout(() => { - setSelectionWarning(false); + setWarningMessage(null); }, WARNING_PROMPT_DURATION_MS); }; + + const handleSelectionWarning = () => { + handleWarning('Press Ctrl-S to enter selection mode to copy text.'); + }; + const handlePasteTimeout = () => { + handleWarning('Paste Timed out. Possibly due to slow connection.'); + }; appEvents.on(AppEvent.SelectionWarning, handleSelectionWarning); + appEvents.on(AppEvent.PasteTimeout, handlePasteTimeout); return () => { appEvents.off(AppEvent.SelectionWarning, handleSelectionWarning); + appEvents.off(AppEvent.PasteTimeout, handlePasteTimeout); if (timeoutId) { clearTimeout(timeoutId); } @@ -1366,7 +1376,7 @@ Logging in with Google... Please restart Gemini CLI to continue. embeddedShellFocused, showDebugProfiler, copyModeEnabled, - selectionWarning, + warningMessage, }), [ isThemeDialogOpen, @@ -1452,7 +1462,7 @@ Logging in with Google... Please restart Gemini CLI to continue. apiKeyDefaultValue, authState, copyModeEnabled, - selectionWarning, + warningMessage, ], ); diff --git a/packages/cli/src/ui/components/Composer.tsx b/packages/cli/src/ui/components/Composer.tsx index 6654541816c..15558082a64 100644 --- a/packages/cli/src/ui/components/Composer.tsx +++ b/packages/cli/src/ui/components/Composer.tsx @@ -99,10 +99,8 @@ export const Composer = () => { Press Ctrl+C again to exit. - ) : uiState.selectionWarning ? ( - - Press Ctrl-S to enter selection mode to copy text. - + ) : uiState.warningMessage ? ( + {uiState.warningMessage} ) : uiState.ctrlDPressedOnce ? ( Press Ctrl+D again to exit. diff --git a/packages/cli/src/ui/contexts/KeypressContext.tsx b/packages/cli/src/ui/contexts/KeypressContext.tsx index 5354d66d38f..8be139a38f7 100644 --- a/packages/cli/src/ui/contexts/KeypressContext.tsx +++ b/packages/cli/src/ui/contexts/KeypressContext.tsx @@ -18,10 +18,11 @@ import { import { ESC } from '../utils/input.js'; import { parseMouseEvent } from '../utils/mouse.js'; import { FOCUS_IN, FOCUS_OUT } from '../hooks/useFocus.js'; +import { appEvents, AppEvent } from '../../utils/events.js'; export const BACKSLASH_ENTER_TIMEOUT = 5; export const ESC_TIMEOUT = 50; -export const PASTE_TIMEOUT = 50; +export const PASTE_TIMEOUT = 30_000; // Parse the key itself const KEY_INFO_MAP: Record< @@ -211,7 +212,12 @@ function bufferPaste( key = yield; clearTimeout(timeoutId); - if (key === null || key.name === 'paste-end') { + if (key === null) { + appEvents.emit(AppEvent.PasteTimeout); + break; + } + + if (key.name === 'paste-end') { break; } buffer += key.sequence; diff --git a/packages/cli/src/ui/contexts/UIStateContext.tsx b/packages/cli/src/ui/contexts/UIStateContext.tsx index 6fc19b2808d..4e41f2482c2 100644 --- a/packages/cli/src/ui/contexts/UIStateContext.tsx +++ b/packages/cli/src/ui/contexts/UIStateContext.tsx @@ -124,7 +124,7 @@ export interface UIState { showDebugProfiler: boolean; showFullTodos: boolean; copyModeEnabled: boolean; - selectionWarning: boolean; + warningMessage: string | null; } export const UIStateContext = createContext(null); diff --git a/packages/cli/src/utils/events.ts b/packages/cli/src/utils/events.ts index 50415d150f2..514c0039e05 100644 --- a/packages/cli/src/utils/events.ts +++ b/packages/cli/src/utils/events.ts @@ -14,6 +14,7 @@ export enum AppEvent { Flicker = 'flicker', McpClientUpdate = 'mcp-client-update', SelectionWarning = 'selection-warning', + PasteTimeout = 'paste-timeout', } export interface AppEvents extends ExtensionEvents { @@ -23,6 +24,7 @@ export interface AppEvents extends ExtensionEvents { [AppEvent.Flicker]: never[]; [AppEvent.McpClientUpdate]: Array | never>; [AppEvent.SelectionWarning]: never[]; + [AppEvent.PasteTimeout]: never[]; } export const appEvents = new EventEmitter(); From c9e4e571db77be637a7abffbb1e4cf46dd2f884f Mon Sep 17 00:00:00 2001 From: gemini-cli-robot Date: Sat, 15 Nov 2025 19:12:59 +0000 Subject: [PATCH 09/13] chore(release): v0.16.0-preview.4 --- package-lock.json | 14 +++++++------- package.json | 4 ++-- packages/a2a-server/package.json | 2 +- packages/cli/package.json | 4 ++-- packages/core/package.json | 2 +- packages/test-utils/package.json | 2 +- packages/vscode-ide-companion/package.json | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index aec4b7e6949..4f3a1998b84 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@google/gemini-cli", - "version": "0.16.0-preview.3", + "version": "0.16.0-preview.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@google/gemini-cli", - "version": "0.16.0-preview.3", + "version": "0.16.0-preview.4", "workspaces": [ "packages/*" ], @@ -16960,7 +16960,7 @@ }, "packages/a2a-server": { "name": "@google/gemini-cli-a2a-server", - "version": "0.16.0-preview.3", + "version": "0.16.0-preview.4", "dependencies": { "@a2a-js/sdk": "^0.3.2", "@google-cloud/storage": "^7.16.0", @@ -17250,7 +17250,7 @@ }, "packages/cli": { "name": "@google/gemini-cli", - "version": "0.16.0-preview.3", + "version": "0.16.0-preview.4", "dependencies": { "@google/gemini-cli-core": "file:../core", "@google/genai": "1.16.0", @@ -17350,7 +17350,7 @@ }, "packages/core": { "name": "@google/gemini-cli-core", - "version": "0.16.0-preview.3", + "version": "0.16.0-preview.4", "dependencies": { "@google-cloud/logging": "^11.2.1", "@google-cloud/opentelemetry-cloud-monitoring-exporter": "^0.21.0", @@ -17494,7 +17494,7 @@ }, "packages/test-utils": { "name": "@google/gemini-cli-test-utils", - "version": "0.16.0-preview.3", + "version": "0.16.0-preview.4", "license": "Apache-2.0", "devDependencies": { "typescript": "^5.3.3" @@ -17505,7 +17505,7 @@ }, "packages/vscode-ide-companion": { "name": "gemini-cli-vscode-ide-companion", - "version": "0.16.0-preview.3", + "version": "0.16.0-preview.4", "license": "LICENSE", "dependencies": { "@modelcontextprotocol/sdk": "^1.15.1", diff --git a/package.json b/package.json index 88e82d937e7..e71554cd57f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli", - "version": "0.16.0-preview.3", + "version": "0.16.0-preview.4", "engines": { "node": ">=20.0.0" }, @@ -14,7 +14,7 @@ "url": "git+https://github.com/google-gemini/gemini-cli.git" }, "config": { - "sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.16.0-preview.3" + "sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.16.0-preview.4" }, "scripts": { "start": "cross-env NODE_ENV=development node scripts/start.js", diff --git a/packages/a2a-server/package.json b/packages/a2a-server/package.json index baa940b60b8..702319e68c3 100644 --- a/packages/a2a-server/package.json +++ b/packages/a2a-server/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli-a2a-server", - "version": "0.16.0-preview.3", + "version": "0.16.0-preview.4", "description": "Gemini CLI A2A Server", "repository": { "type": "git", diff --git a/packages/cli/package.json b/packages/cli/package.json index 8a8d306a37f..ba442025ab0 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli", - "version": "0.16.0-preview.3", + "version": "0.16.0-preview.4", "description": "Gemini CLI", "repository": { "type": "git", @@ -25,7 +25,7 @@ "dist" ], "config": { - "sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.16.0-preview.3" + "sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.16.0-preview.4" }, "dependencies": { "@google/gemini-cli-core": "file:../core", diff --git a/packages/core/package.json b/packages/core/package.json index 74bda2ffdde..d47f75182da 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli-core", - "version": "0.16.0-preview.3", + "version": "0.16.0-preview.4", "description": "Gemini CLI Core", "repository": { "type": "git", diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 33a5006ee4a..19b1b453949 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@google/gemini-cli-test-utils", - "version": "0.16.0-preview.3", + "version": "0.16.0-preview.4", "private": true, "main": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/vscode-ide-companion/package.json b/packages/vscode-ide-companion/package.json index 884a29fb6c6..662e4e43e93 100644 --- a/packages/vscode-ide-companion/package.json +++ b/packages/vscode-ide-companion/package.json @@ -2,7 +2,7 @@ "name": "gemini-cli-vscode-ide-companion", "displayName": "Gemini CLI Companion", "description": "Enable Gemini CLI with direct access to your IDE workspace.", - "version": "0.16.0-preview.3", + "version": "0.16.0-preview.4", "publisher": "google", "icon": "assets/icon.png", "repository": { From 3cbb170aa62bf9e773f578005bbb089ae1d9f60b Mon Sep 17 00:00:00 2001 From: gemini-cli-robot Date: Mon, 17 Nov 2025 07:57:35 -0800 Subject: [PATCH 10/13] fix(patch): cherry-pick 78a28bf to release/v0.16.0-preview.4-pr-13188 to patch version v0.16.0-preview.4 and create version 0.16.0-preview.5 (#13229) Co-authored-by: Jacob Richman --- packages/cli/src/ui/components/Footer.tsx | 12 +-- .../ui/components/GradientRegression.test.tsx | 94 +++++++++++++++++++ packages/cli/src/ui/components/Header.tsx | 25 +---- .../cli/src/ui/components/StatsDisplay.tsx | 14 +-- .../cli/src/ui/components/ThemedGradient.tsx | 32 +++++++ .../ui/hooks/useAnimatedScrollbar.test.tsx | 28 ++++++ .../cli/src/ui/hooks/useAnimatedScrollbar.ts | 11 ++- .../cli/src/ui/themes/color-utils.test.ts | 21 +++++ packages/cli/src/ui/themes/color-utils.ts | 9 ++ 9 files changed, 202 insertions(+), 44 deletions(-) create mode 100644 packages/cli/src/ui/components/GradientRegression.test.tsx create mode 100644 packages/cli/src/ui/components/ThemedGradient.tsx diff --git a/packages/cli/src/ui/components/Footer.tsx b/packages/cli/src/ui/components/Footer.tsx index 3e51acd740d..6a803a39eb2 100644 --- a/packages/cli/src/ui/components/Footer.tsx +++ b/packages/cli/src/ui/components/Footer.tsx @@ -10,7 +10,7 @@ import { theme } from '../semantic-colors.js'; import { shortenPath, tildeifyPath } from '@google/gemini-cli-core'; import { ConsoleSummaryDisplay } from './ConsoleSummaryDisplay.js'; import process from 'node:process'; -import Gradient from 'ink-gradient'; +import { ThemedGradient } from './ThemedGradient.js'; import { MemoryUsageDisplay } from './MemoryUsageDisplay.js'; import { ContextUsageDisplay } from './ContextUsageDisplay.js'; import { DebugProfiler } from './DebugProfiler.js'; @@ -87,12 +87,10 @@ export const Footer: React.FC = () => { )} {!hideCWD && (nightly ? ( - - - {displayPath} - {branchName && ({branchName}*)} - - + + {displayPath} + {branchName && ({branchName}*)} + ) : ( {displayPath} diff --git a/packages/cli/src/ui/components/GradientRegression.test.tsx b/packages/cli/src/ui/components/GradientRegression.test.tsx new file mode 100644 index 00000000000..1b4bc8a4f72 --- /dev/null +++ b/packages/cli/src/ui/components/GradientRegression.test.tsx @@ -0,0 +1,94 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { describe, it, expect, vi } from 'vitest'; +import { renderWithProviders } from '../../test-utils/render.js'; +import { Footer } from './Footer.js'; +import { StatsDisplay } from './StatsDisplay.js'; +import * as SessionContext from '../contexts/SessionContext.js'; +import type { SessionStatsState } from '../contexts/SessionContext.js'; + +// Mock the theme module +vi.mock('../semantic-colors.js', async (importOriginal) => { + const original = + await importOriginal(); + return { + ...original, + theme: { + ...original.theme, + ui: { + ...original.theme.ui, + gradient: [], // Empty array to potentially trigger the crash + }, + }, + }; +}); + +// Mock the context to provide controlled data for testing +vi.mock('../contexts/SessionContext.js', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + useSessionStats: vi.fn(), + }; +}); + +const mockSessionStats: SessionStatsState = { + sessionId: 'test-session', + sessionStartTime: new Date(), + lastPromptTokenCount: 0, + promptCount: 0, + metrics: { + models: {}, + tools: { + totalCalls: 0, + totalSuccess: 0, + totalFail: 0, + totalDurationMs: 0, + totalDecisions: { accept: 0, reject: 0, modify: 0, auto_accept: 0 }, + byName: {}, + }, + files: { totalLinesAdded: 0, totalLinesRemoved: 0 }, + }, +}; + +const useSessionStatsMock = vi.mocked(SessionContext.useSessionStats); +useSessionStatsMock.mockReturnValue({ + stats: mockSessionStats, + getPromptCount: () => 0, + startNewPrompt: vi.fn(), +}); + +describe('Gradient Crash Regression Tests', () => { + it('