mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-07-28 18:51:01 -07:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c593a29647 | |||
| cebe386d79 | |||
| 1c207e2f82 | |||
| ee87c98f43 | |||
| 75b5eeeb12 | |||
| 0fa9a54088 | |||
| 603e66b2ea | |||
| dc8fc75ac0 | |||
| 2b58605227 |
@@ -909,7 +909,7 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
- **`hooksConfig.enabled`** (boolean):
|
||||
- **Description:** Canonical toggle for the hooks system. When disabled, no
|
||||
hooks will be executed.
|
||||
- **Default:** `false`
|
||||
- **Default:** `true`
|
||||
|
||||
- **`hooksConfig.disabled`** (array):
|
||||
- **Description:** List of hook names (commands) that should be disabled.
|
||||
|
||||
Generated
+22
-33
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.26.0-nightly.20260115.6cb3ae4e0",
|
||||
"version": "0.26.0-preview.4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.26.0-nightly.20260115.6cb3ae4e0",
|
||||
"version": "0.26.0-preview.4",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
@@ -2474,7 +2474,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",
|
||||
@@ -2655,7 +2654,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"
|
||||
}
|
||||
@@ -2689,7 +2687,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"
|
||||
},
|
||||
@@ -3058,7 +3055,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"
|
||||
@@ -3092,7 +3088,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"
|
||||
@@ -3145,7 +3140,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",
|
||||
@@ -4358,7 +4352,6 @@
|
||||
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
@@ -4636,7 +4629,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",
|
||||
@@ -5641,7 +5633,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"
|
||||
},
|
||||
@@ -6086,7 +6077,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",
|
||||
@@ -7370,6 +7362,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"
|
||||
},
|
||||
@@ -8689,7 +8682,6 @@
|
||||
"integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
@@ -9292,6 +9284,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"
|
||||
}
|
||||
@@ -9301,6 +9294,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"
|
||||
}
|
||||
@@ -9310,6 +9304,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"
|
||||
}
|
||||
@@ -9563,6 +9558,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",
|
||||
@@ -9581,6 +9577,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"
|
||||
}
|
||||
@@ -9589,13 +9586,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"
|
||||
}
|
||||
@@ -10878,7 +10877,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@jrichman/ink/-/ink-6.4.7.tgz",
|
||||
"integrity": "sha512-QHyxhNF5VonF5cRmdAJD/UPucB9nRx3FozWMjQrDGfBxfAL9lpyu72/MlFPgloS1TMTGsOt7YN6dTPPA6mh0Aw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@alcalzone/ansi-tokenize": "^0.2.1",
|
||||
"ansi-escapes": "^7.0.0",
|
||||
@@ -14063,7 +14061,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",
|
||||
@@ -14640,7 +14639,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
|
||||
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -14651,7 +14649,6 @@
|
||||
"integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"shell-quote": "^1.6.1",
|
||||
"ws": "^7"
|
||||
@@ -16911,7 +16908,6 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -17135,8 +17131,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",
|
||||
@@ -17144,7 +17139,6 @@
|
||||
"integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "~0.25.0",
|
||||
"get-tsconfig": "^4.7.5"
|
||||
@@ -17328,7 +17322,6 @@
|
||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -17491,6 +17484,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"
|
||||
}
|
||||
@@ -17545,7 +17539,6 @@
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz",
|
||||
"integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.5.0",
|
||||
@@ -17659,7 +17652,6 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -17672,7 +17664,6 @@
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz",
|
||||
"integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/chai": "^5.2.2",
|
||||
"@vitest/expect": "3.2.4",
|
||||
@@ -18377,7 +18368,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"
|
||||
}
|
||||
@@ -18393,7 +18383,7 @@
|
||||
},
|
||||
"packages/a2a-server": {
|
||||
"name": "@google/gemini-cli-a2a-server",
|
||||
"version": "0.26.0-nightly.20260115.6cb3ae4e0",
|
||||
"version": "0.26.0-preview.4",
|
||||
"dependencies": {
|
||||
"@a2a-js/sdk": "^0.3.8",
|
||||
"@google-cloud/storage": "^7.16.0",
|
||||
@@ -18703,7 +18693,7 @@
|
||||
},
|
||||
"packages/cli": {
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.26.0-nightly.20260115.6cb3ae4e0",
|
||||
"version": "0.26.0-preview.4",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "^0.12.0",
|
||||
@@ -18807,7 +18797,7 @@
|
||||
},
|
||||
"packages/core": {
|
||||
"name": "@google/gemini-cli-core",
|
||||
"version": "0.26.0-nightly.20260115.6cb3ae4e0",
|
||||
"version": "0.26.0-preview.4",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@a2a-js/sdk": "^0.3.8",
|
||||
@@ -18944,7 +18934,6 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -18967,7 +18956,7 @@
|
||||
},
|
||||
"packages/test-utils": {
|
||||
"name": "@google/gemini-cli-test-utils",
|
||||
"version": "0.26.0-nightly.20260115.6cb3ae4e0",
|
||||
"version": "0.26.0-preview.4",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@google/gemini-cli-core": "file:../core",
|
||||
@@ -18984,7 +18973,7 @@
|
||||
},
|
||||
"packages/vscode-ide-companion": {
|
||||
"name": "gemini-cli-vscode-ide-companion",
|
||||
"version": "0.26.0-nightly.20260115.6cb3ae4e0",
|
||||
"version": "0.26.0-preview.4",
|
||||
"license": "LICENSE",
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.23.0",
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.26.0-nightly.20260115.6cb3ae4e0",
|
||||
"version": "0.26.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.26.0-nightly.20260115.6cb3ae4e0"
|
||||
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.26.0-preview.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "cross-env NODE_ENV=development node scripts/start.js",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli-a2a-server",
|
||||
"version": "0.26.0-nightly.20260115.6cb3ae4e0",
|
||||
"version": "0.26.0-preview.4",
|
||||
"description": "Gemini CLI A2A Server",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.26.0-nightly.20260115.6cb3ae4e0",
|
||||
"version": "0.26.0-preview.4",
|
||||
"description": "Gemini CLI",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
@@ -26,7 +26,7 @@
|
||||
"dist"
|
||||
],
|
||||
"config": {
|
||||
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.26.0-nightly.20260115.6cb3ae4e0"
|
||||
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.26.0-preview.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "^0.12.0",
|
||||
|
||||
@@ -511,8 +511,5 @@ describe('migrate command', () => {
|
||||
expect(debugLoggerLogSpy).toHaveBeenCalledWith(
|
||||
'\nMigration complete! Please review the migrated hooks in .gemini/settings.json',
|
||||
);
|
||||
expect(debugLoggerLogSpy).toHaveBeenCalledWith(
|
||||
'Note: Set hooksConfig.enabled to true in your settings to enable the hook system.',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -244,9 +244,6 @@ export async function handleMigrateFromClaude() {
|
||||
debugLogger.log(
|
||||
'\nMigration complete! Please review the migrated hooks in .gemini/settings.json',
|
||||
);
|
||||
debugLogger.log(
|
||||
'Note: Set hooksConfig.enabled to true in your settings to enable the hook system.',
|
||||
);
|
||||
} catch (error) {
|
||||
debugLogger.error(`Error saving migrated hooks: ${getErrorMessage(error)}`);
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
type HookEventName,
|
||||
type OutputFormat,
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
coreEvents,
|
||||
} from '@google/gemini-cli-core';
|
||||
import {
|
||||
type Settings,
|
||||
@@ -47,7 +48,6 @@ import {
|
||||
|
||||
import { loadSandboxConfig } from './sandboxConfig.js';
|
||||
import { resolvePath } from '../utils/resolvePath.js';
|
||||
import { appEvents } from '../utils/events.js';
|
||||
import { RESUME_LATEST } from '../utils/sessionUtils.js';
|
||||
|
||||
import { isWorkspaceTrusted } from './trustedFolders.js';
|
||||
@@ -450,7 +450,7 @@ export async function loadCliConfig(
|
||||
requestSetting: promptForSetting,
|
||||
workspaceDir: cwd,
|
||||
enabledExtensionOverrides: argv.extensions,
|
||||
eventEmitter: appEvents as EventEmitter<ExtensionEvents>,
|
||||
eventEmitter: coreEvents as EventEmitter<ExtensionEvents>,
|
||||
clientVersion: await getVersion(),
|
||||
});
|
||||
await extensionManager.loadExtensions();
|
||||
@@ -746,7 +746,7 @@ export async function loadCliConfig(
|
||||
truncateToolOutputThreshold: settings.tools?.truncateToolOutputThreshold,
|
||||
truncateToolOutputLines: settings.tools?.truncateToolOutputLines,
|
||||
enableToolOutputTruncation: settings.tools?.enableToolOutputTruncation,
|
||||
eventEmitter: appEvents,
|
||||
eventEmitter: coreEvents,
|
||||
useWriteTodos: argv.useWriteTodos ?? settings.useWriteTodos,
|
||||
output: {
|
||||
format: (argv.outputFormat ?? settings.output?.format) as OutputFormat,
|
||||
@@ -763,7 +763,7 @@ export async function loadCliConfig(
|
||||
// TODO: loading of hooks based on workspace trust
|
||||
enableHooks:
|
||||
(settings.tools?.enableHooks ?? true) &&
|
||||
(settings.hooksConfig?.enabled ?? false),
|
||||
(settings.hooksConfig?.enabled ?? true),
|
||||
enableHooksUI: settings.tools?.enableHooks ?? true,
|
||||
hooks: settings.hooks || {},
|
||||
disabledHooks: settings.hooksConfig?.disabled || [],
|
||||
|
||||
@@ -872,6 +872,7 @@ describe('extension tests', () => {
|
||||
);
|
||||
|
||||
const settings = loadSettings(tempWorkspaceDir).merged;
|
||||
settings.hooksConfig.enabled = false;
|
||||
|
||||
extensionManager = new ExtensionManager({
|
||||
workspaceDir: tempWorkspaceDir,
|
||||
|
||||
@@ -1646,7 +1646,7 @@ const SETTINGS_SCHEMA = {
|
||||
label: 'Enable Hooks',
|
||||
category: 'Advanced',
|
||||
requiresRestart: false,
|
||||
default: false,
|
||||
default: true,
|
||||
description:
|
||||
'Canonical toggle for the hooks system. When disabled, no hooks will be executed.',
|
||||
showInDialog: false,
|
||||
|
||||
@@ -1085,6 +1085,8 @@ describe('gemini.tsx main function exit codes', () => {
|
||||
vi.mocked(loadSandboxConfig).mockResolvedValue({} as any);
|
||||
vi.mocked(loadCliConfig).mockResolvedValue({
|
||||
refreshAuth: vi.fn().mockRejectedValue(new Error('Auth failed')),
|
||||
getRemoteAdminSettings: vi.fn().mockReturnValue(undefined),
|
||||
isInteractive: vi.fn().mockReturnValue(true),
|
||||
} as unknown as Config);
|
||||
vi.mocked(loadSettings).mockReturnValue(
|
||||
createMockSettings({
|
||||
|
||||
@@ -373,6 +373,7 @@ export async function main() {
|
||||
// Refresh auth to fetch remote admin settings from CCPA and before entering
|
||||
// the sandbox because the sandbox will interfere with the Oauth2 web
|
||||
// redirect.
|
||||
let initialAuthFailed = false;
|
||||
if (
|
||||
settings.merged.security.auth.selectedType &&
|
||||
!settings.merged.security.auth.useExternal
|
||||
@@ -400,8 +401,7 @@ export async function main() {
|
||||
}
|
||||
} catch (err) {
|
||||
debugLogger.error('Error authenticating:', err);
|
||||
await runExitCleanup();
|
||||
process.exit(ExitCodes.FATAL_AUTHENTICATION_ERROR);
|
||||
initialAuthFailed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,6 +427,10 @@ export async function main() {
|
||||
// another way to decouple refreshAuth from requiring a config.
|
||||
|
||||
if (sandboxConfig) {
|
||||
if (initialAuthFailed) {
|
||||
await runExitCleanup();
|
||||
process.exit(ExitCodes.FATAL_AUTHENTICATION_ERROR);
|
||||
}
|
||||
let stdinData = '';
|
||||
if (!process.stdin.isTTY) {
|
||||
stdinData = await readStdin();
|
||||
|
||||
@@ -103,6 +103,7 @@ import { RELAUNCH_EXIT_CODE } from '../utils/processUtils.js';
|
||||
import type { SessionInfo } from '../utils/sessionUtils.js';
|
||||
import { useMessageQueue } from './hooks/useMessageQueue.js';
|
||||
import { useAutoAcceptIndicator } from './hooks/useAutoAcceptIndicator.js';
|
||||
import { useMcpStatus } from './hooks/useMcpStatus.js';
|
||||
import { useSessionStats } from './contexts/SessionContext.js';
|
||||
import { useGitBranchName } from './hooks/useGitBranchName.js';
|
||||
import {
|
||||
@@ -129,6 +130,7 @@ import {
|
||||
} from './constants.js';
|
||||
import { LoginWithGoogleRestartDialog } from './auth/LoginWithGoogleRestartDialog.js';
|
||||
import { useInactivityTimer } from './hooks/useInactivityTimer.js';
|
||||
import { isSlashCommand } from './utils/commandUtils.js';
|
||||
|
||||
function isToolExecuting(pendingHistoryItems: HistoryItemWithoutId[]) {
|
||||
return pendingHistoryItems.some((item) => {
|
||||
@@ -861,6 +863,8 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
isActive: !embeddedShellFocused,
|
||||
});
|
||||
|
||||
const { isMcpReady } = useMcpStatus(config);
|
||||
|
||||
const {
|
||||
messageQueue,
|
||||
addMessage,
|
||||
@@ -871,6 +875,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
isConfigInitialized,
|
||||
streamingState,
|
||||
submitQuery,
|
||||
isMcpReady,
|
||||
});
|
||||
|
||||
cancelHandlerRef.current = useCallback(
|
||||
@@ -909,10 +914,31 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
|
||||
const handleFinalSubmit = useCallback(
|
||||
(submittedValue: string) => {
|
||||
addMessage(submittedValue);
|
||||
const isSlash = isSlashCommand(submittedValue.trim());
|
||||
const isIdle = streamingState === StreamingState.Idle;
|
||||
|
||||
if (isSlash || (isIdle && isMcpReady)) {
|
||||
void submitQuery(submittedValue);
|
||||
} else {
|
||||
// Check messageQueue.length === 0 to only notify on the first queued item
|
||||
if (isIdle && !isMcpReady && messageQueue.length === 0) {
|
||||
coreEvents.emitFeedback(
|
||||
'info',
|
||||
'Waiting for MCP servers to initialize... Slash commands are still available and prompts will be queued.',
|
||||
);
|
||||
}
|
||||
addMessage(submittedValue);
|
||||
}
|
||||
addInput(submittedValue); // Track input for up-arrow history
|
||||
},
|
||||
[addMessage, addInput],
|
||||
[
|
||||
addMessage,
|
||||
addInput,
|
||||
submitQuery,
|
||||
isMcpReady,
|
||||
streamingState,
|
||||
messageQueue.length,
|
||||
],
|
||||
);
|
||||
|
||||
const handleClearScreen = useCallback(() => {
|
||||
|
||||
@@ -212,6 +212,9 @@ describe('KeypressContext', () => {
|
||||
name: 'return',
|
||||
sequence: '\r',
|
||||
insertable: true,
|
||||
shift: true,
|
||||
meta: false,
|
||||
ctrl: false,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -158,6 +158,9 @@ function bufferFastReturn(keypressHandler: KeypressHandler): KeypressHandler {
|
||||
keypressHandler({
|
||||
...key,
|
||||
name: 'return',
|
||||
shift: true, // to make it a newline, not a submission
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
sequence: '\r',
|
||||
insertable: true,
|
||||
});
|
||||
|
||||
@@ -1962,73 +1962,6 @@ describe('useGeminiStream', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('MCP Discovery State', () => {
|
||||
it('should block non-slash command queries when discovery is in progress and servers exist', async () => {
|
||||
const mockMcpClientManager = {
|
||||
getDiscoveryState: vi
|
||||
.fn()
|
||||
.mockReturnValue(MCPDiscoveryState.IN_PROGRESS),
|
||||
getMcpServerCount: vi.fn().mockReturnValue(1),
|
||||
};
|
||||
mockConfig.getMcpClientManager = () => mockMcpClientManager as any;
|
||||
|
||||
const { result } = renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('test query');
|
||||
});
|
||||
|
||||
expect(coreEvents.emitFeedback).toHaveBeenCalledWith(
|
||||
'info',
|
||||
'Waiting for MCP servers to initialize... Slash commands are still available.',
|
||||
);
|
||||
expect(mockSendMessageStream).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should NOT block queries when discovery is NOT_STARTED but there are no servers', async () => {
|
||||
const mockMcpClientManager = {
|
||||
getDiscoveryState: vi
|
||||
.fn()
|
||||
.mockReturnValue(MCPDiscoveryState.NOT_STARTED),
|
||||
getMcpServerCount: vi.fn().mockReturnValue(0),
|
||||
};
|
||||
mockConfig.getMcpClientManager = () => mockMcpClientManager as any;
|
||||
|
||||
const { result } = renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('test query');
|
||||
});
|
||||
|
||||
expect(coreEvents.emitFeedback).not.toHaveBeenCalledWith(
|
||||
'info',
|
||||
'Waiting for MCP servers to initialize... Slash commands are still available.',
|
||||
);
|
||||
expect(mockSendMessageStream).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should NOT block slash commands even when discovery is in progress', async () => {
|
||||
const mockMcpClientManager = {
|
||||
getDiscoveryState: vi
|
||||
.fn()
|
||||
.mockReturnValue(MCPDiscoveryState.IN_PROGRESS),
|
||||
getMcpServerCount: vi.fn().mockReturnValue(1),
|
||||
};
|
||||
mockConfig.getMcpClientManager = () => mockMcpClientManager as any;
|
||||
|
||||
const { result } = renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('/help');
|
||||
});
|
||||
|
||||
expect(coreEvents.emitFeedback).not.toHaveBeenCalledWith(
|
||||
'info',
|
||||
'Waiting for MCP servers to initialize... Slash commands are still available.',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleFinishedEvent', () => {
|
||||
it('should add info message for MAX_TOKENS finish reason', async () => {
|
||||
// Setup mock to return a stream with MAX_TOKENS finish reason
|
||||
@@ -3182,68 +3115,4 @@ describe('useGeminiStream', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('MCP Server Initialization', () => {
|
||||
it('should allow slash commands to run while MCP servers are initializing', async () => {
|
||||
const mockMcpClientManager = {
|
||||
getDiscoveryState: vi
|
||||
.fn()
|
||||
.mockReturnValue(MCPDiscoveryState.IN_PROGRESS),
|
||||
getMcpServerCount: vi.fn().mockReturnValue(1),
|
||||
};
|
||||
mockConfig.getMcpClientManager = () => mockMcpClientManager as any;
|
||||
|
||||
const { result } = renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('/help');
|
||||
});
|
||||
|
||||
// Slash command should be handled, and no Gemini call should be made.
|
||||
expect(mockHandleSlashCommand).toHaveBeenCalledWith('/help');
|
||||
expect(coreEvents.emitFeedback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should block normal prompts and provide feedback while MCP servers are initializing', async () => {
|
||||
const mockMcpClientManager = {
|
||||
getDiscoveryState: vi
|
||||
.fn()
|
||||
.mockReturnValue(MCPDiscoveryState.IN_PROGRESS),
|
||||
getMcpServerCount: vi.fn().mockReturnValue(1),
|
||||
};
|
||||
mockConfig.getMcpClientManager = () => mockMcpClientManager as any;
|
||||
const { result } = renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('a normal prompt');
|
||||
});
|
||||
|
||||
// No slash command, no Gemini call, but feedback should be emitted.
|
||||
expect(mockHandleSlashCommand).not.toHaveBeenCalled();
|
||||
expect(mockSendMessageStream).not.toHaveBeenCalled();
|
||||
expect(coreEvents.emitFeedback).toHaveBeenCalledWith(
|
||||
'info',
|
||||
'Waiting for MCP servers to initialize... Slash commands are still available.',
|
||||
);
|
||||
});
|
||||
|
||||
it('should allow normal prompts to run when MCP servers are finished initializing', async () => {
|
||||
const mockMcpClientManager = {
|
||||
getDiscoveryState: vi.fn().mockReturnValue(MCPDiscoveryState.COMPLETED),
|
||||
getMcpServerCount: vi.fn().mockReturnValue(1),
|
||||
};
|
||||
mockConfig.getMcpClientManager = () => mockMcpClientManager as any;
|
||||
|
||||
const { result } = renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('a normal prompt');
|
||||
});
|
||||
|
||||
// Prompt should be sent to Gemini.
|
||||
expect(mockHandleSlashCommand).not.toHaveBeenCalled();
|
||||
expect(mockSendMessageStream).toHaveBeenCalled();
|
||||
expect(coreEvents.emitFeedback).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -31,7 +31,6 @@ import {
|
||||
ValidationRequiredError,
|
||||
coreEvents,
|
||||
CoreEvent,
|
||||
MCPDiscoveryState,
|
||||
} from '@google/gemini-cli-core';
|
||||
import type {
|
||||
Config,
|
||||
@@ -961,25 +960,6 @@ export const useGeminiStream = (
|
||||
async ({ metadata: spanMetadata }) => {
|
||||
spanMetadata.input = query;
|
||||
|
||||
const discoveryState = config
|
||||
.getMcpClientManager()
|
||||
?.getDiscoveryState();
|
||||
const mcpServerCount =
|
||||
config.getMcpClientManager()?.getMcpServerCount() ?? 0;
|
||||
if (
|
||||
!options?.isContinuation &&
|
||||
typeof query === 'string' &&
|
||||
!isSlashCommand(query.trim()) &&
|
||||
mcpServerCount > 0 &&
|
||||
discoveryState !== MCPDiscoveryState.COMPLETED
|
||||
) {
|
||||
coreEvents.emitFeedback(
|
||||
'info',
|
||||
'Waiting for MCP servers to initialize... Slash commands are still available.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const queryId = `${Date.now()}-${Math.random()}`;
|
||||
activeQueryIdRef.current = queryId;
|
||||
if (
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
|
||||
import { act } from 'react';
|
||||
import { render } from '../../test-utils/render.js';
|
||||
import { useMcpStatus } from './useMcpStatus.js';
|
||||
import {
|
||||
MCPDiscoveryState,
|
||||
type Config,
|
||||
CoreEvent,
|
||||
coreEvents,
|
||||
} from '@google/gemini-cli-core';
|
||||
|
||||
describe('useMcpStatus', () => {
|
||||
let mockConfig: Config;
|
||||
let mockMcpClientManager: {
|
||||
getDiscoveryState: Mock<() => MCPDiscoveryState>;
|
||||
getMcpServerCount: Mock<() => number>;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockMcpClientManager = {
|
||||
getDiscoveryState: vi.fn().mockReturnValue(MCPDiscoveryState.NOT_STARTED),
|
||||
getMcpServerCount: vi.fn().mockReturnValue(0),
|
||||
};
|
||||
|
||||
mockConfig = {
|
||||
getMcpClientManager: vi.fn().mockReturnValue(mockMcpClientManager),
|
||||
} as unknown as Config;
|
||||
});
|
||||
|
||||
const renderMcpStatusHook = (config: Config) => {
|
||||
let hookResult: ReturnType<typeof useMcpStatus>;
|
||||
function TestComponent({ config }: { config: Config }) {
|
||||
hookResult = useMcpStatus(config);
|
||||
return null;
|
||||
}
|
||||
render(<TestComponent config={config} />);
|
||||
return {
|
||||
result: {
|
||||
get current() {
|
||||
return hookResult;
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
it('should initialize with correct values (no servers)', () => {
|
||||
const { result } = renderMcpStatusHook(mockConfig);
|
||||
|
||||
expect(result.current.discoveryState).toBe(MCPDiscoveryState.NOT_STARTED);
|
||||
expect(result.current.mcpServerCount).toBe(0);
|
||||
expect(result.current.isMcpReady).toBe(true);
|
||||
});
|
||||
|
||||
it('should initialize with correct values (with servers, not started)', () => {
|
||||
mockMcpClientManager.getMcpServerCount.mockReturnValue(1);
|
||||
const { result } = renderMcpStatusHook(mockConfig);
|
||||
|
||||
expect(result.current.isMcpReady).toBe(false);
|
||||
});
|
||||
|
||||
it('should not be ready while in progress', () => {
|
||||
mockMcpClientManager.getDiscoveryState.mockReturnValue(
|
||||
MCPDiscoveryState.IN_PROGRESS,
|
||||
);
|
||||
mockMcpClientManager.getMcpServerCount.mockReturnValue(1);
|
||||
const { result } = renderMcpStatusHook(mockConfig);
|
||||
|
||||
expect(result.current.isMcpReady).toBe(false);
|
||||
});
|
||||
|
||||
it('should update state when McpClientUpdate is emitted', () => {
|
||||
mockMcpClientManager.getMcpServerCount.mockReturnValue(1);
|
||||
mockMcpClientManager.getDiscoveryState.mockReturnValue(
|
||||
MCPDiscoveryState.IN_PROGRESS,
|
||||
);
|
||||
const { result } = renderMcpStatusHook(mockConfig);
|
||||
|
||||
expect(result.current.isMcpReady).toBe(false);
|
||||
|
||||
mockMcpClientManager.getDiscoveryState.mockReturnValue(
|
||||
MCPDiscoveryState.COMPLETED,
|
||||
);
|
||||
|
||||
act(() => {
|
||||
coreEvents.emit(CoreEvent.McpClientUpdate, new Map());
|
||||
});
|
||||
|
||||
expect(result.current.discoveryState).toBe(MCPDiscoveryState.COMPLETED);
|
||||
expect(result.current.isMcpReady).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import {
|
||||
type Config,
|
||||
coreEvents,
|
||||
MCPDiscoveryState,
|
||||
CoreEvent,
|
||||
} from '@google/gemini-cli-core';
|
||||
|
||||
export function useMcpStatus(config: Config) {
|
||||
const [discoveryState, setDiscoveryState] = useState<MCPDiscoveryState>(
|
||||
() =>
|
||||
config.getMcpClientManager()?.getDiscoveryState() ??
|
||||
MCPDiscoveryState.NOT_STARTED,
|
||||
);
|
||||
|
||||
const [mcpServerCount, setMcpServerCount] = useState<number>(
|
||||
() => config.getMcpClientManager()?.getMcpServerCount() ?? 0,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const onChange = () => {
|
||||
const manager = config.getMcpClientManager();
|
||||
if (manager) {
|
||||
setDiscoveryState(manager.getDiscoveryState());
|
||||
setMcpServerCount(manager.getMcpServerCount());
|
||||
}
|
||||
};
|
||||
|
||||
coreEvents.on(CoreEvent.McpClientUpdate, onChange);
|
||||
return () => {
|
||||
coreEvents.off(CoreEvent.McpClientUpdate, onChange);
|
||||
};
|
||||
}, [config]);
|
||||
|
||||
// We are ready if discovery has completed, OR if it hasn't even started and there are no servers.
|
||||
const isMcpReady =
|
||||
discoveryState === MCPDiscoveryState.COMPLETED ||
|
||||
(discoveryState === MCPDiscoveryState.NOT_STARTED && mcpServerCount === 0);
|
||||
|
||||
return {
|
||||
discoveryState,
|
||||
mcpServerCount,
|
||||
isMcpReady,
|
||||
};
|
||||
}
|
||||
@@ -28,6 +28,7 @@ describe('useMessageQueue', () => {
|
||||
isConfigInitialized: boolean;
|
||||
streamingState: StreamingState;
|
||||
submitQuery: (query: string) => void;
|
||||
isMcpReady: boolean;
|
||||
}) => {
|
||||
let hookResult: ReturnType<typeof useMessageQueue>;
|
||||
function TestComponent(props: typeof initialProps) {
|
||||
@@ -51,6 +52,7 @@ describe('useMessageQueue', () => {
|
||||
isConfigInitialized: true,
|
||||
streamingState: StreamingState.Idle,
|
||||
submitQuery: mockSubmitQuery,
|
||||
isMcpReady: true,
|
||||
});
|
||||
|
||||
expect(result.current.messageQueue).toEqual([]);
|
||||
@@ -62,6 +64,7 @@ describe('useMessageQueue', () => {
|
||||
isConfigInitialized: true,
|
||||
streamingState: StreamingState.Responding,
|
||||
submitQuery: mockSubmitQuery,
|
||||
isMcpReady: true,
|
||||
});
|
||||
|
||||
act(() => {
|
||||
@@ -80,6 +83,7 @@ describe('useMessageQueue', () => {
|
||||
isConfigInitialized: true,
|
||||
streamingState: StreamingState.Responding,
|
||||
submitQuery: mockSubmitQuery,
|
||||
isMcpReady: true,
|
||||
});
|
||||
|
||||
act(() => {
|
||||
@@ -100,6 +104,7 @@ describe('useMessageQueue', () => {
|
||||
isConfigInitialized: true,
|
||||
streamingState: StreamingState.Responding,
|
||||
submitQuery: mockSubmitQuery,
|
||||
isMcpReady: true,
|
||||
});
|
||||
|
||||
act(() => {
|
||||
@@ -120,6 +125,7 @@ describe('useMessageQueue', () => {
|
||||
isConfigInitialized: true,
|
||||
streamingState: StreamingState.Responding,
|
||||
submitQuery: mockSubmitQuery,
|
||||
isMcpReady: true,
|
||||
});
|
||||
|
||||
act(() => {
|
||||
@@ -133,11 +139,12 @@ describe('useMessageQueue', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should auto-submit queued messages when transitioning to Idle', async () => {
|
||||
it('should auto-submit queued messages when transitioning to Idle and MCP is ready', async () => {
|
||||
const { result, rerender } = renderMessageQueueHook({
|
||||
isConfigInitialized: true,
|
||||
streamingState: StreamingState.Responding,
|
||||
submitQuery: mockSubmitQuery,
|
||||
isMcpReady: true,
|
||||
});
|
||||
|
||||
// Add some messages
|
||||
@@ -157,11 +164,37 @@ describe('useMessageQueue', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should wait for MCP readiness before auto-submitting', async () => {
|
||||
const { result, rerender } = renderMessageQueueHook({
|
||||
isConfigInitialized: true,
|
||||
streamingState: StreamingState.Idle,
|
||||
submitQuery: mockSubmitQuery,
|
||||
isMcpReady: false,
|
||||
});
|
||||
|
||||
// Add some messages while Idle but MCP not ready
|
||||
act(() => {
|
||||
result.current.addMessage('Delayed message');
|
||||
});
|
||||
|
||||
expect(result.current.messageQueue).toEqual(['Delayed message']);
|
||||
expect(mockSubmitQuery).not.toHaveBeenCalled();
|
||||
|
||||
// Transition MCP to ready
|
||||
rerender({ isMcpReady: true });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockSubmitQuery).toHaveBeenCalledWith('Delayed message');
|
||||
expect(result.current.messageQueue).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not auto-submit when queue is empty', () => {
|
||||
const { rerender } = renderMessageQueueHook({
|
||||
isConfigInitialized: true,
|
||||
streamingState: StreamingState.Responding,
|
||||
submitQuery: mockSubmitQuery,
|
||||
isMcpReady: true,
|
||||
});
|
||||
|
||||
// Transition to Idle with empty queue
|
||||
@@ -175,6 +208,7 @@ describe('useMessageQueue', () => {
|
||||
isConfigInitialized: true,
|
||||
streamingState: StreamingState.Responding,
|
||||
submitQuery: mockSubmitQuery,
|
||||
isMcpReady: true,
|
||||
});
|
||||
|
||||
// Add messages
|
||||
@@ -194,6 +228,7 @@ describe('useMessageQueue', () => {
|
||||
isConfigInitialized: true,
|
||||
streamingState: StreamingState.Idle,
|
||||
submitQuery: mockSubmitQuery,
|
||||
isMcpReady: true,
|
||||
});
|
||||
|
||||
// Start responding
|
||||
@@ -235,6 +270,7 @@ describe('useMessageQueue', () => {
|
||||
isConfigInitialized: true,
|
||||
streamingState: StreamingState.Responding,
|
||||
submitQuery: mockSubmitQuery,
|
||||
isMcpReady: true,
|
||||
});
|
||||
|
||||
// Add multiple messages
|
||||
@@ -265,6 +301,7 @@ describe('useMessageQueue', () => {
|
||||
isConfigInitialized: true,
|
||||
streamingState: StreamingState.Responding,
|
||||
submitQuery: mockSubmitQuery,
|
||||
isMcpReady: true,
|
||||
});
|
||||
|
||||
let poppedMessages: string | undefined = 'not-undefined';
|
||||
@@ -281,6 +318,7 @@ describe('useMessageQueue', () => {
|
||||
isConfigInitialized: true,
|
||||
streamingState: StreamingState.Responding,
|
||||
submitQuery: mockSubmitQuery,
|
||||
isMcpReady: false,
|
||||
});
|
||||
|
||||
act(() => {
|
||||
@@ -301,6 +339,7 @@ describe('useMessageQueue', () => {
|
||||
isConfigInitialized: true,
|
||||
streamingState: StreamingState.Responding,
|
||||
submitQuery: mockSubmitQuery,
|
||||
isMcpReady: false,
|
||||
});
|
||||
|
||||
act(() => {
|
||||
@@ -330,6 +369,7 @@ describe('useMessageQueue', () => {
|
||||
isConfigInitialized: true,
|
||||
streamingState: StreamingState.Responding,
|
||||
submitQuery: mockSubmitQuery,
|
||||
isMcpReady: false,
|
||||
});
|
||||
|
||||
// Add messages
|
||||
|
||||
@@ -11,6 +11,7 @@ export interface UseMessageQueueOptions {
|
||||
isConfigInitialized: boolean;
|
||||
streamingState: StreamingState;
|
||||
submitQuery: (query: string) => void;
|
||||
isMcpReady: boolean;
|
||||
}
|
||||
|
||||
export interface UseMessageQueueReturn {
|
||||
@@ -30,6 +31,7 @@ export function useMessageQueue({
|
||||
isConfigInitialized,
|
||||
streamingState,
|
||||
submitQuery,
|
||||
isMcpReady,
|
||||
}: UseMessageQueueOptions): UseMessageQueueReturn {
|
||||
const [messageQueue, setMessageQueue] = useState<string[]>([]);
|
||||
|
||||
@@ -67,6 +69,7 @@ export function useMessageQueue({
|
||||
if (
|
||||
isConfigInitialized &&
|
||||
streamingState === StreamingState.Idle &&
|
||||
isMcpReady &&
|
||||
messageQueue.length > 0
|
||||
) {
|
||||
// Combine all messages with double newlines for clarity
|
||||
@@ -75,7 +78,13 @@ export function useMessageQueue({
|
||||
setMessageQueue([]);
|
||||
submitQuery(combinedMessage);
|
||||
}
|
||||
}, [isConfigInitialized, streamingState, messageQueue, submitQuery]);
|
||||
}, [
|
||||
isConfigInitialized,
|
||||
streamingState,
|
||||
isMcpReady,
|
||||
messageQueue,
|
||||
submitQuery,
|
||||
]);
|
||||
|
||||
return {
|
||||
messageQueue,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli-core",
|
||||
"version": "0.26.0-nightly.20260115.6cb3ae4e0",
|
||||
"version": "0.26.0-preview.4",
|
||||
"description": "Gemini CLI Core",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
|
||||
@@ -682,7 +682,7 @@ export class Config {
|
||||
? false
|
||||
: (params.useWriteTodos ?? true);
|
||||
this.enableHooksUI = params.enableHooksUI ?? true;
|
||||
this.enableHooks = params.enableHooks ?? false;
|
||||
this.enableHooks = params.enableHooks ?? true;
|
||||
this.disabledHooks = params.disabledHooks ?? [];
|
||||
|
||||
this.codebaseInvestigatorSettings = {
|
||||
|
||||
@@ -245,6 +245,7 @@ export class McpClientManager {
|
||||
if (currentPromise === this.discoveryPromise) {
|
||||
this.discoveryPromise = undefined;
|
||||
this.discoveryState = MCPDiscoveryState.COMPLETED;
|
||||
this.eventEmitter?.emit('mcp-client-update', this.clients);
|
||||
}
|
||||
})
|
||||
.catch(() => {}); // Prevents unhandled rejection from the .finally branch
|
||||
@@ -273,6 +274,12 @@ export class McpClientManager {
|
||||
this.cliConfig.getMcpServerCommand(),
|
||||
);
|
||||
|
||||
if (Object.keys(servers).length === 0) {
|
||||
this.discoveryState = MCPDiscoveryState.COMPLETED;
|
||||
this.eventEmitter?.emit('mcp-client-update', this.clients);
|
||||
return;
|
||||
}
|
||||
|
||||
this.eventEmitter?.emit('mcp-client-update', this.clients);
|
||||
await Promise.all(
|
||||
Object.entries(servers).map(([name, config]) =>
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
*/
|
||||
|
||||
import { EventEmitter } from 'node:events';
|
||||
import type { McpClient } from '../tools/mcp-client.js';
|
||||
import type { ExtensionEvents } from './extensionLoader.js';
|
||||
|
||||
/**
|
||||
* Defines the severity level for user-facing feedback.
|
||||
@@ -115,6 +117,7 @@ export enum CoreEvent {
|
||||
Output = 'output',
|
||||
MemoryChanged = 'memory-changed',
|
||||
ExternalEditorClosed = 'external-editor-closed',
|
||||
McpClientUpdate = 'mcp-client-update',
|
||||
SettingsChanged = 'settings-changed',
|
||||
HookStart = 'hook-start',
|
||||
HookEnd = 'hook-end',
|
||||
@@ -123,13 +126,14 @@ export enum CoreEvent {
|
||||
RetryAttempt = 'retry-attempt',
|
||||
}
|
||||
|
||||
export interface CoreEvents {
|
||||
export interface CoreEvents extends ExtensionEvents {
|
||||
[CoreEvent.UserFeedback]: [UserFeedbackPayload];
|
||||
[CoreEvent.ModelChanged]: [ModelChangedPayload];
|
||||
[CoreEvent.ConsoleLog]: [ConsoleLogPayload];
|
||||
[CoreEvent.Output]: [OutputPayload];
|
||||
[CoreEvent.MemoryChanged]: [MemoryChangedPayload];
|
||||
[CoreEvent.ExternalEditorClosed]: never[];
|
||||
[CoreEvent.McpClientUpdate]: Array<Map<string, McpClient> | never>;
|
||||
[CoreEvent.SettingsChanged]: never[];
|
||||
[CoreEvent.HookStart]: [HookStartPayload];
|
||||
[CoreEvent.HookEnd]: [HookEndPayload];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli-test-utils",
|
||||
"version": "0.26.0-nightly.20260115.6cb3ae4e0",
|
||||
"version": "0.26.0-preview.4",
|
||||
"private": true,
|
||||
"main": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -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.26.0-nightly.20260115.6cb3ae4e0",
|
||||
"version": "0.26.0-preview.4",
|
||||
"publisher": "google",
|
||||
"icon": "assets/icon.png",
|
||||
"repository": {
|
||||
|
||||
@@ -1574,8 +1574,8 @@
|
||||
"enabled": {
|
||||
"title": "Enable Hooks",
|
||||
"description": "Canonical toggle for the hooks system. When disabled, no hooks will be executed.",
|
||||
"markdownDescription": "Canonical toggle for the hooks system. When disabled, no hooks will be executed.\n\n- Category: `Advanced`\n- Requires restart: `no`\n- Default: `false`",
|
||||
"default": false,
|
||||
"markdownDescription": "Canonical toggle for the hooks system. When disabled, no hooks will be executed.\n\n- Category: `Advanced`\n- Requires restart: `no`\n- Default: `true`",
|
||||
"default": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
"disabled": {
|
||||
|
||||
Reference in New Issue
Block a user