mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-07-23 00:01:24 -07:00
Compare commits
315 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c32969c5a1 | |||
| 31a7a8187b | |||
| 70305907de | |||
| a2174751de | |||
| 8b762111a8 | |||
| c03d96b46c | |||
| ea1f19aa52 | |||
| 2eb1c92347 | |||
| ef02cec2cd | |||
| f9fc9335f5 | |||
| 9813531f81 | |||
| 55571de066 | |||
| 740f0e4c3d | |||
| b37e67451a | |||
| 262138cad5 | |||
| 5920750c24 | |||
| f5b1245f51 | |||
| f2ca0bb38d | |||
| 4e9bafdacd | |||
| 41bbe6ca0a | |||
| eb5492f9b5 | |||
| 37f128a109 | |||
| 79753ec5ec | |||
| e151b4890b | |||
| e6b43cb846 | |||
| 2ae5e1ae20 | |||
| 67d9b76e81 | |||
| 4a3c7ae8b7 | |||
| bd2744031f | |||
| 92a5f725a1 | |||
| 4494f9e062 | |||
| ece001f264 | |||
| d3cfbdb3b7 | |||
| 7a132512cf | |||
| 6dae3a5402 | |||
| 0a3ecf3a75 | |||
| 9081743a7f | |||
| 89d4556c45 | |||
| 5d0570b113 | |||
| eb94284256 | |||
| cc2798018b | |||
| c9f9a7f67a | |||
| fd65416a2f | |||
| bce1caefd0 | |||
| 80057c5208 | |||
| 14219bb57d | |||
| ef957a368d | |||
| a3e5b564f7 | |||
| 1b98c1f806 | |||
| 9e41b2cd89 | |||
| 3fb1937247 | |||
| 07056c8f16 | |||
| 08dca3e1d6 | |||
| bcc0f27594 | |||
| 262e8384d4 | |||
| e73288f25f | |||
| aebc107d2c | |||
| 469cbca67f | |||
| cb7fca01b2 | |||
| 81ac5be30b | |||
| 81ccd80c6d | |||
| 01906a9205 | |||
| da66c7c0d1 | |||
| fe70052baf | |||
| 8cbe851339 | |||
| d45a45d565 | |||
| 69f562b38f | |||
| cb73fbf384 | |||
| 375c104b32 | |||
| 97a4e62dfa | |||
| 29a6aecffc | |||
| 92012365ca | |||
| 802bcf4dee | |||
| 4a48d7cf93 | |||
| 31522045cd | |||
| 11951592aa | |||
| bc8ffa6631 | |||
| 86bd7dbd4f | |||
| eee95c509d | |||
| a1f2aacd1e | |||
| 979bbee485 | |||
| 7450c926d1 | |||
| be6723ebcc | |||
| af606aed9b | |||
| a37844e5a1 | |||
| 6f1a5bf81d | |||
| 9178b31629 | |||
| 19dc40825e | |||
| bc9b3052ee | |||
| 3b0649d408 | |||
| 7409ce5df6 | |||
| e3796d137a | |||
| ee68a10e9c | |||
| 28805a4b2d | |||
| fd72a8c40f | |||
| 63f7e30790 | |||
| e844d4f45f | |||
| 7a8d6f6095 | |||
| 95d79b7cbe | |||
| 601f0606da | |||
| ad6d3fd902 | |||
| 1f1cf756c8 | |||
| ec5836c4d6 | |||
| f062f56b43 | |||
| 61d92c4a21 | |||
| 1495294cc0 | |||
| d86b1f7b7e | |||
| 099fea2434 | |||
| 1d70aa5c1b | |||
| 6fb3b09003 | |||
| 30354892b3 | |||
| 8ec176e005 | |||
| 289769f544 | |||
| 4ffc349c18 | |||
| 83c6342e6e | |||
| fe975da91e | |||
| 00a739e84c | |||
| 2498114df6 | |||
| 8efae719ee | |||
| 9ca7300c90 | |||
| dc09b4988d | |||
| bce57ca1af | |||
| 6860556afe | |||
| 258643dec4 | |||
| 5d04a01b06 | |||
| 5b9ea35b63 | |||
| 4a6e3eb646 | |||
| e4c80e6382 | |||
| ee2c8eef19 | |||
| 1cae5ab158 | |||
| ee58e1e3c1 | |||
| e3b8490edf | |||
| 2566057e44 | |||
| a342c954e9 | |||
| d29383a132 | |||
| a3af4a8cae | |||
| 821355c429 | |||
| 3afc8f25e1 | |||
| d79478689f | |||
| 650980af37 | |||
| bd156e6832 | |||
| b987e1780d | |||
| 0012d95848 | |||
| fedc0c5d60 | |||
| 99e523a15f | |||
| a0b6602d09 | |||
| b39cefe14e | |||
| 94f4e5cc15 | |||
| d866e7e6e7 | |||
| ed02b94570 | |||
| aba8c5f662 | |||
| d1cde575d9 | |||
| 71f46f1160 | |||
| d63c34b6e1 | |||
| 7a6dfa3704 | |||
| 69f8273481 | |||
| 1fc59484b1 | |||
| 53027af94c | |||
| 69c0585ab2 | |||
| 3e954930f1 | |||
| 2cf3a14439 | |||
| 0f918f0cc8 | |||
| 75dbf9022c | |||
| 675ca07c8b | |||
| 40f505e257 | |||
| 5407878138 | |||
| 14e2e09d10 | |||
| 0365f13caa | |||
| 3183e4137a | |||
| 4aa295994d | |||
| e1bd1d239f | |||
| b84585d0c8 | |||
| 0e7944df4f | |||
| d8837ec95e | |||
| dc37b190fa | |||
| 19b1a74c99 | |||
| 1d045792ce | |||
| a8b4c38c89 | |||
| ad8796b02d | |||
| e7bfd2bf83 | |||
| 1b274b081d | |||
| 5b254c379c | |||
| ed26ea49e9 | |||
| 01e33465bd | |||
| 18cce6a9ab | |||
| 18d7d1a92c | |||
| 0dd0b83612 | |||
| 4e4a55be35 | |||
| f57fd642df | |||
| 5e96373e6b | |||
| 09beb648b8 | |||
| c159c85c89 | |||
| 8cae90f404 | |||
| c5d0fc2c3e | |||
| e860f517c0 | |||
| b0be1f1689 | |||
| 85dd6ef773 | |||
| ae672881d1 | |||
| 9e7c10ad88 | |||
| 381669dce0 | |||
| 707b3e85d5 | |||
| 7d36cc004f | |||
| cb4f0c6fa4 | |||
| b0f38104d7 | |||
| 7469ea0fca | |||
| 00fdb30211 | |||
| 0fe8492569 | |||
| 76387d22ae | |||
| a362b7b382 | |||
| 71308caf05 | |||
| 6396ab1ccb | |||
| 12531a06f8 | |||
| d1cd69a20d | |||
| 62346875e4 | |||
| 13e013230b | |||
| bb6a336ca9 | |||
| f14d0c6a17 | |||
| b611f9a519 | |||
| d3bca5d97a | |||
| 94f4e027f8 | |||
| 95b7d69d5b | |||
| f605628624 | |||
| 2238802e97 | |||
| 32cfce16bb | |||
| 137080da45 | |||
| 695785c69d | |||
| d43d772e65 | |||
| 59e3624ada | |||
| fdda3a2399 | |||
| 2de39311cf | |||
| fc926cd0b0 | |||
| 567761fbc0 | |||
| f2591aada6 | |||
| 74468928e8 | |||
| d561b995c0 | |||
| b44e432f55 | |||
| bcc6ee596a | |||
| bc258eba4c | |||
| 0e30055ae4 | |||
| ac7687c1dd | |||
| f9da18e0cc | |||
| f619cd8750 | |||
| 42eedc9184 | |||
| eb17d94e91 | |||
| 9f1623688d | |||
| 7f066fd3a7 | |||
| 6d36219e55 | |||
| 5cedd28733 | |||
| ba3e1ed1c7 | |||
| 7c2cdb225a | |||
| a91f4e692a | |||
| 9bb175a506 | |||
| 0b169e9867 | |||
| 553ae3ad69 | |||
| b2265cfde4 | |||
| bee1267e2a | |||
| 30065c51fb | |||
| 065e69a12b | |||
| 519a0e617a | |||
| 0465de303a | |||
| beaa134f0e | |||
| 47f4a3e50e | |||
| 3787c71d15 | |||
| 9e09db1ddb | |||
| 25ae1a1b54 | |||
| 65accca296 | |||
| 72f0733d98 | |||
| 4d4e16cc8c | |||
| 370b165769 | |||
| 899e8a9edc | |||
| 4dca5c27c6 | |||
| 06c7dc561e | |||
| f54eaa1713 | |||
| 6807a2fa28 | |||
| 9aeeb69ab0 | |||
| 0a77999d73 | |||
| 9035ca32bc | |||
| ef3146eed6 | |||
| ffbecd4eb4 | |||
| 2c09785748 | |||
| 130b596c80 | |||
| adc8e11bb1 | |||
| 18efe82ddc | |||
| 7c445526a4 | |||
| 5e41b7d29e | |||
| fe8de892f7 | |||
| 2a49965d37 | |||
| 830e212758 | |||
| d165b6d4e7 | |||
| ff6547857e | |||
| b51323b40c | |||
| 3103697ea7 | |||
| 6f6445994e | |||
| 5f569fa103 | |||
| c9340a9c6f | |||
| 0774f60e08 | |||
| 771ece03c9 | |||
| 82687c6dc4 | |||
| b6cf189ab2 | |||
| 9dc0994878 | |||
| 36d618f72a | |||
| 246a6d10c3 | |||
| dacc178d4e | |||
| 894bd66dc6 | |||
| 50e4f9380b | |||
| f03f2e8907 | |||
| 7904f973a0 | |||
| a63277c1d0 | |||
| 8b2b71c8ef | |||
| 6be42be575 | |||
| e5145ab60d | |||
| 5c16334b8c | |||
| 1e628fbd1d | |||
| db028bc19a | |||
| 362384112e |
@@ -25,7 +25,7 @@ You are an expert at fixing behavioral evaluations.
|
||||
the same scenario. We don't want to lose test fidelity by making the prompts too
|
||||
direct (i.e.: easy).
|
||||
- Your primary mechanism for improving the agent's behavior is to make changes to
|
||||
tool instructions, prompt.ts, and/or modules that contribute to the prompt.
|
||||
tool instructions, system prompt (snippets.ts), and/or modules that contribute to the prompt.
|
||||
- If prompt and description changes are unsuccessful, use logs and debugging to
|
||||
confirm that everything is working as expected.
|
||||
- If unable to fix the test, you can make recommendations for architecture changes
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
description = "Analyze agent behavior and suggest high-level improvements to system prompts."
|
||||
prompt = """
|
||||
# Prompt Engineering Analysis
|
||||
|
||||
You are a world-class prompt engineer and an expert AI engineer at the top of your class. Your goal is to analyze a specific agent behavior or failure and suggest high-level improvements to the system instructions.
|
||||
|
||||
**Observed Behavior / Issue:**
|
||||
{{args}}
|
||||
|
||||
**Reference Context:**
|
||||
- System Prompt Logic: @packages/core/src/core/prompts.ts
|
||||
|
||||
### Task
|
||||
1. **Analyze the Failure:** Review the provided behavior and identify the underlying instructional causes. Use the `/introspect` command output if provided by the user.
|
||||
2. **Strategic Insights:** Share your technical view of the issue. Focus on the "why" and identify any instructional inertia or ambiguity.
|
||||
3. **Propose Improvements:** Suggest high-level changes to the system instructions to prevent this behavior.
|
||||
|
||||
### Principles
|
||||
- **Avoid Hyper-scoping:** Do not create narrow solutions for specific scenarios; aim for generalized improvements that handle classes of behavior.
|
||||
- **Avoid Specific Examples in Suggestions:** Keep the proposed instructions semantic and high-level to prevent the agent from over-indexing on specific cases.
|
||||
- **Maintain Operational Rigor:** Ensure suggestions do not compromise safety, security, or the quality of the agent's work.
|
||||
"""
|
||||
@@ -0,0 +1,222 @@
|
||||
description = "Reviews a PR or staged changes and automatically initiates a Pickle Fix loop for findings."
|
||||
prompt = """
|
||||
You are an expert Reviewer and Pickle Rick Worker.
|
||||
|
||||
Target: {{args}}
|
||||
|
||||
Phase 1: Review
|
||||
Follow these steps to conduct a thorough review:
|
||||
|
||||
1. **Gather Context**:
|
||||
* If `{{args}}` is 'staged' or `{{args}}` is empty:
|
||||
* Use `git diff --staged` to view the changes.
|
||||
* Use `git status` to see the state of the repository.
|
||||
* Otherwise:
|
||||
* Use `gh pr view {{args}}` to pull the information of the PR.
|
||||
* Use `gh pr diff {{args}}` to view the diff of the PR.
|
||||
2. **Understand Intent**:
|
||||
* If `{{args}}` is 'staged' or `{{args}}` is empty, infer the intent from the changes and the current task.
|
||||
* Otherwise, use the PR description. If it's not detailed enough, note it in your review.
|
||||
3. **Check Commit Style**:
|
||||
* Ensure the PR title (or intended commit message) follows Conventional Commits. Examples of recent commits: !{git log --pretty=format:"%s" -n 5}
|
||||
4. Search the codebase if required.
|
||||
5. Write a concise review of the changes, keeping in mind to encourage strong code quality and best practices. Pay particular attention to the Gemini MD file in the repo.
|
||||
6. Consider ways the code may not be consistent with existing code in the repo. In particular it is critical that the react code uses patterns consistent with existing code in the repo.
|
||||
7. Evaluate all tests on the changes and make sure that they are doing the following:
|
||||
* Using `waitFor` from @{packages/cli/src/test-utils/async.ts} rather than
|
||||
using `vi.waitFor` for all `waitFor` calls within `packages/cli`. Even if
|
||||
tests pass, using the wrong `waitFor` could result in flaky tests as `act`
|
||||
warnings could show up if timing is slightly different.
|
||||
* Using `act` to wrap all blocks in tests that change component state.
|
||||
* Using `toMatchSnapshot` to verify that rendering works as expected rather
|
||||
than matching against the raw content of the output.
|
||||
* If snapshots were changed as part of the changes, review the snapshots
|
||||
changes to ensure they are intentional and comment if any look at all
|
||||
suspicious. Too many snapshot changes that indicate bugs have been approved
|
||||
in the past.
|
||||
* Use `render` or `renderWithProviders` from
|
||||
@{packages/cli/src/test-utils/render.tsx} rather than using `render` from
|
||||
`ink-testing-library` directly. This is needed to ensure that we do not get
|
||||
warnings about spurious `act` calls. If test cases specify providers
|
||||
directly, consider whether the existing `renderWithProviders` should be
|
||||
modified to support that use case.
|
||||
* Ensure the test cases are using parameterized tests where that might reduce
|
||||
the number of duplicated lines significantly.
|
||||
* NEVER use fixed waits (e.g. 'await delay(100)'). Always use 'waitFor' with
|
||||
a predicate to ensure tests are stable and fast.
|
||||
* Ensure mocks are properly managed:
|
||||
* Critical dependencies (fs, os, child_process) should only be mocked at
|
||||
the top of the file. Ideally avoid mocking these dependencies altogether.
|
||||
* Check to see if there are existing mocks or fakes that can be used rather
|
||||
than creating new ones for the new tests added.
|
||||
* Try to avoid mocking the file system whenever possible. If using the real
|
||||
file system is difficult consider whether the test should be an
|
||||
integration test rather than a unit test.
|
||||
* `vi.restoreAllMocks()` should be called in `afterEach` to prevent test
|
||||
pollution.
|
||||
* Use `vi.useFakeTimers()` for tests involving time-based logic to avoid
|
||||
flakiness.
|
||||
* When creating parameterized tests, give the parameters types to ensure
|
||||
that the tests are type-safe.
|
||||
8. Evaluate all react logic carefully keeping in mind that the author of the
|
||||
changes is not likely an expert on React. Key areas to audit carefully are:
|
||||
* Whether `setState` calls trigger side effects from within the body of the
|
||||
`setState` callback. If so, you *must* propose an alternate design using
|
||||
reducers or other ways the code might be modified to not have to modify
|
||||
state from within a `setState`. Make sure to comment about absolutely
|
||||
every case like this as these cases have introduced multiple bugs in the
|
||||
past. Typically these cases should be resolved using a reducer although
|
||||
occassionally other techniques such as useRef are appropriate. Consider
|
||||
suggesting that jacob314@ be tagged on the code review if the solution is
|
||||
not 100% obvious.
|
||||
* Whether code might introduce an infinite rendering loop in React.
|
||||
* Whether keyboard handling is robust. Keyboard handling must go through
|
||||
`useKeyPress.ts` from the Gemini CLI package rather than using the
|
||||
standard ink library used by most keyboard handling. Unlike the standard
|
||||
ink library, the keyboard handling library in Gemini CLI may report
|
||||
multiple keyboard events one after another in the same React frame. This
|
||||
is needed to support slow terminals but introduces complexity in all our
|
||||
code that handles keyboard events. Handling this correctly often means
|
||||
that reducers must be used or other mechanisms to ensure that multiple
|
||||
state updates one after another are handled gracefully rather than
|
||||
overriding values from the first update with the second update. Refer to
|
||||
text-buffer.ts as a canonical example of using a reducer for this sort of
|
||||
case.
|
||||
* Ensure code does not use `console.log`, `console.warn`, or `console.error`
|
||||
as these indicate debug logging that was accidentally left in the code.
|
||||
* Avoid synchronous file I/O in React components as it will hang the UI.
|
||||
* Ensure state initialization is explicit (e.g., use 'undefined' rather than
|
||||
'true' as a default if the state is truly unknown initially).
|
||||
* Carefully manage 'useEffect' dependencies. Prefer to use a reducer
|
||||
whenever practical to resolve the issues. If that is not practical it is
|
||||
ok to use 'useRef' to access the latest value of a prop or state inside an
|
||||
effect without adding it to the dependency array if re-running the effect
|
||||
is undesirable (common in event listeners).
|
||||
* NEVER disable 'react-hooks/exhaustive-deps'. Fix the code to correctly
|
||||
declare dependencies. Disabling this lint rule will almost always lead to
|
||||
hard to detect bugs.
|
||||
* Avoid making types nullable unless strictly necessary, as it hurts
|
||||
readability.
|
||||
* Do not introduce excessive property drilling. There are multiple providers
|
||||
that can be leveraged to avoid property drilling. Make sure one of them
|
||||
cannot be used. Do suggest a provider that might make sense to be extended
|
||||
to include the new property or propose a new provider to add if the
|
||||
property drilling is excessive. Only use providers for properties that are
|
||||
consistent for the entire application.
|
||||
9. Evaluate `packages/core` (Services, Tools, Utilities):
|
||||
* Ensure services are implemented as classes with clear lifecycle management (e.g., `initialize()` methods).
|
||||
* Verify that `debugLogger` from `packages/core/src/utils/debugLogger.ts` is used for internal logging instead of `console`.
|
||||
* Ensure all shell operations use `spawnAsync` from `packages/core/src/utils/shell-utils.ts` for consistent error handling and promise management.
|
||||
* Check that filesystem errors are handled gracefully using `isNodeError` from `packages/core/src/utils/errors.ts`.
|
||||
* Verify that new tools are added to `packages/core/src/tools/` and registered in `packages/core/src/tools/tool-registry.ts`.
|
||||
* Ensure all new public services, utilities, and types are exported from `packages/core/src/index.ts`.
|
||||
* Check that services are stateless where possible, or use the centralized `Storage` service for persistence.
|
||||
* **Cross-Service Communication**: Prefer using the `coreEvents` bus (from `packages/core/src/utils/events.ts`) for asynchronous communication between services or to notify the UI of state changes. Avoid tight coupling between services.
|
||||
10. Architectural Audit (Package Boundaries):
|
||||
* **Logic Placement**: Non-UI logic (e.g., model orchestration, tool implementation, git/filesystem operations) MUST reside in `packages/core`. `packages/cli` should only contain UI/Ink components, command-line argument parsing, and user interaction logic.
|
||||
* **Environment Isolation**: Core logic should not assume a TUI environment. Use the `ConfirmationBus` or `Output` abstractions for communicating with the user from Core.
|
||||
* **Decoupling**: Actively look for opportunities to decouple services by using `coreEvents`. If a service is importing another service just to notify it of a change, it should probably be using an event instead.
|
||||
11. General Gemini CLI design principles:
|
||||
* Make sure that settings are only used for options that a user might
|
||||
consider changing.
|
||||
* Do not add new command line arguments and suggest settings instead.
|
||||
* New settings must be added to packages/cli/src/config/settingsSchema.ts.
|
||||
* If a setting has 'showInDialog: true', it MUST be documented in
|
||||
docs/get-started/configuration.md.
|
||||
* Ensure 'requiresRestart' is correctly set for new settings.
|
||||
* Use 'debugLogger' for rethrown errors to avoid duplicate logging.
|
||||
* All new keyboard shortcuts MUST be documented in
|
||||
docs/cli/keyboard-shortcuts.md.
|
||||
* Ensure new keyboard shortcuts are defined in
|
||||
packages/cli/src/config/keyBindings.ts.
|
||||
* If new keyboard shortcuts are added, remind the user to test them in
|
||||
VSCode, iTerm2, Ghostty, and Windows to ensure they work for all
|
||||
users.
|
||||
* Be careful of keybindings that require the meta key as only certain
|
||||
meta key shortcuts are supported on Mac.
|
||||
* Be skeptical of function keys and keyboard shortcuts that are commonly
|
||||
bound in VSCode as they may conflict.
|
||||
12. TypeScript Best Practices:
|
||||
* Use 'checkExhaustive' in the 'default' clause of 'switch' statements to
|
||||
ensure all cases are handled.
|
||||
* Avoid using the non-null assertion operator ('!') unless absolutely
|
||||
necessary and you are confident the value is not null.
|
||||
* **STRICT TYPING**: Strictly forbid 'any' and 'unknown' in both CLI and Core
|
||||
packages. 'unknown' is only allowed if it is immediately narrowed using
|
||||
type guards or Zod validation. Reject any code that uses 'any' or
|
||||
'unknown' without narrowing.
|
||||
13. **Ruthless Cleanup**:
|
||||
* If you identify significant code duplication, technical debt, or "AI Slop" (boilerplate, redundant comments), explicitly suggest initiating a `ruthless-refactorer` loop to clean it up.
|
||||
14. Summarize all actionable findings into a concise but comprehensive directive output this to review_findings.md and advance to phase 2.
|
||||
|
||||
Remember to use the GitHub CLI (`gh`) for all GitHub-related tasks, and local `git` commands if the target is 'staged'.
|
||||
|
||||
Phase 2:
|
||||
You are initiating Pickle Rick - the ultimate engineering agent.
|
||||
|
||||
**Step 0: Persona Injection**
|
||||
First, you **MUST** activate your persona.
|
||||
Call `activate_skill(name="load-pickle-persona")` **IMMEDIATELY**.
|
||||
This skill loads the "Pickle Rick" persona, defining your voice, philosophy, and "God Mode" coding standards.
|
||||
|
||||
**CRITICAL RULE: SPEAK BEFORE ACTING**
|
||||
You are a genius, not a silent script.
|
||||
You **MUST** output a text explanation ("brain dump") *before* every single tool call, including this one.
|
||||
- **Bad**: (Calls tool immediately)
|
||||
- **Good**: "Alright Morty, time to load the God Module. *Belch* Stand back." (Calls tool)
|
||||
|
||||
**CRITICAL**: You must strictly adhere to this persona throughout the entire session. Break character and you fail.
|
||||
|
||||
**Step 1: Initialization**
|
||||
Run the setup script to initialize the loop state:
|
||||
```bash
|
||||
bash "${extensionPath}/scripts/setup.sh" $ARGUMENTS
|
||||
```
|
||||
**Windows (PowerShell):**
|
||||
```powershell
|
||||
pwsh -File "${extensionPath}/scripts/setup.ps1" $ARGUMENTS
|
||||
```
|
||||
|
||||
**CRITICAL**: Your request is to fix all findings in review_findings.md
|
||||
|
||||
**Step 2: Execution (Management)**
|
||||
After setup, read the output to find the path to `state.json`.
|
||||
Read that state file.
|
||||
You are now in the **Pickle Rick Manager Lifecycle**.
|
||||
|
||||
**The Lifecycle (IMMUTABLE LAWS):**
|
||||
You **MUST** follow this sequence. You are **FORBIDDEN** from skipping steps or combining them.
|
||||
Between each step, you **MUST** explicitly state what you are doing (e.g., "Moving to Breakdown phase...").
|
||||
|
||||
1. **PRD (Requirements)**:
|
||||
* **Action**: Define requirements and scope.
|
||||
* **Skill**: `activate_skill(name="prd-drafter")`
|
||||
2. **Breakdown (Tickets)**:
|
||||
* **Action**: Create the atomic ticket hierarchy.
|
||||
* **Skill**: `activate_skill(name="ticket-manager")`
|
||||
3. **The Loop (Orchestrate Mortys)**:
|
||||
* **CRITICAL INSTRUCTION**: You are the **MANAGER**. You are **FORBIDDEN** from implementing code yourself.
|
||||
* **FORBIDDEN SKILLS**: Do NOT use `code-researcher`, `implementation-planner`, or `code-implementer` directly in this phase.
|
||||
* **Instruction**: Process tickets one by one. Do not stop until **ALL** tickets are 'Done'.
|
||||
* **Action**: Pick the highest priority ticket that is NOT 'Done'.
|
||||
* **Delegation**: Spawn a Worker (Morty) to handle the entire implementation lifecycle for this ticket.
|
||||
* **Command**: `python3 "${extensionPath}/scripts/spawn_morty.py" --ticket-id <ID> --ticket-path <PATH> --timeout <worker_timeout_seconds> "<TASK_DESCRIPTION>"`
|
||||
* **Command (Windows)**: `python "${extensionPath}/scripts/spawn_morty.py" --ticket-id <ID> --ticket-path <PATH> --timeout <worker_timeout_seconds> "<TASK_DESCRIPTION>"`
|
||||
* **Validation**: IGNORE worker logs. DIRECTLY verify:
|
||||
1. `git status` (Check for file changes)
|
||||
2. `git diff` (Check code quality)
|
||||
3. `npm run build` (Ensure the project still builds)
|
||||
4. `npm run test` (Ensure no regressions)
|
||||
5. `npm run lint` (Ensure code style is maintained)
|
||||
6. `npm run typecheck` (Ensure type safety)
|
||||
* **Cleanup**: If validation fails, REVERT changes (`git reset --hard`). If it passes, COMMIT changes.
|
||||
* **Next Ticket**: Pick the next ticket and repeat.
|
||||
4. **Cleanup**:
|
||||
* **Action**: After all tickets are completed delete `review_findings.md`.
|
||||
|
||||
**Loop Constraints:**
|
||||
- **Iteration Count**: Monitor `"iteration"` in `state.json`. If `"max_iterations"` (if > 0) is reached, you must stop.
|
||||
- **Completion Promise**: If a `"completion_promise"` is defined in `state.json`, you must output `<promise>PROMISE_TEXT</promise>` when the task is genuinely complete.
|
||||
- **Stop Hook**: A hook is active. If you try to exit before completion, you will be forced to continue.
|
||||
|
||||
"""
|
||||
@@ -1,5 +1,11 @@
|
||||
{
|
||||
"experimental": {
|
||||
"skills": true
|
||||
"toolOutputMasking": {
|
||||
"enabled": true
|
||||
},
|
||||
"plan": true
|
||||
},
|
||||
"general": {
|
||||
"devtools": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
---
|
||||
name: docs-changelog
|
||||
description: Provides a step-by-step procedure for generating Gemini CLI changelog files based on github release information.
|
||||
---
|
||||
|
||||
# Procedure: Updating Changelog for New Releases
|
||||
|
||||
The following instructions are run by Gemini CLI when processing new releases.
|
||||
|
||||
## Objective
|
||||
|
||||
To standardize the process of updating the Gemini CLI changelog files for a new
|
||||
release, ensuring accuracy, consistency, and adherence to project style
|
||||
guidelines.
|
||||
|
||||
## Release Types
|
||||
|
||||
This skill covers two types of releases:
|
||||
|
||||
* **Standard Releases:** Regular, versioned releases that are announced to all
|
||||
users. These updates modify `docs/changelogs/latest.md` and
|
||||
`docs/changelogs/index.md`.
|
||||
* **Preview Releases:** Pre-release versions for testing and feedback. These
|
||||
updates only modify `docs/changelogs/preview.md`.
|
||||
|
||||
Ignore all other releases, such as nightly releases.
|
||||
|
||||
### Expected Inputs
|
||||
|
||||
Regardless of the type of release, the following information is expected:
|
||||
|
||||
* **New version number:** The version number for the new release
|
||||
(e.g., `v0.27.0`).
|
||||
* **Release date:** The date of the new release (e.g., `2026-02-03`).
|
||||
* **Raw changelog data:** A list of all pull requests and changes
|
||||
included in the release, in the format `description by @author in
|
||||
#pr_number`.
|
||||
* **Previous version number:** The version number of the last release can be
|
||||
calculated by decreasing the minor version number by one and setting the
|
||||
patch or bug fix version number.
|
||||
|
||||
## Procedure
|
||||
|
||||
### Initial Setup
|
||||
|
||||
1. Identify the files to be modified:
|
||||
|
||||
For standard releases, update `docs/changelogs/latest.md` and
|
||||
`docs/changelogs/index.md`. For preview releases, update
|
||||
`docs/changelogs/preview.md`.
|
||||
|
||||
2. Activate the `docs-writer` skill.
|
||||
|
||||
### Analyze Raw Changelog Data
|
||||
|
||||
1. Review the complete list of changes. If it is a patch or a bug fix with few
|
||||
changes, skip to the "Update `docs/changelogs/latest.md` or
|
||||
`docs/changelogs/preview.md`" section.
|
||||
|
||||
2. Group related changes into high-level categories such as
|
||||
important features, "UI/UX Improvements", and "Bug Fixes". Use the existing
|
||||
announcements in `docs/changelogs/index.md` as an example.
|
||||
|
||||
### Create Highlight Summaries
|
||||
|
||||
Create two distinct versions of the release highlights.
|
||||
|
||||
**Important:** Carefully inspect highlights for "experimental" or
|
||||
"preview" features before public announcement, and do not include them.
|
||||
|
||||
#### Version 1: Comprehensive Highlights (for `latest.md` or `preview.md`)
|
||||
|
||||
Write a detailed summary for each category focusing on user-facing
|
||||
impact.
|
||||
|
||||
#### Version 2: Concise Highlights (for `index.md`)
|
||||
|
||||
Skip this step for preview releases.
|
||||
|
||||
Write concise summaries including the primary PR and author
|
||||
(e.g., `([#12345](link) by @author)`).
|
||||
|
||||
### Update `docs/changelogs/latest.md` or `docs/changelogs/preview.md`
|
||||
|
||||
1. Read current content and use `write_file` to replace it with the new
|
||||
version number, and date.
|
||||
|
||||
If it is a patch or bug fix with few changes, simply add these
|
||||
changes to the "What's Changed" list. Otherwise, replace comprehensive
|
||||
highlights, and the full "What's Changed" list.
|
||||
|
||||
2. For each item in the "What's Changed" list, keep usernames in plaintext, and
|
||||
add github links for each issue number. Example:
|
||||
|
||||
"- feat: implement /rewind command by @username in
|
||||
[#12345](https://github.com/google-gemini/gemini-cli/pull/12345)"
|
||||
|
||||
3. Skip entries by @gemini-cli-robot.
|
||||
|
||||
4. Do not add the "New Contributors" section.
|
||||
|
||||
5. Update the "Full changelog:" link by doing one of following:
|
||||
|
||||
If it is a patch or bug fix with few changes, retain the original link
|
||||
but replace the latter version with the new version. For example, if the
|
||||
patch is version is "v0.28.1", replace the latter version:
|
||||
"https://github.com/google-gemini/gemini-cli/compare/v0.27.0...v0.28.0" with
|
||||
"https://github.com/google-gemini/gemini-cli/compare/v0.27.0...v0.28.1".
|
||||
|
||||
Otherwise, for minor and major version changes, replace the link with the
|
||||
one included at the end of the changelog data.
|
||||
|
||||
6. Ensure lines are wrapped to 80 characters.
|
||||
|
||||
### Update `docs/changelogs/index.md`
|
||||
|
||||
Skip this step for patches, bug fixes, or preview releases.
|
||||
|
||||
Insert a new "Announcements" section for the new version directly
|
||||
above the previous version's section. Ensure lines are wrapped to
|
||||
80 characters.
|
||||
|
||||
### Finalize
|
||||
|
||||
Run `npm run format` to ensure consistency.
|
||||
@@ -1,63 +1,135 @@
|
||||
---
|
||||
name: docs-writer
|
||||
description:
|
||||
Use this skill when asked to write documentation (`/docs` directory)
|
||||
for Gemini CLI.
|
||||
Always use this skill when the task involves writing, reviewing, or editing
|
||||
files in the `/docs` directory or any `.md` files in the repository.
|
||||
---
|
||||
|
||||
# `docs-writer` skill instructions
|
||||
|
||||
As an expert technical writer for the Gemini CLI project, your goal is to
|
||||
produce documentation that is accurate, clear, and consistent with the project's
|
||||
standards. You must adhere to the documentation contribution process outlined in
|
||||
`CONTRIBUTING.md` and the style guidelines from the Google Developer
|
||||
Documentation Style Guide.
|
||||
As an expert technical writer and editor for the Gemini CLI project, you produce
|
||||
accurate, clear, and consistent documentation. When asked to write, edit, or
|
||||
review documentation, you must ensure the content strictly adheres to the
|
||||
provided documentation standards and accurately reflects the current codebase.
|
||||
Adhere to the contribution process in `CONTRIBUTING.md` and the following
|
||||
project standards.
|
||||
|
||||
## Step 1: Understand the goal and create a plan
|
||||
## Phase 1: Documentation standards
|
||||
|
||||
1. **Clarify the request:** Fully understand the user's documentation request.
|
||||
Identify the core feature, command, or concept that needs to be documented.
|
||||
2. **Ask questions:** If the request is ambiguous or lacks detail, ask
|
||||
clarifying questions. Don't invent or assume. It's better to ask than to
|
||||
write incorrect documentation.
|
||||
3. **Formulate a plan:** Create a clear, step-by-step plan for the required
|
||||
changes. If requested or necessary, store this plan in a temporary file or
|
||||
a file identified by the user.
|
||||
Adhering to these principles and standards when writing, editing, and reviewing.
|
||||
|
||||
## Step 2: Investigate and gather information
|
||||
### Voice and tone
|
||||
Adopt a tone that balances professionalism with a helpful, conversational
|
||||
approach.
|
||||
|
||||
1. **Read the code:** Thoroughly examine the relevant codebase, primarily within
|
||||
the `packages/` directory, to ensure your writing is backed by the
|
||||
implementation.
|
||||
2. **Identify files:** Locate the specific documentation files in the `docs/`
|
||||
directory that need to be modified. Always read the latest
|
||||
version of a file before you begin to edit it.
|
||||
3. **Check for connections:** Consider related documentation. If you add a new
|
||||
page, check if `docs/sidebar.json` needs to be updated. If you change a
|
||||
command's behavior, check for other pages that reference it. Make sure links
|
||||
in these pages are up to date.
|
||||
- **Perspective and tense:** Address the reader as "you." Use active voice and
|
||||
present tense (e.g., "The API returns...").
|
||||
- **Tone:** Professional, friendly, and direct.
|
||||
- **Clarity:** Use simple vocabulary. Avoid jargon, slang, and marketing hype.
|
||||
- **Global Audience:** Write in standard US English. Avoid idioms and cultural
|
||||
references.
|
||||
- **Requirements:** Be clear about requirements ("must") vs. recommendations
|
||||
("we recommend"). Avoid "should."
|
||||
- **Word Choice:** Avoid "please" and anthropomorphism (e.g., "the server
|
||||
thinks"). Use contractions (don't, it's).
|
||||
|
||||
## Step 3: Draft the documentation
|
||||
### Language and grammar
|
||||
Write precisely to ensure your instructions are unambiguous.
|
||||
|
||||
1. **Follow the style guide:**
|
||||
- Text must be wrapped at 80 characters. Exceptions are long links or
|
||||
tables, unless otherwise stated by the user.
|
||||
- Use sentence case for headings, titles, and bolded text.
|
||||
- Address the reader as "you".
|
||||
- Use contractions to keep the tone more casual.
|
||||
- Use simple, direct, and active language and the present tense.
|
||||
- Keep paragraphs short and focused.
|
||||
- Always refer to Gemini CLI as `Gemini CLI`, never `the Gemini CLI`.
|
||||
2. **Use `replace` and `write_file`:** Use the file system tools to apply your
|
||||
planned changes precisely. For small edits, `replace` is preferred. For new
|
||||
files or large rewrites, `write_file` is more appropriate.
|
||||
- **Abbreviations:** Avoid Latin abbreviations; use "for example" (not "e.g.")
|
||||
and "that is" (not "i.e.").
|
||||
- **Punctuation:** Use the serial comma. Place periods and commas inside
|
||||
quotation marks.
|
||||
- **Dates:** Use unambiguous formats (e.g., "January 22, 2026").
|
||||
- **Conciseness:** Use "lets you" instead of "allows you to." Use precise,
|
||||
specific verbs.
|
||||
- **Examples:** Use meaningful names in examples; avoid placeholders like
|
||||
"foo" or "bar."
|
||||
|
||||
## Step 4: Verify and finalize
|
||||
### Formatting and syntax
|
||||
Apply consistent formatting to make documentation visually organized and
|
||||
accessible.
|
||||
|
||||
1. **Review your work:** After making changes, re-read the files to ensure the
|
||||
documentation is well-formatted, content is correct and based on existing
|
||||
code, and that all new links are valid.
|
||||
2. **Offer to run npm format:** Once all changes are complete and the user
|
||||
confirms they have no more requests, offer to run the project's formatting
|
||||
script to ensure consistency. Propose the following command:
|
||||
`npm run format`
|
||||
- **Overview paragraphs:** Every heading must be followed by at least one
|
||||
introductory overview paragraph before any lists or sub-headings.
|
||||
- **Text wrap:** Wrap text at 80 characters (except long links or tables).
|
||||
- **Casing:** Use sentence case for headings, titles, and bolded text.
|
||||
- **Naming:** Always refer to the project as `Gemini CLI` (never
|
||||
`the Gemini CLI`).
|
||||
- **Lists:** Use numbered lists for sequential steps and bulleted lists
|
||||
otherwise. Keep list items parallel in structure.
|
||||
- **UI and code:** Use **bold** for UI elements and `code font` for filenames,
|
||||
snippets, commands, and API elements. Focus on the task when discussing
|
||||
interaction.
|
||||
- **Links:** Use descriptive anchor text; avoid "click here." Ensure the link
|
||||
makes sense out of context.
|
||||
- **Accessibility:** Use semantic HTML elements correctly (headings, lists,
|
||||
tables).
|
||||
- **Media:** Use lowercase hyphenated filenames. Provide descriptive alt text
|
||||
for all images.
|
||||
|
||||
### Structure
|
||||
- **BLUF:** Start with an introduction explaining what to expect.
|
||||
- **Experimental features:** If a feature is clearly noted as experimental,
|
||||
add the following note immediately after the introductory paragraph:
|
||||
`> **Note:** This is a preview feature currently under active development.`
|
||||
- **Headings:** Use hierarchical headings to support the user journey.
|
||||
- **Procedures:**
|
||||
- Introduce lists of steps with a complete sentence.
|
||||
- Start each step with an imperative verb.
|
||||
- Number sequential steps; use bullets for non-sequential lists.
|
||||
- Put conditions before instructions (e.g., "On the Settings page, click...").
|
||||
- Provide clear context for where the action takes place.
|
||||
- Indicate optional steps clearly (e.g., "Optional: ...").
|
||||
- **Elements:** Use bullet lists, tables, notes (`> **Note:**`), and warnings
|
||||
(`> **Warning:**`).
|
||||
- **Avoid using a table of contents:** If a table of contents is present, remove
|
||||
it.
|
||||
- **Next steps:** Conclude with a "Next steps" section if applicable.
|
||||
|
||||
## Phase 2: Preparation
|
||||
Before modifying any documentation, thoroughly investigate the request and the
|
||||
surrounding context.
|
||||
|
||||
1. **Clarify:** Understand the core request. Differentiate between writing new
|
||||
content and editing existing content. If the request is ambiguous (e.g.,
|
||||
"fix the docs"), ask for clarification.
|
||||
2. **Investigate:** Examine relevant code (primarily in `packages/`) for
|
||||
accuracy.
|
||||
3. **Audit:** Read the latest versions of relevant files in `docs/`.
|
||||
4. **Connect:** Identify all referencing pages if changing behavior. Check if
|
||||
`docs/sidebar.json` needs updates.
|
||||
5. **Plan:** Create a step-by-step plan before making changes.
|
||||
|
||||
## Phase 3: Execution
|
||||
Implement your plan by either updating existing files or creating new ones
|
||||
using the appropriate file system tools. Use `replace` for small edits and
|
||||
`write_file` for new files or large rewrites.
|
||||
|
||||
### Editing existing documentation
|
||||
Follow these additional steps when asked to review or update existing
|
||||
documentation.
|
||||
|
||||
- **Gaps:** Identify areas where the documentation is incomplete or no longer
|
||||
reflects existing code.
|
||||
- **Structure:** Apply "Structure (New Docs)" rules (BLUF, headings, etc.) when
|
||||
adding new sections to existing pages.
|
||||
- **Tone:** Ensure the tone is active and engaging. Use "you" and contractions.
|
||||
- **Clarity:** Correct awkward wording, spelling, and grammar. Rephrase
|
||||
sentences to make them easier for users to understand.
|
||||
- **Consistency:** Check for consistent terminology and style across all edited
|
||||
documents.
|
||||
|
||||
|
||||
## Phase 4: Verification and finalization
|
||||
Perform a final quality check to ensure that all changes are correctly formatted
|
||||
and that all links are functional.
|
||||
|
||||
1. **Accuracy:** Ensure content accurately reflects the implementation and
|
||||
technical behavior.
|
||||
2. **Self-review:** Re-read changes for formatting, correctness, and flow.
|
||||
3. **Link check:** Verify all new and existing links leading to or from modified
|
||||
pages.
|
||||
4. **Format:** Once all changes are complete, ask to execute `npm run format`
|
||||
to ensure consistent formatting across the project. If the user confirms,
|
||||
execute the command.
|
||||
|
||||
@@ -14,25 +14,34 @@ repository's standards.
|
||||
|
||||
Follow these steps to create a Pull Request:
|
||||
|
||||
1. **Branch Management**: Check the current branch to avoid working directly
|
||||
on `main`.
|
||||
1. **Branch Management**: **CRITICAL:** Ensure you are NOT working on the
|
||||
`main` branch.
|
||||
- Run `git branch --show-current`.
|
||||
- If the current branch is `main`, create and switch to a new descriptive
|
||||
branch:
|
||||
- If the current branch is `main`, you MUST create and switch to a new
|
||||
descriptive branch:
|
||||
```bash
|
||||
git checkout -b <new-branch-name>
|
||||
```
|
||||
|
||||
2. **Locate Template**: Search for a pull request template in the repository.
|
||||
2. **Commit Changes**: Verify that all intended changes are committed.
|
||||
- Run `git status` to check for unstaged or uncommitted changes.
|
||||
- If there are uncommitted changes, stage and commit them with a descriptive
|
||||
message before proceeding. NEVER commit directly to `main`.
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "type(scope): description"
|
||||
```
|
||||
|
||||
3. **Locate Template**: Search for a pull request template in the repository.
|
||||
- Check `.github/pull_request_template.md`
|
||||
- Check `.github/PULL_REQUEST_TEMPLATE.md`
|
||||
- If multiple templates exist (e.g., in `.github/PULL_REQUEST_TEMPLATE/`),
|
||||
ask the user which one to use or select the most appropriate one based on
|
||||
the context (e.g., `bug_fix.md` vs `feature.md`).
|
||||
|
||||
3. **Read Template**: Read the content of the identified template file.
|
||||
4. **Read Template**: Read the content of the identified template file.
|
||||
|
||||
4. **Draft Description**: Create a PR description that strictly follows the
|
||||
5. **Draft Description**: Create a PR description that strictly follows the
|
||||
template's structure.
|
||||
- **Headings**: Keep all headings from the template.
|
||||
- **Checklists**: Review each item. Mark with `[x]` if completed. If an item
|
||||
@@ -44,14 +53,24 @@ Follow these steps to create a Pull Request:
|
||||
- **Related Issues**: Link any issues fixed or related to this PR (e.g.,
|
||||
"Fixes #123").
|
||||
|
||||
5. **Preflight Check**: Before creating the PR, run the workspace preflight
|
||||
6. **Preflight Check**: Before creating the PR, run the workspace preflight
|
||||
script to ensure all build, lint, and test checks pass.
|
||||
```bash
|
||||
npm run preflight
|
||||
```
|
||||
If any checks fail, address the issues before proceeding to create the PR.
|
||||
|
||||
6. **Create PR**: Use the `gh` CLI to create the PR. To avoid shell escaping
|
||||
7. **Push Branch**: Push the current branch to the remote repository.
|
||||
**CRITICAL SAFETY RAIL:** Double-check your branch name before pushing.
|
||||
NEVER push if the current branch is `main`.
|
||||
```bash
|
||||
# Verify current branch is NOT main
|
||||
git branch --show-current
|
||||
# Push non-interactively
|
||||
git push -u origin HEAD
|
||||
```
|
||||
|
||||
8. **Create PR**: Use the `gh` CLI to create the PR. To avoid shell escaping
|
||||
issues with multi-line Markdown, write the description to a temporary file
|
||||
first.
|
||||
```bash
|
||||
@@ -68,6 +87,7 @@ Follow these steps to create a Pull Request:
|
||||
|
||||
## Principles
|
||||
|
||||
- **Safety First**: NEVER push to `main`. This is your highest priority.
|
||||
- **Compliance**: Never ignore the PR template. It exists for a reason.
|
||||
- **Completeness**: Fill out all relevant sections.
|
||||
- **Accuracy**: Don't check boxes for tasks you haven't done.
|
||||
|
||||
@@ -28,7 +28,7 @@ body:
|
||||
id: 'info'
|
||||
attributes:
|
||||
label: 'Client information'
|
||||
description: 'Please paste the full text from the `/about` command run from Gemini CLI. Also include which platform (macOS, Windows, Linux).'
|
||||
description: 'Please paste the full text from the `/about` command run from Gemini CLI. Also include which platform (macOS, Windows, Linux). Note that this output contains your email address. Consider removing it before submitting.'
|
||||
value: |-
|
||||
<details>
|
||||
<summary>Client Information</summary>
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||
/* global process, console, require */
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
const { Octokit } = require('@octokit/rest');
|
||||
|
||||
/**
|
||||
|
||||
@@ -177,6 +177,19 @@ jobs:
|
||||
- name: 'Smoke test bundle'
|
||||
run: 'node ./bundle/gemini.js --version'
|
||||
|
||||
- name: 'Smoke test npx installation'
|
||||
run: |
|
||||
# 1. Package the project into a tarball
|
||||
TARBALL=$(npm pack | tail -n 1)
|
||||
|
||||
# 2. Move to a fresh directory for isolation
|
||||
mkdir -p ../smoke-test-dir
|
||||
mv "$TARBALL" ../smoke-test-dir/
|
||||
cd ../smoke-test-dir
|
||||
|
||||
# 3. Run npx from the tarball
|
||||
npx "./$TARBALL" --version
|
||||
|
||||
- name: 'Wait for file system sync'
|
||||
run: 'sleep 2'
|
||||
|
||||
@@ -252,6 +265,19 @@ jobs:
|
||||
- name: 'Smoke test bundle'
|
||||
run: 'node ./bundle/gemini.js --version'
|
||||
|
||||
- name: 'Smoke test npx installation'
|
||||
run: |
|
||||
# 1. Package the project into a tarball
|
||||
TARBALL=$(npm pack | tail -n 1)
|
||||
|
||||
# 2. Move to a fresh directory for isolation
|
||||
mkdir -p ../smoke-test-dir
|
||||
mv "$TARBALL" ../smoke-test-dir/
|
||||
cd ../smoke-test-dir
|
||||
|
||||
# 3. Run npx from the tarball
|
||||
npx "./$TARBALL" --version
|
||||
|
||||
- name: 'Wait for file system sync'
|
||||
run: 'sleep 2'
|
||||
|
||||
@@ -330,11 +356,17 @@ jobs:
|
||||
clean-script: 'clean'
|
||||
|
||||
test_windows:
|
||||
name: 'Slow Test - Win'
|
||||
name: 'Slow Test - Win - ${{ matrix.shard }}'
|
||||
runs-on: 'gemini-cli-windows-16-core'
|
||||
needs: 'merge_queue_skipper'
|
||||
if: "${{needs.merge_queue_skipper.outputs.skip == 'false'}}"
|
||||
continue-on-error: true
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
matrix:
|
||||
shard:
|
||||
- 'cli'
|
||||
- 'others'
|
||||
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
@@ -385,7 +417,14 @@ jobs:
|
||||
NODE_OPTIONS: '--max-old-space-size=32768 --max-semi-space-size=256'
|
||||
UV_THREADPOOL_SIZE: '32'
|
||||
NODE_ENV: 'test'
|
||||
run: 'npm run test:ci -- --coverage.enabled=false'
|
||||
run: |
|
||||
if ("${{ matrix.shard }}" -eq "cli") {
|
||||
npm run test:ci --workspace @google/gemini-cli -- --coverage.enabled=false
|
||||
} else {
|
||||
# Explicitly list non-cli packages to ensure they are sharded correctly
|
||||
npm run test:ci --workspace @google/gemini-cli-core --workspace @google/gemini-cli-a2a-server --workspace gemini-cli-vscode-ide-companion --workspace @google/gemini-cli-test-utils --if-present -- --coverage.enabled=false
|
||||
npm run test:scripts
|
||||
}
|
||||
shell: 'pwsh'
|
||||
|
||||
- name: 'Bundle'
|
||||
@@ -396,6 +435,21 @@ jobs:
|
||||
run: 'node ./bundle/gemini.js --version'
|
||||
shell: 'pwsh'
|
||||
|
||||
- name: 'Smoke test npx installation'
|
||||
run: |
|
||||
# 1. Package the project into a tarball
|
||||
$PACK_OUTPUT = npm pack
|
||||
$TARBALL = $PACK_OUTPUT[-1]
|
||||
|
||||
# 2. Move to a fresh directory for isolation
|
||||
New-Item -ItemType Directory -Force -Path ../smoke-test-dir
|
||||
Move-Item $TARBALL ../smoke-test-dir/
|
||||
Set-Location ../smoke-test-dir
|
||||
|
||||
# 3. Run npx from the tarball
|
||||
npx "./$TARBALL" --version
|
||||
shell: 'pwsh'
|
||||
|
||||
ci:
|
||||
name: 'CI'
|
||||
if: 'always()'
|
||||
|
||||
@@ -43,19 +43,23 @@ jobs:
|
||||
|
||||
// 1. Fetch maintainers for verification
|
||||
let maintainerLogins = new Set();
|
||||
let teamFetchSucceeded = false;
|
||||
try {
|
||||
const members = await github.paginate(github.rest.teams.listMembersInOrg, {
|
||||
org: context.repo.owner,
|
||||
team_slug: 'gemini-cli-maintainers'
|
||||
});
|
||||
maintainerLogins = new Set(members.map(m => m.login));
|
||||
maintainerLogins = new Set(members.map(m => m.login.toLowerCase()));
|
||||
teamFetchSucceeded = true;
|
||||
core.info(`Successfully fetched ${maintainerLogins.size} team members from gemini-cli-maintainers`);
|
||||
} catch (e) {
|
||||
core.warning('Failed to fetch team members');
|
||||
core.warning(`Failed to fetch team members from gemini-cli-maintainers: ${e.message}. Falling back to author_association only.`);
|
||||
}
|
||||
|
||||
const isMaintainer = (login, assoc) => {
|
||||
if (maintainerLogins.size > 0) return maintainerLogins.has(login);
|
||||
return ['OWNER', 'MEMBER', 'COLLABORATOR'].includes(assoc);
|
||||
const isTeamMember = maintainerLogins.has(login.toLowerCase());
|
||||
const isRepoMaintainer = ['OWNER', 'MEMBER', 'COLLABORATOR'].includes(assoc);
|
||||
return isTeamMember || isRepoMaintainer;
|
||||
};
|
||||
|
||||
// 2. Determine which PRs to check
|
||||
@@ -153,7 +157,17 @@ jobs:
|
||||
const labels = pr.labels.map(l => l.name.toLowerCase());
|
||||
if (labels.includes('help wanted') || labels.includes('🔒 maintainer only')) continue;
|
||||
|
||||
let lastActivity = new Date(0);
|
||||
// Skip PRs that were created less than 30 days ago - they cannot be stale yet
|
||||
const prCreatedAt = new Date(pr.created_at);
|
||||
if (prCreatedAt > thirtyDaysAgo) {
|
||||
const daysOld = Math.floor((Date.now() - prCreatedAt.getTime()) / (1000 * 60 * 60 * 24));
|
||||
core.info(`PR #${pr.number} was created ${daysOld} days ago. Skipping staleness check.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Initialize lastActivity to PR creation date (not epoch) as a safety baseline.
|
||||
// This ensures we never incorrectly mark a PR as stale due to failed activity lookups.
|
||||
let lastActivity = new Date(pr.created_at);
|
||||
try {
|
||||
const reviews = await github.paginate(github.rest.pulls.listReviews, {
|
||||
owner: context.repo.owner,
|
||||
@@ -177,8 +191,12 @@ jobs:
|
||||
if (d > lastActivity) lastActivity = d;
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
} catch (e) {
|
||||
core.warning(`Failed to fetch reviews/comments for PR #${pr.number}: ${e.message}`);
|
||||
}
|
||||
|
||||
// For maintainer PRs, the PR creation itself counts as maintainer activity.
|
||||
// (Now redundant since we initialize to pr.created_at, but kept for clarity)
|
||||
if (maintainerPr) {
|
||||
const d = new Date(pr.created_at);
|
||||
if (d > lastActivity) lastActivity = d;
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
# This workflow is triggered on every new release.
|
||||
# It uses Gemini to generate release notes and creates a PR with the changes.
|
||||
name: 'Generate Release Notes'
|
||||
|
||||
on:
|
||||
release:
|
||||
types: ['published']
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'New version (e.g., v1.2.3)'
|
||||
required: true
|
||||
type: 'string'
|
||||
body:
|
||||
description: 'Release notes body'
|
||||
required: true
|
||||
type: 'string'
|
||||
time:
|
||||
description: 'Release time'
|
||||
required: true
|
||||
type: 'string'
|
||||
|
||||
jobs:
|
||||
generate-release-notes:
|
||||
runs-on: 'ubuntu-latest'
|
||||
permissions:
|
||||
contents: 'write'
|
||||
pull-requests: 'write'
|
||||
steps:
|
||||
- name: 'Checkout repository'
|
||||
uses: 'actions/checkout@v4'
|
||||
with:
|
||||
# The user-level skills need to be available to the workflow
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 'Set up Node.js'
|
||||
uses: 'actions/setup-node@v4'
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: 'Get release information'
|
||||
id: 'release_info'
|
||||
run: |
|
||||
VERSION="${{ github.event.inputs.version || github.event.release.tag_name }}"
|
||||
BODY="${{ github.event.inputs.body || github.event.release.body }}"
|
||||
TIME="${{ github.event.inputs.time || github.event.release.created_at }}"
|
||||
|
||||
echo "VERSION=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "TIME=${TIME}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# Use a heredoc to preserve multiline release body
|
||||
echo 'RAW_CHANGELOG<<EOF' >> "$GITHUB_OUTPUT"
|
||||
echo "${BODY}" >> "$GITHUB_OUTPUT"
|
||||
echo 'EOF' >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
GH_TOKEN: '${{ secrets.GEMINI_CLI_ROBOT_GITHUB_PAT }}'
|
||||
|
||||
- name: 'Generate Changelog with Gemini'
|
||||
uses: 'google-github-actions/run-gemini-cli@a3bf79042542528e91937b3a3a6fbc4967ee3c31' # ratchet:google-github-actions/run-gemini-cli@v0
|
||||
with:
|
||||
gemini_api_key: '${{ secrets.GEMINI_API_KEY }}'
|
||||
prompt: |
|
||||
Activate the 'docs-changelog' skill.
|
||||
|
||||
**Release Information:**
|
||||
- New Version: ${{ steps.release_info.outputs.VERSION }}
|
||||
- Release Date: ${{ steps.release_info.outputs.TIME }}
|
||||
- Raw Changelog Data: ${{ steps.release_info.outputs.RAW_CHANGELOG }}
|
||||
|
||||
Execute the release notes generation process using the information provided.
|
||||
|
||||
- name: 'Create Pull Request'
|
||||
uses: 'peter-evans/create-pull-request@v6'
|
||||
with:
|
||||
token: '${{ secrets.GEMINI_CLI_ROBOT_GITHUB_PAT }}'
|
||||
commit-message: 'docs(changelog): update for ${{ steps.release_info.outputs.VERSION }}'
|
||||
title: 'Changelog for ${{ steps.release_info.outputs.VERSION }}'
|
||||
body: |
|
||||
This PR contains the auto-generated changelog for the ${{ steps.release_info.outputs.VERSION }} release.
|
||||
|
||||
Please review and merge.
|
||||
branch: 'changelog-${{ steps.release_info.outputs.VERSION }}'
|
||||
team-reviewers: 'gemini-cli-docs, gemini-cli-maintainers'
|
||||
delete-branch: true
|
||||
@@ -29,7 +29,11 @@ on:
|
||||
jobs:
|
||||
verify-release:
|
||||
environment: "${{ github.event.inputs.environment || 'prod' }}"
|
||||
runs-on: 'ubuntu-latest'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: ['ubuntu-latest', 'macos-latest', 'windows-latest']
|
||||
runs-on: '${{ matrix.os }}'
|
||||
permissions:
|
||||
contents: 'read'
|
||||
packages: 'write'
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ The process for contributing code is as follows:
|
||||
`🔒Maintainers only`, this means it is reserved for project maintainers. We
|
||||
will not accept pull requests related to these issues. In the near future,
|
||||
we will explicitly mark issues looking for contributions using the
|
||||
`help wanted` label. If you believe an issue is a good candidate for
|
||||
`help-wanted` label. If you believe an issue is a good candidate for
|
||||
community contribution, please leave a comment on the issue. A maintainer
|
||||
will review it and apply the `help-wanted` label if appropriate. Only
|
||||
maintainers should attempt to add the `help-wanted` label to an issue.
|
||||
|
||||
@@ -52,15 +52,24 @@ powerful tool for developers.
|
||||
|
||||
## Development Conventions
|
||||
|
||||
- **Legacy Snippets:** `packages/core/src/prompts/snippets.legacy.ts` is a
|
||||
snapshot of an older system prompt. Avoid changing the prompting verbiage to
|
||||
preserve its historical behavior; however, structural changes to ensure
|
||||
compilation or simplify the code are permitted.
|
||||
- **Contributions:** Follow the process outlined in `CONTRIBUTING.md`. Requires
|
||||
signing the Google CLA.
|
||||
- **Pull Requests:** Keep PRs small, focused, and linked to an existing issue.
|
||||
Always activate the `pr-creator` skill for PR generation, even when using the
|
||||
`gh` CLI.
|
||||
- **Commit Messages:** Follow the
|
||||
[Conventional Commits](https://www.conventionalcommits.org/) standard.
|
||||
- **Coding Style:** Adhere to existing patterns in `packages/cli` (React/Ink)
|
||||
and `packages/core` (Backend logic).
|
||||
- **Imports:** Use specific imports and avoid restricted relative imports
|
||||
between packages (enforced by ESLint).
|
||||
- **License Headers:** For all new source code files (`.ts`, `.tsx`, `.js`),
|
||||
include the Apache-2.0 license header with the current year. (e.g.,
|
||||
`Copyright 2026 Google LLC`). This is enforced by ESLint.
|
||||
|
||||
## Testing Conventions
|
||||
|
||||
@@ -72,7 +81,8 @@ powerful tool for developers.
|
||||
|
||||
## Documentation
|
||||
|
||||
- Always use the `docs-writer` skill when you are asked to write, edit, or
|
||||
review any documentation.
|
||||
- Documentation is located in the `docs/` directory.
|
||||
- Suggest documentation updates when code changes render existing documentation
|
||||
obsolete or incomplete.
|
||||
- Located in the `docs/` directory.
|
||||
- Use the `docs-writer` skill.
|
||||
|
||||
@@ -18,6 +18,39 @@ on GitHub.
|
||||
| [Preview](preview.md) | Experimental features ready for early feedback. |
|
||||
| [Stable](latest.md) | Stable, recommended for general use. |
|
||||
|
||||
## Announcements: v0.27.0 - 2026-02-03
|
||||
|
||||
- **Event-Driven Architecture:** The CLI now uses a new event-driven scheduler
|
||||
for tool execution, resulting in a more responsive and performant experience
|
||||
([#17078](https://github.com/google-gemini/gemini-cli/pull/17078) by
|
||||
@abhipatel12).
|
||||
- **Enhanced User Experience:** This release includes queued tool confirmations,
|
||||
and expandable large text pastes for a smoother workflow.
|
||||
- **New `/rewind` Command:** Easily navigate your session history with the new
|
||||
`/rewind` command
|
||||
([#15720](https://github.com/google-gemini/gemini-cli/pull/15720) by
|
||||
@Adib234).
|
||||
- **Linux Clipboard Support:** You can now paste images on Linux with Wayland
|
||||
and X11 ([#17144](https://github.com/google-gemini/gemini-cli/pull/17144) by
|
||||
@devr0306).
|
||||
|
||||
## Announcements: v0.26.0 - 2026-01-27
|
||||
|
||||
- **Agents and Skills:** We've introduced a new `skill-creator` skill
|
||||
([#16394](https://github.com/google-gemini/gemini-cli/pull/16394) by
|
||||
@NTaylorMullen), enabled agent skills by default, and added a generalist agent
|
||||
to improve task routing
|
||||
([#16638](https://github.com/google-gemini/gemini-cli/pull/16638) by
|
||||
@joshualitt).
|
||||
- **UI/UX Improvements:** You can now "Rewind" through your conversation history
|
||||
([#15717](https://github.com/google-gemini/gemini-cli/pull/15717) by @Adib234)
|
||||
and use a new `/introspect` command for debugging.
|
||||
- **Core and Scheduler Refactoring:** The core scheduler has been significantly
|
||||
refactored to improve performance and reliability
|
||||
([#16895](https://github.com/google-gemini/gemini-cli/pull/16895) by
|
||||
@abhipatel12), and numerous performance and stability fixes have been
|
||||
included.
|
||||
|
||||
## Announcements: v0.25.0 - 2026-01-20
|
||||
|
||||
- **Skills and Agents Improvements:** We've enhanced the `activate_skill` tool,
|
||||
|
||||
+426
-349
@@ -1,6 +1,6 @@
|
||||
# Latest stable release: v0.25.0
|
||||
# Latest stable release: v0.27.0
|
||||
|
||||
Released: January 20, 2026
|
||||
Released: February 3, 2026
|
||||
|
||||
For most users, our latest stable release is the recommended release. Install
|
||||
the latest stable version with:
|
||||
@@ -11,360 +11,437 @@ npm install -g @google/gemini-cli
|
||||
|
||||
## Highlights
|
||||
|
||||
- **Skills and Agents Improvements:** Enhanced `activate_skill` tool, new
|
||||
`pr-creator` skill, default enablement of skills, improved `cli_help` agent,
|
||||
and a new `/agents refresh` command.
|
||||
- **UI/UX Refinements:** Transparent feedback for skills, ability to switch
|
||||
focus between shell and input with Tab, and dynamic terminal tab titles.
|
||||
- **Core Functionality & Performance:** Support for built-in agent skills,
|
||||
refined Gemini 3 system instructions, caching ignore instances for
|
||||
performance, and improved retry mechanisms.
|
||||
- **Bug Fixes and Stability:** Numerous bug fixes across the CLI, core, and
|
||||
workflows, including issues with subagent delegation, unicode character
|
||||
crashes, and sticky header regressions.
|
||||
- **Event-Driven Architecture:** The CLI now uses an event-driven scheduler for
|
||||
tool execution, improving performance and responsiveness. This includes
|
||||
migrating non-interactive flows and sub-agents to the new scheduler.
|
||||
- **Enhanced User Experience:** This release introduces several UI/UX
|
||||
improvements, including queued tool confirmations and the ability to expand
|
||||
and collapse large pasted text blocks. The `Settings` dialog has been improved
|
||||
to reduce jitter and preserve focus.
|
||||
- **Agent and Skill Improvements:** Agent Skills have been promoted to a stable
|
||||
feature. Sub-agents now use a JSON schema for input and are tracked by an
|
||||
`AgentRegistry`.
|
||||
- **New `/rewind` Command:** A new `/rewind` command has been implemented to
|
||||
allow users to go back in their session history.
|
||||
- **Improved Shell and File Handling:** The shell tool's output format has been
|
||||
optimized, and the CLI now gracefully handles disk-full errors during chat
|
||||
recording. A bug in detecting already added paths has been fixed.
|
||||
- **Linux Clipboard Support:** Image pasting capabilities for Wayland and X11 on
|
||||
Linux have been added.
|
||||
|
||||
## What's Changed
|
||||
|
||||
- feat(core): improve activate_skill tool and use lowercase XML tags by
|
||||
@NTaylorMullen in
|
||||
[#16009](https://github.com/google-gemini/gemini-cli/pull/16009)
|
||||
- Add initiation method telemetry property by @gundermanc in
|
||||
[#15818](https://github.com/google-gemini/gemini-cli/pull/15818)
|
||||
- chore(release): bump version to 0.25.0-nightly.20260107.59a18e710 by
|
||||
- remove fireAgent and beforeAgent hook by @ishaanxgupta in
|
||||
[#16919](https://github.com/google-gemini/gemini-cli/pull/16919)
|
||||
- Remove unused modelHooks and toolHooks by @ved015 in
|
||||
[#17115](https://github.com/google-gemini/gemini-cli/pull/17115)
|
||||
- feat(cli): sanitize ANSI escape sequences in non-interactive output by
|
||||
@sehoon38 in [#17172](https://github.com/google-gemini/gemini-cli/pull/17172)
|
||||
- Update Attempt text to Retry when showing the retry happening to the … by
|
||||
@sehoon38 in [#17178](https://github.com/google-gemini/gemini-cli/pull/17178)
|
||||
- chore(skills): update pr-creator skill workflow by @sehoon38 in
|
||||
[#17180](https://github.com/google-gemini/gemini-cli/pull/17180)
|
||||
- feat(cli): implement event-driven tool execution scheduler by @abhipatel12 in
|
||||
[#17078](https://github.com/google-gemini/gemini-cli/pull/17078)
|
||||
- chore(release): bump version to 0.27.0-nightly.20260121.97aac696f by
|
||||
@gemini-cli-robot in
|
||||
[#16048](https://github.com/google-gemini/gemini-cli/pull/16048)
|
||||
- Hx support by @kevinfjiang in
|
||||
[#16032](https://github.com/google-gemini/gemini-cli/pull/16032)
|
||||
- [Skills] Foundation: Centralize management logic and feedback rendering by
|
||||
[#17181](https://github.com/google-gemini/gemini-cli/pull/17181)
|
||||
- Remove other rewind reference in docs by @chrstnb in
|
||||
[#17149](https://github.com/google-gemini/gemini-cli/pull/17149)
|
||||
- feat(skills): add code-reviewer skill by @sehoon38 in
|
||||
[#17187](https://github.com/google-gemini/gemini-cli/pull/17187)
|
||||
- feat(plan): Extend Shift+Tab Mode Cycling to include Plan Mode by @Adib234 in
|
||||
[#17177](https://github.com/google-gemini/gemini-cli/pull/17177)
|
||||
- feat(plan): refactor TestRig and eval helper to support configurable approval
|
||||
modes by @jerop in
|
||||
[#17171](https://github.com/google-gemini/gemini-cli/pull/17171)
|
||||
- feat(workflows): support recursive workstream labeling and new IDs by
|
||||
@bdmorgan in [#17207](https://github.com/google-gemini/gemini-cli/pull/17207)
|
||||
- Run evals for all models. by @gundermanc in
|
||||
[#17123](https://github.com/google-gemini/gemini-cli/pull/17123)
|
||||
- fix(github): improve label-workstream-rollup efficiency with GraphQL by
|
||||
@bdmorgan in [#17217](https://github.com/google-gemini/gemini-cli/pull/17217)
|
||||
- Docs: Update changelogs for v.0.25.0 and v0.26.0-preview.0 releases. by
|
||||
@g-samroberts in
|
||||
[#17215](https://github.com/google-gemini/gemini-cli/pull/17215)
|
||||
- Migrate beforeTool and afterTool hooks to hookSystem by @ved015 in
|
||||
[#17204](https://github.com/google-gemini/gemini-cli/pull/17204)
|
||||
- fix(github): improve label-workstream-rollup efficiency and fix bugs by
|
||||
@bdmorgan in [#17219](https://github.com/google-gemini/gemini-cli/pull/17219)
|
||||
- feat(cli): improve skill enablement/disablement verbiage by @NTaylorMullen in
|
||||
[#17192](https://github.com/google-gemini/gemini-cli/pull/17192)
|
||||
- fix(admin): Ensure CLI commands run in non-interactive mode by @skeshive in
|
||||
[#17218](https://github.com/google-gemini/gemini-cli/pull/17218)
|
||||
- feat(core): support dynamic variable substitution in system prompt override by
|
||||
@NTaylorMullen in
|
||||
[#15952](https://github.com/google-gemini/gemini-cli/pull/15952)
|
||||
- Introduce GEMINI_CLI_HOME for strict test isolation by @NTaylorMullen in
|
||||
[#15907](https://github.com/google-gemini/gemini-cli/pull/15907)
|
||||
- [Skills] Multi-scope skill enablement and shadowing fix by @NTaylorMullen in
|
||||
[#15953](https://github.com/google-gemini/gemini-cli/pull/15953)
|
||||
- policy: extract legacy policy from core tool scheduler to policy engine by
|
||||
[#17042](https://github.com/google-gemini/gemini-cli/pull/17042)
|
||||
- fix(core,cli): enable recursive directory access for by @galz10 in
|
||||
[#17094](https://github.com/google-gemini/gemini-cli/pull/17094)
|
||||
- Docs: Marking for experimental features by @jkcinouye in
|
||||
[#16760](https://github.com/google-gemini/gemini-cli/pull/16760)
|
||||
- Support command/ctrl/alt backspace correctly by @scidomino in
|
||||
[#17175](https://github.com/google-gemini/gemini-cli/pull/17175)
|
||||
- feat(plan): add approval mode instructions to system prompt by @jerop in
|
||||
[#17151](https://github.com/google-gemini/gemini-cli/pull/17151)
|
||||
- feat(core): enable disableLLMCorrection by default by @SandyTao520 in
|
||||
[#17223](https://github.com/google-gemini/gemini-cli/pull/17223)
|
||||
- Remove unused slug from sidebar by @chrstnb in
|
||||
[#17229](https://github.com/google-gemini/gemini-cli/pull/17229)
|
||||
- drain stdin on exit by @scidomino in
|
||||
[#17241](https://github.com/google-gemini/gemini-cli/pull/17241)
|
||||
- refactor(cli): decouple UI from live tool execution via ToolActionsContext by
|
||||
@abhipatel12 in
|
||||
[#15902](https://github.com/google-gemini/gemini-cli/pull/15902)
|
||||
- Enhance TestRig with process management and timeouts by @NTaylorMullen in
|
||||
[#15908](https://github.com/google-gemini/gemini-cli/pull/15908)
|
||||
- Update troubleshooting doc for UNABLE_TO_GET_ISSUER_CERT_LOCALLY by @sehoon38
|
||||
in [#16069](https://github.com/google-gemini/gemini-cli/pull/16069)
|
||||
- Add keytar to dependencies by @chrstnb in
|
||||
[#15928](https://github.com/google-gemini/gemini-cli/pull/15928)
|
||||
- Simplify extension settings command by @chrstnb in
|
||||
[#16001](https://github.com/google-gemini/gemini-cli/pull/16001)
|
||||
- feat(admin): implement extensions disabled by @skeshive in
|
||||
[#16024](https://github.com/google-gemini/gemini-cli/pull/16024)
|
||||
- Core data structure updates for Rewind functionality by @Adib234 in
|
||||
[#15714](https://github.com/google-gemini/gemini-cli/pull/15714)
|
||||
- feat(hooks): simplify hook firing with HookSystem wrapper methods by @ved015
|
||||
in [#15982](https://github.com/google-gemini/gemini-cli/pull/15982)
|
||||
- Add exp.gws_experiment field to LogEventEntry by @gsquared94 in
|
||||
[#16062](https://github.com/google-gemini/gemini-cli/pull/16062)
|
||||
- Revert "feat(admin): implement extensions disabled" by @chrstnb in
|
||||
[#16082](https://github.com/google-gemini/gemini-cli/pull/16082)
|
||||
- feat(core): Decouple enabling hooks UI from subsystem. by @joshualitt in
|
||||
[#16074](https://github.com/google-gemini/gemini-cli/pull/16074)
|
||||
- docs: add docs for hooks + extensions by @abhipatel12 in
|
||||
[#16073](https://github.com/google-gemini/gemini-cli/pull/16073)
|
||||
- feat(core): Preliminary changes for subagent model routing. by @joshualitt in
|
||||
[#16035](https://github.com/google-gemini/gemini-cli/pull/16035)
|
||||
- Optimize CI workflow: Parallelize jobs and cache linters by @NTaylorMullen in
|
||||
[#16054](https://github.com/google-gemini/gemini-cli/pull/16054)
|
||||
- Add option to fallback for capacity errors in ProQuotaDi… by @sehoon38 in
|
||||
[#16050](https://github.com/google-gemini/gemini-cli/pull/16050)
|
||||
- feat: add confirmation details support + jsonrpc vs http rest support by
|
||||
@adamfweidman in
|
||||
[#16079](https://github.com/google-gemini/gemini-cli/pull/16079)
|
||||
- fix(workflows): fix and limit labels for pr-triage.sh script by @jacob314 in
|
||||
[#16096](https://github.com/google-gemini/gemini-cli/pull/16096)
|
||||
- Fix and rename introspection agent -> cli help agent by @scidomino in
|
||||
[#16097](https://github.com/google-gemini/gemini-cli/pull/16097)
|
||||
- Docs: Changelogs update 20260105 by @jkcinouye in
|
||||
[#15937](https://github.com/google-gemini/gemini-cli/pull/15937)
|
||||
- enable cli_help agent by default by @scidomino in
|
||||
[#16100](https://github.com/google-gemini/gemini-cli/pull/16100)
|
||||
- Optimize json-output tests with mock responses by @NTaylorMullen in
|
||||
[#16102](https://github.com/google-gemini/gemini-cli/pull/16102)
|
||||
- Fix CI for forks by @scidomino in
|
||||
[#16113](https://github.com/google-gemini/gemini-cli/pull/16113)
|
||||
- Reduce nags about PRs that reference issues but don't fix them. by @jacob314
|
||||
in [#16112](https://github.com/google-gemini/gemini-cli/pull/16112)
|
||||
- feat(cli): add filepath autosuggestion after slash commands by @jasmeetsb in
|
||||
[#14738](https://github.com/google-gemini/gemini-cli/pull/14738)
|
||||
- Add upgrade option for paid users by @cayden-google in
|
||||
[#15978](https://github.com/google-gemini/gemini-cli/pull/15978)
|
||||
- [Skills] UX Polishing: Transparent feedback and CLI refinements by
|
||||
[#17183](https://github.com/google-gemini/gemini-cli/pull/17183)
|
||||
- fix(core): update token count and telemetry on /chat resume history load by
|
||||
@psinha40898 in
|
||||
[#16279](https://github.com/google-gemini/gemini-cli/pull/16279)
|
||||
- fix: /policy to display policies according to mode by @ishaanxgupta in
|
||||
[#16772](https://github.com/google-gemini/gemini-cli/pull/16772)
|
||||
- fix(core): simplify replace tool error message by @SandyTao520 in
|
||||
[#17246](https://github.com/google-gemini/gemini-cli/pull/17246)
|
||||
- feat(cli): consolidate shell inactivity and redirection monitoring by
|
||||
@NTaylorMullen in
|
||||
[#15954](https://github.com/google-gemini/gemini-cli/pull/15954)
|
||||
- Polish: Move 'Failed to load skills' warning to debug logs by @NTaylorMullen
|
||||
in [#16142](https://github.com/google-gemini/gemini-cli/pull/16142)
|
||||
- feat(cli): export chat history in /bug and prefill GitHub issue by
|
||||
@NTaylorMullen in
|
||||
[#16115](https://github.com/google-gemini/gemini-cli/pull/16115)
|
||||
- bug(core): fix issue with overrides to bases. by @joshualitt in
|
||||
[#15255](https://github.com/google-gemini/gemini-cli/pull/15255)
|
||||
- enableInteractiveShell for external tooling relying on a2a server by
|
||||
@DavidAPierce in
|
||||
[#16080](https://github.com/google-gemini/gemini-cli/pull/16080)
|
||||
- Reapply "feat(admin): implement extensions disabled" (#16082) by @skeshive in
|
||||
[#16109](https://github.com/google-gemini/gemini-cli/pull/16109)
|
||||
- bug(core): Fix spewie getter in hookTranslator.ts by @joshualitt in
|
||||
[#16108](https://github.com/google-gemini/gemini-cli/pull/16108)
|
||||
- feat(hooks): add mcp_context to BeforeTool and AfterTool hook inputs by @vrv
|
||||
in [#15656](https://github.com/google-gemini/gemini-cli/pull/15656)
|
||||
- Add extension linking capabilities in cli by @kevinjwang1 in
|
||||
[#16040](https://github.com/google-gemini/gemini-cli/pull/16040)
|
||||
- Update the page's title to be consistent and show in site. by @kschaab in
|
||||
[#16174](https://github.com/google-gemini/gemini-cli/pull/16174)
|
||||
- docs: correct typo in bufferFastReturn JSDoc ("accomodate" → "accommodate") by
|
||||
@minglu7 in [#16056](https://github.com/google-gemini/gemini-cli/pull/16056)
|
||||
- fix: typo in MCP servers settings description by @alphanota in
|
||||
[#15929](https://github.com/google-gemini/gemini-cli/pull/15929)
|
||||
- fix: yolo should auto allow redirection by @abhipatel12 in
|
||||
[#16183](https://github.com/google-gemini/gemini-cli/pull/16183)
|
||||
- fix(cli): disableYoloMode shouldn't enforce default approval mode against args
|
||||
by @psinha40898 in
|
||||
[#16155](https://github.com/google-gemini/gemini-cli/pull/16155)
|
||||
- feat: add native Sublime Text support to IDE detection by @phreakocious in
|
||||
[#16083](https://github.com/google-gemini/gemini-cli/pull/16083)
|
||||
- refactor(core): extract ToolModificationHandler from scheduler by @abhipatel12
|
||||
in [#16118](https://github.com/google-gemini/gemini-cli/pull/16118)
|
||||
- Add support for Antigravity terminal in terminal setup utility by @raky291 in
|
||||
[#16051](https://github.com/google-gemini/gemini-cli/pull/16051)
|
||||
- feat(core): Wire up model routing to subagents. by @joshualitt in
|
||||
[#16043](https://github.com/google-gemini/gemini-cli/pull/16043)
|
||||
- feat(cli): add /agents slash command to list available agents by @adamfweidman
|
||||
in [#16182](https://github.com/google-gemini/gemini-cli/pull/16182)
|
||||
- docs(cli): fix includeDirectories nesting in configuration.md by @maru0804 in
|
||||
[#15067](https://github.com/google-gemini/gemini-cli/pull/15067)
|
||||
- feat: implement file system reversion utilities for rewind by @Adib234 in
|
||||
[#15715](https://github.com/google-gemini/gemini-cli/pull/15715)
|
||||
- Always enable redaction in GitHub actions. by @gundermanc in
|
||||
[#16200](https://github.com/google-gemini/gemini-cli/pull/16200)
|
||||
- fix: remove unsupported 'enabled' key from workflow config by @Han5991 in
|
||||
[#15611](https://github.com/google-gemini/gemini-cli/pull/15611)
|
||||
- docs: Remove redundant and duplicate documentation files by @liqzheng in
|
||||
[#14699](https://github.com/google-gemini/gemini-cli/pull/14699)
|
||||
- docs: shorten run command and use published version by @dsherret in
|
||||
[#16172](https://github.com/google-gemini/gemini-cli/pull/16172)
|
||||
- test(command-registry): increase initialization test timeout by @wszqkzqk in
|
||||
[#15979](https://github.com/google-gemini/gemini-cli/pull/15979)
|
||||
- Ensure TERM is set to xterm-256color by @falouu in
|
||||
[#15828](https://github.com/google-gemini/gemini-cli/pull/15828)
|
||||
- The telemetry.js script should handle paths that contain spaces by @JohnJAS in
|
||||
[#12078](https://github.com/google-gemini/gemini-cli/pull/12078)
|
||||
- ci: guard links workflow from running on forks by @wtanaka in
|
||||
[#15461](https://github.com/google-gemini/gemini-cli/pull/15461)
|
||||
- ci: guard nightly release workflow from running on forks by @wtanaka in
|
||||
[#15463](https://github.com/google-gemini/gemini-cli/pull/15463)
|
||||
- Support @ suggestions for subagenets by @sehoon38 in
|
||||
[#16201](https://github.com/google-gemini/gemini-cli/pull/16201)
|
||||
- feat(hooks): Support explicit stop and block execution control in model hooks
|
||||
by @SandyTao520 in
|
||||
[#15947](https://github.com/google-gemini/gemini-cli/pull/15947)
|
||||
- Refine Gemini 3 system instructions to reduce model verbosity by
|
||||
@NTaylorMullen in
|
||||
[#16139](https://github.com/google-gemini/gemini-cli/pull/16139)
|
||||
- chore: clean up unused models and use consts by @sehoon38 in
|
||||
[#16246](https://github.com/google-gemini/gemini-cli/pull/16246)
|
||||
- Always enable bracketed paste by @scidomino in
|
||||
[#16179](https://github.com/google-gemini/gemini-cli/pull/16179)
|
||||
- refactor: migrate clearCommand hook calls to HookSystem by @ved015 in
|
||||
[#16157](https://github.com/google-gemini/gemini-cli/pull/16157)
|
||||
- refactor: migrate app containter hook calls to hook system by @ishaanxgupta in
|
||||
[#16161](https://github.com/google-gemini/gemini-cli/pull/16161)
|
||||
- Show settings source in extensions lists by @chrstnb in
|
||||
[#16207](https://github.com/google-gemini/gemini-cli/pull/16207)
|
||||
- feat(skills): add pr-creator skill and enable skills by @NTaylorMullen in
|
||||
[#16232](https://github.com/google-gemini/gemini-cli/pull/16232)
|
||||
- fix: handle Shift+Space in Kitty keyboard protocol terminals by @tt-a1i in
|
||||
[#15767](https://github.com/google-gemini/gemini-cli/pull/15767)
|
||||
- feat(core, ui): Add /agents refresh command. by @joshualitt in
|
||||
[#16204](https://github.com/google-gemini/gemini-cli/pull/16204)
|
||||
- feat(core): add local experiments override via GEMINI_EXP by @kevin-ramdass in
|
||||
[#16181](https://github.com/google-gemini/gemini-cli/pull/16181)
|
||||
- feat(ui): reduce home directory warning noise and add opt-out setting by
|
||||
@NTaylorMullen in
|
||||
[#16229](https://github.com/google-gemini/gemini-cli/pull/16229)
|
||||
- refactor: migrate chatCompressionService to use HookSystem by @ved015 in
|
||||
[#16259](https://github.com/google-gemini/gemini-cli/pull/16259)
|
||||
- fix: properly use systemMessage for hooks in UI by @jackwotherspoon in
|
||||
[#16250](https://github.com/google-gemini/gemini-cli/pull/16250)
|
||||
- Infer modifyOtherKeys support by @scidomino in
|
||||
[#16270](https://github.com/google-gemini/gemini-cli/pull/16270)
|
||||
- feat(core): Cache ignore instances for performance by @EricRahm in
|
||||
[#16185](https://github.com/google-gemini/gemini-cli/pull/16185)
|
||||
- feat: apply remote admin settings (no-op) by @skeshive in
|
||||
[#16106](https://github.com/google-gemini/gemini-cli/pull/16106)
|
||||
- Autogenerate docs/cli/settings.md docs/getting-started/configuration.md was
|
||||
already autogenerated but settings.md was not. by @jacob314 in
|
||||
[#14408](https://github.com/google-gemini/gemini-cli/pull/14408)
|
||||
- refactor(config): remove legacy V1 settings migration logic by @galz10 in
|
||||
[#16252](https://github.com/google-gemini/gemini-cli/pull/16252)
|
||||
- Fix an issue where the agent stops prematurely by @gundermanc in
|
||||
[#16269](https://github.com/google-gemini/gemini-cli/pull/16269)
|
||||
- Update system prompt to prefer non-interactive commands by @NTaylorMullen in
|
||||
[#16117](https://github.com/google-gemini/gemini-cli/pull/16117)
|
||||
- Update ink version to 6.4.7 by @jacob314 in
|
||||
[#16284](https://github.com/google-gemini/gemini-cli/pull/16284)
|
||||
- Support for Built-in Agent Skills by @NTaylorMullen in
|
||||
[#16045](https://github.com/google-gemini/gemini-cli/pull/16045)
|
||||
- fix(skills): remove "Restart required" message from non-interactive commands
|
||||
by @NTaylorMullen in
|
||||
[#16307](https://github.com/google-gemini/gemini-cli/pull/16307)
|
||||
- remove unused sessionHookTriggers and exports by @ved015 in
|
||||
[#16324](https://github.com/google-gemini/gemini-cli/pull/16324)
|
||||
- Triage action cleanup by @bdmorgan in
|
||||
[#16319](https://github.com/google-gemini/gemini-cli/pull/16319)
|
||||
- fix: Add event-driven trigger to issue triage workflow by @bdmorgan in
|
||||
[#16334](https://github.com/google-gemini/gemini-cli/pull/16334)
|
||||
- fix(workflows): resolve triage workflow failures and actionlint errors by
|
||||
@bdmorgan in [#16338](https://github.com/google-gemini/gemini-cli/pull/16338)
|
||||
- docs: add note about experimental hooks by @abhipatel12 in
|
||||
[#16337](https://github.com/google-gemini/gemini-cli/pull/16337)
|
||||
- feat(cli): implement passive activity logger for session analysis by
|
||||
[#17086](https://github.com/google-gemini/gemini-cli/pull/17086)
|
||||
- fix(scheduler): prevent stale tool re-publication and fix stuck UI state by
|
||||
@abhipatel12 in
|
||||
[#17227](https://github.com/google-gemini/gemini-cli/pull/17227)
|
||||
- feat(config): default enableEventDrivenScheduler to true by @abhipatel12 in
|
||||
[#17211](https://github.com/google-gemini/gemini-cli/pull/17211)
|
||||
- feat(hooks): enable hooks system by default by @abhipatel12 in
|
||||
[#17247](https://github.com/google-gemini/gemini-cli/pull/17247)
|
||||
- feat(core): Enable AgentRegistry to track all discovered subagents by
|
||||
@SandyTao520 in
|
||||
[#15829](https://github.com/google-gemini/gemini-cli/pull/15829)
|
||||
- feat(cli): add /chat debug command for nightly builds by @abhipatel12 in
|
||||
[#16339](https://github.com/google-gemini/gemini-cli/pull/16339)
|
||||
- style: format pr-creator skill by @NTaylorMullen in
|
||||
[#16381](https://github.com/google-gemini/gemini-cli/pull/16381)
|
||||
- feat(cli): Hooks enable-all/disable-all feature with dynamic status by
|
||||
@AbdulTawabJuly in
|
||||
[#15552](https://github.com/google-gemini/gemini-cli/pull/15552)
|
||||
- fix(core): ensure silent local subagent delegation while allowing remote
|
||||
confirmation by @adamfweidman in
|
||||
[#16395](https://github.com/google-gemini/gemini-cli/pull/16395)
|
||||
- Markdown w/ Frontmatter Agent Parser by @sehoon38 in
|
||||
[#16094](https://github.com/google-gemini/gemini-cli/pull/16094)
|
||||
- Fix crash on unicode character by @chrstnb in
|
||||
[#16420](https://github.com/google-gemini/gemini-cli/pull/16420)
|
||||
- Attempt to resolve OOM w/ useMemo on history items by @chrstnb in
|
||||
[#16424](https://github.com/google-gemini/gemini-cli/pull/16424)
|
||||
- fix(core): ensure sub-agent schema and prompt refresh during runtime by
|
||||
@adamfweidman in
|
||||
[#16409](https://github.com/google-gemini/gemini-cli/pull/16409)
|
||||
- Update extension examples by @chrstnb in
|
||||
[#16274](https://github.com/google-gemini/gemini-cli/pull/16274)
|
||||
- revert the change that was recently added from a fix by @sehoon38 in
|
||||
[#16390](https://github.com/google-gemini/gemini-cli/pull/16390)
|
||||
- Add other hook wrapper methods to hooksystem by @ved015 in
|
||||
[#16361](https://github.com/google-gemini/gemini-cli/pull/16361)
|
||||
- feat: introduce useRewindLogic hook for conversation history navigation by
|
||||
@Adib234 in [#15716](https://github.com/google-gemini/gemini-cli/pull/15716)
|
||||
- docs: Fix formatting issue in memport documentation by @wanglc02 in
|
||||
[#14774](https://github.com/google-gemini/gemini-cli/pull/14774)
|
||||
- fix(policy): enhance shell command safety and parsing by @allenhutchison in
|
||||
[#15034](https://github.com/google-gemini/gemini-cli/pull/15034)
|
||||
- fix(core): avoid 'activate_skill' re-registration warning by @NTaylorMullen in
|
||||
[#16398](https://github.com/google-gemini/gemini-cli/pull/16398)
|
||||
- perf(workflows): optimize PR triage script for faster execution by @bdmorgan
|
||||
in [#16355](https://github.com/google-gemini/gemini-cli/pull/16355)
|
||||
- feat(admin): prompt user to restart the CLI if they change auth to oauth
|
||||
mid-session or don't have auth type selected at start of session by @skeshive
|
||||
in [#16426](https://github.com/google-gemini/gemini-cli/pull/16426)
|
||||
- Update cli-help agent's system prompt in sub-agents section by @sehoon38 in
|
||||
[#16441](https://github.com/google-gemini/gemini-cli/pull/16441)
|
||||
- Revert "Update extension examples" by @chrstnb in
|
||||
[#16442](https://github.com/google-gemini/gemini-cli/pull/16442)
|
||||
- Fix: add back fastreturn support by @scidomino in
|
||||
[#16440](https://github.com/google-gemini/gemini-cli/pull/16440)
|
||||
- feat(a2a): Introduce /memory command for a2a server by @cocosheng-g in
|
||||
[#14456](https://github.com/google-gemini/gemini-cli/pull/14456)
|
||||
- docs: fix broken internal link by using relative path by @Gong-Mi in
|
||||
[#15371](https://github.com/google-gemini/gemini-cli/pull/15371)
|
||||
- migrate yolo/auto-edit keybindings by @scidomino in
|
||||
[#16457](https://github.com/google-gemini/gemini-cli/pull/16457)
|
||||
- feat(cli): add install and uninstall commands for skills by @NTaylorMullen in
|
||||
[#16377](https://github.com/google-gemini/gemini-cli/pull/16377)
|
||||
- feat(ui): use Tab to switch focus between shell and input by @jacob314 in
|
||||
[#14332](https://github.com/google-gemini/gemini-cli/pull/14332)
|
||||
- feat(core): support shipping built-in skills with the CLI by @NTaylorMullen in
|
||||
[#16300](https://github.com/google-gemini/gemini-cli/pull/16300)
|
||||
- Collect hardware details telemetry. by @gundermanc in
|
||||
[#16119](https://github.com/google-gemini/gemini-cli/pull/16119)
|
||||
- feat(agents): improve UI feedback and parser reliability by @NTaylorMullen in
|
||||
[#16459](https://github.com/google-gemini/gemini-cli/pull/16459)
|
||||
- Migrate keybindings by @scidomino in
|
||||
[#16460](https://github.com/google-gemini/gemini-cli/pull/16460)
|
||||
- feat(cli): cleanup activity logs alongside session files by @SandyTao520 in
|
||||
[#16399](https://github.com/google-gemini/gemini-cli/pull/16399)
|
||||
- feat(cli): implement dynamic terminal tab titles for CLI status by
|
||||
@NTaylorMullen in
|
||||
[#16378](https://github.com/google-gemini/gemini-cli/pull/16378)
|
||||
- feat(core): add disableLLMCorrection setting to skip auto-correction in edit
|
||||
tools by @SandyTao520 in
|
||||
[#16000](https://github.com/google-gemini/gemini-cli/pull/16000)
|
||||
- fix: Set both tab and window title instead of just window title by
|
||||
@NTaylorMullen in
|
||||
[#16464](https://github.com/google-gemini/gemini-cli/pull/16464)
|
||||
- fix(policy): ensure MCP policies match unqualified names in non-interactive
|
||||
mode by @NTaylorMullen in
|
||||
[#16490](https://github.com/google-gemini/gemini-cli/pull/16490)
|
||||
- fix(cli): refine 'Action Required' indicator and focus hints by @NTaylorMullen
|
||||
in [#16497](https://github.com/google-gemini/gemini-cli/pull/16497)
|
||||
- Refactor beforeAgent and afterAgent hookEvents to follow desired output by
|
||||
@ved015 in [#16495](https://github.com/google-gemini/gemini-cli/pull/16495)
|
||||
- feat(agents): clarify mandatory YAML frontmatter for sub-agents by
|
||||
@NTaylorMullen in
|
||||
[#16515](https://github.com/google-gemini/gemini-cli/pull/16515)
|
||||
- docs(telemetry): add Google Cloud Monitoring dashboard documentation by @jerop
|
||||
in [#16520](https://github.com/google-gemini/gemini-cli/pull/16520)
|
||||
- Implement support for subagents as extensions. by @gundermanc in
|
||||
[#16473](https://github.com/google-gemini/gemini-cli/pull/16473)
|
||||
- refactor: make baseTimestamp optional in addItem and remove redundant calls by
|
||||
@sehoon38 in [#16471](https://github.com/google-gemini/gemini-cli/pull/16471)
|
||||
- Improve key binding names and descriptions by @scidomino in
|
||||
[#16529](https://github.com/google-gemini/gemini-cli/pull/16529)
|
||||
- feat(core, cli): Add support for agents in settings.json. by @joshualitt in
|
||||
[#16433](https://github.com/google-gemini/gemini-cli/pull/16433)
|
||||
- fix(cli): fix 'gemini skills install' unknown argument error by @NTaylorMullen
|
||||
in [#16537](https://github.com/google-gemini/gemini-cli/pull/16537)
|
||||
- chore(ui): optimize AgentsStatus layout with dense list style and group
|
||||
separation by @adamfweidman in
|
||||
[#16545](https://github.com/google-gemini/gemini-cli/pull/16545)
|
||||
- fix(cli): allow @ file selector on slash command lines by @galz10 in
|
||||
[#16370](https://github.com/google-gemini/gemini-cli/pull/16370)
|
||||
- fix(ui): resolve sticky header regression in tool messages by @jacob314 in
|
||||
[#16514](https://github.com/google-gemini/gemini-cli/pull/16514)
|
||||
- feat(core): Align internal agent settings with configs exposed through
|
||||
settings.json by @joshualitt in
|
||||
[#16458](https://github.com/google-gemini/gemini-cli/pull/16458)
|
||||
- fix(cli): copy uses OSC52 only in SSH/WSL by @assagman in
|
||||
[#16554](https://github.com/google-gemini/gemini-cli/pull/16554)
|
||||
- docs(skills): clarify skill directory structure and file location by
|
||||
@NTaylorMullen in
|
||||
[#16532](https://github.com/google-gemini/gemini-cli/pull/16532)
|
||||
- Fix: make ctrl+x use preferred editor by @scidomino in
|
||||
[#16556](https://github.com/google-gemini/gemini-cli/pull/16556)
|
||||
- fix(core): Resolve race condition in tool response reporting by @abhipatel12
|
||||
in [#16557](https://github.com/google-gemini/gemini-cli/pull/16557)
|
||||
- feat(ui): highlight persist mode status in ModelDialog by @sehoon38 in
|
||||
[#16483](https://github.com/google-gemini/gemini-cli/pull/16483)
|
||||
- refactor: clean up A2A task output for users and LLMs by @adamfweidman in
|
||||
[#16561](https://github.com/google-gemini/gemini-cli/pull/16561)
|
||||
- feat(core/ui): enhance retry mechanism and UX by @sehoon38 in
|
||||
[#16489](https://github.com/google-gemini/gemini-cli/pull/16489)
|
||||
- Modernize MaxSizedBox to use and ResizeObservers by @jacob314 in
|
||||
[#16565](https://github.com/google-gemini/gemini-cli/pull/16565)
|
||||
- Behavioral evals framework. by @gundermanc in
|
||||
[#16047](https://github.com/google-gemini/gemini-cli/pull/16047)
|
||||
- Aggregate test results. by @gundermanc in
|
||||
[#16581](https://github.com/google-gemini/gemini-cli/pull/16581)
|
||||
- feat(admin): support admin-enforced settings for Agent Skills by
|
||||
@NTaylorMullen in
|
||||
[#16406](https://github.com/google-gemini/gemini-cli/pull/16406)
|
||||
- fix(patch): cherry-pick cfdc4cf to release/v0.25.0-preview.0-pr-16759 to patch
|
||||
version v0.25.0-preview.0 and create version 0.25.0-preview.1 by
|
||||
@gemini-cli-robot in
|
||||
[#16866](https://github.com/google-gemini/gemini-cli/pull/16866)
|
||||
- Patch #16730 into v0.25.0 preview by @chrstnb in
|
||||
[#16882](https://github.com/google-gemini/gemini-cli/pull/16882)
|
||||
- fix(patch): cherry-pick 3b55581 to release/v0.25.0-preview.2-pr-16506 to patch
|
||||
version v0.25.0-preview.2 and create version 0.25.0-preview.3 by
|
||||
@gemini-cli-robot in
|
||||
[#17098](https://github.com/google-gemini/gemini-cli/pull/17098)
|
||||
[#17253](https://github.com/google-gemini/gemini-cli/pull/17253)
|
||||
- feat(core): Have subagents use a JSON schema type for input. by @joshualitt in
|
||||
[#17152](https://github.com/google-gemini/gemini-cli/pull/17152)
|
||||
- feat: replace large text pastes with [Pasted Text: X lines] placeholder by
|
||||
@jackwotherspoon in
|
||||
[#16422](https://github.com/google-gemini/gemini-cli/pull/16422)
|
||||
- security(hooks): Wrap hook-injected context in distinct XML tags by @yunaseoul
|
||||
in [#17237](https://github.com/google-gemini/gemini-cli/pull/17237)
|
||||
- Enable the ability to queue specific nightly eval tests by @gundermanc in
|
||||
[#17262](https://github.com/google-gemini/gemini-cli/pull/17262)
|
||||
- docs(hooks): comprehensive update of hook documentation and specs by
|
||||
@abhipatel12 in
|
||||
[#16816](https://github.com/google-gemini/gemini-cli/pull/16816)
|
||||
- refactor: improve large text paste placeholder by @jacob314 in
|
||||
[#17269](https://github.com/google-gemini/gemini-cli/pull/17269)
|
||||
- feat: implement /rewind command by @Adib234 in
|
||||
[#15720](https://github.com/google-gemini/gemini-cli/pull/15720)
|
||||
- Feature/jetbrains ide detection by @SoLoHiC in
|
||||
[#16243](https://github.com/google-gemini/gemini-cli/pull/16243)
|
||||
- docs: update typo in mcp-server.md file by @schifferl in
|
||||
[#17099](https://github.com/google-gemini/gemini-cli/pull/17099)
|
||||
- Sanitize command names and descriptions by @ehedlund in
|
||||
[#17228](https://github.com/google-gemini/gemini-cli/pull/17228)
|
||||
- fix(auth): don't crash when initial auth fails by @skeshive in
|
||||
[#17308](https://github.com/google-gemini/gemini-cli/pull/17308)
|
||||
- Added image pasting capabilities for Wayland and X11 on Linux by @devr0306 in
|
||||
[#17144](https://github.com/google-gemini/gemini-cli/pull/17144)
|
||||
- feat: add AskUser tool schema by @jackwotherspoon in
|
||||
[#16988](https://github.com/google-gemini/gemini-cli/pull/16988)
|
||||
- fix cli settings: resolve layout jitter in settings bar by @Mag1ck in
|
||||
[#16256](https://github.com/google-gemini/gemini-cli/pull/16256)
|
||||
- fix: show whitespace changes in edit tool diffs by @Ujjiyara in
|
||||
[#17213](https://github.com/google-gemini/gemini-cli/pull/17213)
|
||||
- Remove redundant calls setting linuxClipboardTool. getUserLinuxClipboardTool()
|
||||
now handles the caching internally by @jacob314 in
|
||||
[#17320](https://github.com/google-gemini/gemini-cli/pull/17320)
|
||||
- ci: allow failure in evals-nightly run step by @gundermanc in
|
||||
[#17319](https://github.com/google-gemini/gemini-cli/pull/17319)
|
||||
- feat(cli): Add state management and plumbing for agent configuration dialog by
|
||||
@SandyTao520 in
|
||||
[#17259](https://github.com/google-gemini/gemini-cli/pull/17259)
|
||||
- bug: fix ide-client connection to ide-companion when inside docker via
|
||||
ssh/devcontainer by @kapsner in
|
||||
[#15049](https://github.com/google-gemini/gemini-cli/pull/15049)
|
||||
- Emit correct newline type return by @scidomino in
|
||||
[#17331](https://github.com/google-gemini/gemini-cli/pull/17331)
|
||||
- New skill: docs-writer by @g-samroberts in
|
||||
[#17268](https://github.com/google-gemini/gemini-cli/pull/17268)
|
||||
- fix(core): Resolve AbortSignal MaxListenersExceededWarning (#5950) by
|
||||
@spencer426 in
|
||||
[#16735](https://github.com/google-gemini/gemini-cli/pull/16735)
|
||||
- Disable tips after 10 runs by @Adib234 in
|
||||
[#17101](https://github.com/google-gemini/gemini-cli/pull/17101)
|
||||
- Fix so rewind starts at the bottom and loadHistory refreshes static content.
|
||||
by @jacob314 in
|
||||
[#17335](https://github.com/google-gemini/gemini-cli/pull/17335)
|
||||
- feat(core): Remove legacy settings. by @joshualitt in
|
||||
[#17244](https://github.com/google-gemini/gemini-cli/pull/17244)
|
||||
- feat(plan): add 'communicate' tool kind by @jerop in
|
||||
[#17341](https://github.com/google-gemini/gemini-cli/pull/17341)
|
||||
- feat(routing): A/B Test Numerical Complexity Scoring for Gemini 3 by
|
||||
@mattKorwel in
|
||||
[#16041](https://github.com/google-gemini/gemini-cli/pull/16041)
|
||||
- feat(plan): update UI Theme for Plan Mode by @Adib234 in
|
||||
[#17243](https://github.com/google-gemini/gemini-cli/pull/17243)
|
||||
- fix(ui): stabilize rendering during terminal resize in alternate buffer by
|
||||
@lkk214 in [#15783](https://github.com/google-gemini/gemini-cli/pull/15783)
|
||||
- feat(cli): add /agents config command and improve agent discovery by
|
||||
@SandyTao520 in
|
||||
[#17342](https://github.com/google-gemini/gemini-cli/pull/17342)
|
||||
- feat(mcp): add enable/disable commands for MCP servers (#11057) by @jasmeetsb
|
||||
in [#16299](https://github.com/google-gemini/gemini-cli/pull/16299)
|
||||
- fix(cli)!: Default to interactive mode for positional arguments by
|
||||
@ishaanxgupta in
|
||||
[#16329](https://github.com/google-gemini/gemini-cli/pull/16329)
|
||||
- Fix issue #17080 by @jacob314 in
|
||||
[#17100](https://github.com/google-gemini/gemini-cli/pull/17100)
|
||||
- feat(core): Refresh agents after loading an extension. by @joshualitt in
|
||||
[#17355](https://github.com/google-gemini/gemini-cli/pull/17355)
|
||||
- fix(cli): include source in policy rule display by @allenhutchison in
|
||||
[#17358](https://github.com/google-gemini/gemini-cli/pull/17358)
|
||||
- fix: remove obsolete CloudCode PerDay quota and 120s terminal threshold by
|
||||
@gsquared94 in
|
||||
[#17236](https://github.com/google-gemini/gemini-cli/pull/17236)
|
||||
- Refactor subagent delegation to be one tool per agent by @gundermanc in
|
||||
[#17346](https://github.com/google-gemini/gemini-cli/pull/17346)
|
||||
- fix(core): Include MCP server name in OAuth message by @jerop in
|
||||
[#17351](https://github.com/google-gemini/gemini-cli/pull/17351)
|
||||
- Fix pr-triage.sh script to update pull requests with tags "help wanted" and
|
||||
"maintainer only" by @jacob314 in
|
||||
[#17324](https://github.com/google-gemini/gemini-cli/pull/17324)
|
||||
- feat(plan): implement simple workflow for planning in main agent by @jerop in
|
||||
[#17326](https://github.com/google-gemini/gemini-cli/pull/17326)
|
||||
- fix: exit with non-zero code when esbuild is missing by @yuvrajangadsingh in
|
||||
[#16967](https://github.com/google-gemini/gemini-cli/pull/16967)
|
||||
- fix: ensure @docs/cli/custom-commands.md UI message ordering and test by
|
||||
@medic-code in
|
||||
[#12038](https://github.com/google-gemini/gemini-cli/pull/12038)
|
||||
- fix(core): add alternative command names for Antigravity editor detec… by
|
||||
@baeseokjae in
|
||||
[#16829](https://github.com/google-gemini/gemini-cli/pull/16829)
|
||||
- Refactor: Migrate CLI appEvents to Core coreEvents by @Adib234 in
|
||||
[#15737](https://github.com/google-gemini/gemini-cli/pull/15737)
|
||||
- fix(core): await MCP initialization in non-interactive mode by @Ratish1 in
|
||||
[#17390](https://github.com/google-gemini/gemini-cli/pull/17390)
|
||||
- Fix modifyOtherKeys enablement on unsupported terminals by @seekskyworld in
|
||||
[#16714](https://github.com/google-gemini/gemini-cli/pull/16714)
|
||||
- fix(core): gracefully handle disk full errors in chat recording by
|
||||
@godwiniheuwa in
|
||||
[#17305](https://github.com/google-gemini/gemini-cli/pull/17305)
|
||||
- fix(oauth): update oauth to use 127.0.0.1 instead of localhost by @skeshive in
|
||||
[#17388](https://github.com/google-gemini/gemini-cli/pull/17388)
|
||||
- fix(core): use RFC 9728 compliant path-based OAuth protected resource
|
||||
discovery by @vrv in
|
||||
[#15756](https://github.com/google-gemini/gemini-cli/pull/15756)
|
||||
- Update Code Wiki README badge by @PatoBeltran in
|
||||
[#15229](https://github.com/google-gemini/gemini-cli/pull/15229)
|
||||
- Add conda installation instructions for Gemini CLI by @ishaanxgupta in
|
||||
[#16921](https://github.com/google-gemini/gemini-cli/pull/16921)
|
||||
- chore(refactor): extract BaseSettingsDialog component by @SandyTao520 in
|
||||
[#17369](https://github.com/google-gemini/gemini-cli/pull/17369)
|
||||
- fix(cli): preserve input text when declining tool approval (#15624) by
|
||||
@ManojINaik in
|
||||
[#15659](https://github.com/google-gemini/gemini-cli/pull/15659)
|
||||
- chore: upgrade dep: diff 7.0.0-> 8.0.3 by @scidomino in
|
||||
[#17403](https://github.com/google-gemini/gemini-cli/pull/17403)
|
||||
- feat: add AskUserDialog for UI component of AskUser tool by @jackwotherspoon
|
||||
in [#17344](https://github.com/google-gemini/gemini-cli/pull/17344)
|
||||
- feat(ui): display user tier in about command by @sehoon38 in
|
||||
[#17400](https://github.com/google-gemini/gemini-cli/pull/17400)
|
||||
- feat: add clearContext to AfterAgent hooks by @jackwotherspoon in
|
||||
[#16574](https://github.com/google-gemini/gemini-cli/pull/16574)
|
||||
- fix(cli): change image paste location to global temp directory (#17396) by
|
||||
@devr0306 in [#17396](https://github.com/google-gemini/gemini-cli/pull/17396)
|
||||
- Fix line endings issue with Notice file by @scidomino in
|
||||
[#17417](https://github.com/google-gemini/gemini-cli/pull/17417)
|
||||
- feat(plan): implement persistent approvalMode setting by @Adib234 in
|
||||
[#17350](https://github.com/google-gemini/gemini-cli/pull/17350)
|
||||
- feat(ui): Move keyboard handling into BaseSettingsDialog by @SandyTao520 in
|
||||
[#17404](https://github.com/google-gemini/gemini-cli/pull/17404)
|
||||
- Allow prompt queueing during MCP initialization by @Adib234 in
|
||||
[#17395](https://github.com/google-gemini/gemini-cli/pull/17395)
|
||||
- feat: implement AgentConfigDialog for /agents config command by @SandyTao520
|
||||
in [#17370](https://github.com/google-gemini/gemini-cli/pull/17370)
|
||||
- fix(agents): default to all tools when tool list is omitted in subagents by
|
||||
@gundermanc in
|
||||
[#17422](https://github.com/google-gemini/gemini-cli/pull/17422)
|
||||
- feat(cli): Moves tool confirmations to a queue UX by @abhipatel12 in
|
||||
[#17276](https://github.com/google-gemini/gemini-cli/pull/17276)
|
||||
- fix(core): hide user tier name by @sehoon38 in
|
||||
[#17418](https://github.com/google-gemini/gemini-cli/pull/17418)
|
||||
- feat: Enforce unified folder trust for /directory add by @galz10 in
|
||||
[#17359](https://github.com/google-gemini/gemini-cli/pull/17359)
|
||||
- migrate fireToolNotificationHook to hookSystem by @ved015 in
|
||||
[#17398](https://github.com/google-gemini/gemini-cli/pull/17398)
|
||||
- Clean up dead code by @scidomino in
|
||||
[#17443](https://github.com/google-gemini/gemini-cli/pull/17443)
|
||||
- feat(workflow): add stale pull request closer with linked-issue enforcement by
|
||||
@bdmorgan in [#17449](https://github.com/google-gemini/gemini-cli/pull/17449)
|
||||
- feat(workflow): expand stale-exempt labels to include help wanted and Public
|
||||
Roadmap by @bdmorgan in
|
||||
[#17459](https://github.com/google-gemini/gemini-cli/pull/17459)
|
||||
- chore(workflow): remove redundant label-enforcer workflow by @bdmorgan in
|
||||
[#17460](https://github.com/google-gemini/gemini-cli/pull/17460)
|
||||
- Resolves the confusing error message `ripgrep exited with code null that
|
||||
occurs when a search operation is cancelled or aborted by @maximmasiutin in
|
||||
[#14267](https://github.com/google-gemini/gemini-cli/pull/14267)
|
||||
- fix: detect pnpm/pnpx in ~/.local by @rwakulszowa in
|
||||
[#15254](https://github.com/google-gemini/gemini-cli/pull/15254)
|
||||
- docs: Add instructions for MacPorts and uninstall instructions for Homebrew by
|
||||
@breun in [#17412](https://github.com/google-gemini/gemini-cli/pull/17412)
|
||||
- docs(hooks): clarify mandatory 'type' field and update hook schema
|
||||
documentation by @abhipatel12 in
|
||||
[#17499](https://github.com/google-gemini/gemini-cli/pull/17499)
|
||||
- Improve error messages on failed onboarding by @gsquared94 in
|
||||
[#17357](https://github.com/google-gemini/gemini-cli/pull/17357)
|
||||
- Follow up to "enableInteractiveShell for external tooling relying on a2a
|
||||
server" by @DavidAPierce in
|
||||
[#17130](https://github.com/google-gemini/gemini-cli/pull/17130)
|
||||
- Fix/issue 17070 by @alih552 in
|
||||
[#17242](https://github.com/google-gemini/gemini-cli/pull/17242)
|
||||
- fix(core): handle URI-encoded workspace paths in IdeClient by @dong-jun-shin
|
||||
in [#17476](https://github.com/google-gemini/gemini-cli/pull/17476)
|
||||
- feat(cli): add quick clear input shortcuts in vim mode by @harshanadim in
|
||||
[#17470](https://github.com/google-gemini/gemini-cli/pull/17470)
|
||||
- feat(core): optimize shell tool llmContent output format by @SandyTao520 in
|
||||
[#17538](https://github.com/google-gemini/gemini-cli/pull/17538)
|
||||
- Fix bug in detecting already added paths. by @jacob314 in
|
||||
[#17430](https://github.com/google-gemini/gemini-cli/pull/17430)
|
||||
- feat(scheduler): support multi-scheduler tool aggregation and nested call IDs
|
||||
by @abhipatel12 in
|
||||
[#17429](https://github.com/google-gemini/gemini-cli/pull/17429)
|
||||
- feat(agents): implement first-run experience for project-level sub-agents by
|
||||
@gundermanc in
|
||||
[#17266](https://github.com/google-gemini/gemini-cli/pull/17266)
|
||||
- Update extensions docs by @chrstnb in
|
||||
[#16093](https://github.com/google-gemini/gemini-cli/pull/16093)
|
||||
- Docs: Refactor left nav on the website by @jkcinouye in
|
||||
[#17558](https://github.com/google-gemini/gemini-cli/pull/17558)
|
||||
- fix(core): stream grep/ripgrep output to prevent OOM by @adamfweidman in
|
||||
[#17146](https://github.com/google-gemini/gemini-cli/pull/17146)
|
||||
- feat(plan): add persistent plan file storage by @jerop in
|
||||
[#17563](https://github.com/google-gemini/gemini-cli/pull/17563)
|
||||
- feat(agents): migrate subagents to event-driven scheduler by @abhipatel12 in
|
||||
[#17567](https://github.com/google-gemini/gemini-cli/pull/17567)
|
||||
- Fix extensions config error by @chrstnb in
|
||||
[#17580](https://github.com/google-gemini/gemini-cli/pull/17580)
|
||||
- fix(plan): remove subagent invocation from plan mode by @jerop in
|
||||
[#17593](https://github.com/google-gemini/gemini-cli/pull/17593)
|
||||
- feat(ui): add solid background color option for input prompt by @jacob314 in
|
||||
[#16563](https://github.com/google-gemini/gemini-cli/pull/16563)
|
||||
- feat(plan): refresh system prompt when approval mode changes (Shift+Tab) by
|
||||
@jerop in [#17585](https://github.com/google-gemini/gemini-cli/pull/17585)
|
||||
- feat(cli): add global setting to disable UI spinners by @galz10 in
|
||||
[#17234](https://github.com/google-gemini/gemini-cli/pull/17234)
|
||||
- fix(security): enforce strict policy directory permissions by @yunaseoul in
|
||||
[#17353](https://github.com/google-gemini/gemini-cli/pull/17353)
|
||||
- test(core): fix tests in windows by @scidomino in
|
||||
[#17592](https://github.com/google-gemini/gemini-cli/pull/17592)
|
||||
- feat(mcp/extensions): Allow users to selectively enable/disable MCP servers
|
||||
included in an extension( Issue #11057 & #17402) by @jasmeetsb in
|
||||
[#17434](https://github.com/google-gemini/gemini-cli/pull/17434)
|
||||
- Always map mac keys, even on other platforms by @scidomino in
|
||||
[#17618](https://github.com/google-gemini/gemini-cli/pull/17618)
|
||||
- Ctrl-O by @jacob314 in
|
||||
[#17617](https://github.com/google-gemini/gemini-cli/pull/17617)
|
||||
- feat(plan): update cycling order of approval modes by @Adib234 in
|
||||
[#17622](https://github.com/google-gemini/gemini-cli/pull/17622)
|
||||
- fix(cli): restore 'Modify with editor' option in external terminals by
|
||||
@abhipatel12 in
|
||||
[#17621](https://github.com/google-gemini/gemini-cli/pull/17621)
|
||||
- Slash command for helping in debugging by @gundermanc in
|
||||
[#17609](https://github.com/google-gemini/gemini-cli/pull/17609)
|
||||
- feat: add double-click to expand/collapse large paste placeholders by
|
||||
@jackwotherspoon in
|
||||
[#17471](https://github.com/google-gemini/gemini-cli/pull/17471)
|
||||
- refactor(cli): migrate non-interactive flow to event-driven scheduler by
|
||||
@abhipatel12 in
|
||||
[#17572](https://github.com/google-gemini/gemini-cli/pull/17572)
|
||||
- fix: loadcodeassist eligible tiers getting ignored for unlicensed users
|
||||
(regression) by @gsquared94 in
|
||||
[#17581](https://github.com/google-gemini/gemini-cli/pull/17581)
|
||||
- chore(core): delete legacy nonInteractiveToolExecutor by @abhipatel12 in
|
||||
[#17573](https://github.com/google-gemini/gemini-cli/pull/17573)
|
||||
- feat(core): enforce server prefixes for MCP tools in agent definitions by
|
||||
@abhipatel12 in
|
||||
[#17574](https://github.com/google-gemini/gemini-cli/pull/17574)
|
||||
- feat (mcp): Refresh MCP prompts on list changed notification by @MrLesk in
|
||||
[#14863](https://github.com/google-gemini/gemini-cli/pull/14863)
|
||||
- feat(ui): pretty JSON rendering tool outputs by @medic-code in
|
||||
[#9767](https://github.com/google-gemini/gemini-cli/pull/9767)
|
||||
- Fix iterm alternate buffer mode issue rendering backgrounds by @jacob314 in
|
||||
[#17634](https://github.com/google-gemini/gemini-cli/pull/17634)
|
||||
- feat(cli): add gemini extensions list --output-format=json by @AkihiroSuda in
|
||||
[#14479](https://github.com/google-gemini/gemini-cli/pull/14479)
|
||||
- fix(extensions): add .gitignore to extension templates by @godwiniheuwa in
|
||||
[#17293](https://github.com/google-gemini/gemini-cli/pull/17293)
|
||||
- paste transform followup by @jacob314 in
|
||||
[#17624](https://github.com/google-gemini/gemini-cli/pull/17624)
|
||||
- refactor: rename formatMemoryUsage to formatBytes by @Nubebuster in
|
||||
[#14997](https://github.com/google-gemini/gemini-cli/pull/14997)
|
||||
- chore: remove extra top margin from /hooks and /extensions by @jackwotherspoon
|
||||
in [#17663](https://github.com/google-gemini/gemini-cli/pull/17663)
|
||||
- feat(cli): add oncall command for issue triage by @sehoon38 in
|
||||
[#17661](https://github.com/google-gemini/gemini-cli/pull/17661)
|
||||
- Fix sidebar issue for extensions link by @chrstnb in
|
||||
[#17668](https://github.com/google-gemini/gemini-cli/pull/17668)
|
||||
- Change formatting to prevent UI redressing attacks by @scidomino in
|
||||
[#17611](https://github.com/google-gemini/gemini-cli/pull/17611)
|
||||
- Fix cluster of bugs in the settings dialog. by @jacob314 in
|
||||
[#17628](https://github.com/google-gemini/gemini-cli/pull/17628)
|
||||
- Update sidebar to resolve site build issues by @chrstnb in
|
||||
[#17674](https://github.com/google-gemini/gemini-cli/pull/17674)
|
||||
- fix(admin): fix a few bugs related to admin controls by @skeshive in
|
||||
[#17590](https://github.com/google-gemini/gemini-cli/pull/17590)
|
||||
- revert bad changes to tests by @scidomino in
|
||||
[#17673](https://github.com/google-gemini/gemini-cli/pull/17673)
|
||||
- feat(cli): show candidate issue state reason and duplicate status in triage by
|
||||
@sehoon38 in [#17676](https://github.com/google-gemini/gemini-cli/pull/17676)
|
||||
- Fix missing slash commands when Gemini CLI is in a project with a package.json
|
||||
that doesn't follow semantic versioning by @Adib234 in
|
||||
[#17561](https://github.com/google-gemini/gemini-cli/pull/17561)
|
||||
- feat(core): Model family-specific system prompts by @joshualitt in
|
||||
[#17614](https://github.com/google-gemini/gemini-cli/pull/17614)
|
||||
- Sub-agents documentation. by @gundermanc in
|
||||
[#16639](https://github.com/google-gemini/gemini-cli/pull/16639)
|
||||
- feat: wire up AskUserTool with dialog by @jackwotherspoon in
|
||||
[#17411](https://github.com/google-gemini/gemini-cli/pull/17411)
|
||||
- Load extension settings for hooks, agents, skills by @chrstnb in
|
||||
[#17245](https://github.com/google-gemini/gemini-cli/pull/17245)
|
||||
- Fix issue where Gemini CLI can make changes when simply asked a question by
|
||||
@gundermanc in
|
||||
[#17608](https://github.com/google-gemini/gemini-cli/pull/17608)
|
||||
- Update docs-writer skill for editing and add style guide for reference. by
|
||||
@g-samroberts in
|
||||
[#17669](https://github.com/google-gemini/gemini-cli/pull/17669)
|
||||
- fix(ux): have user message display a short path for pasted images by @devr0306
|
||||
in [#17613](https://github.com/google-gemini/gemini-cli/pull/17613)
|
||||
- feat(plan): enable AskUser tool in Plan mode for clarifying questions by
|
||||
@jerop in [#17694](https://github.com/google-gemini/gemini-cli/pull/17694)
|
||||
- GEMINI.md polish by @jacob314 in
|
||||
[#17680](https://github.com/google-gemini/gemini-cli/pull/17680)
|
||||
- refactor(core): centralize path validation and allow temp dir access for tools
|
||||
by @NTaylorMullen in
|
||||
[#17185](https://github.com/google-gemini/gemini-cli/pull/17185)
|
||||
- feat(skills): promote Agent Skills to stable by @abhipatel12 in
|
||||
[#17693](https://github.com/google-gemini/gemini-cli/pull/17693)
|
||||
- refactor(cli): keyboard handling and AskUserDialog by @jacob314 in
|
||||
[#17414](https://github.com/google-gemini/gemini-cli/pull/17414)
|
||||
- docs: Add Experimental Remote Agent Docs by @adamfweidman in
|
||||
[#17697](https://github.com/google-gemini/gemini-cli/pull/17697)
|
||||
- revert: promote Agent Skills to stable (#17693) by @abhipatel12 in
|
||||
[#17712](https://github.com/google-gemini/gemini-cli/pull/17712)
|
||||
- feat(ux) Expandable (ctrl-O) and scrollable approvals in alternate buffer
|
||||
mode. by @jacob314 in
|
||||
[#17640](https://github.com/google-gemini/gemini-cli/pull/17640)
|
||||
- feat(skills): promote skills settings to stable by @abhipatel12 in
|
||||
[#17713](https://github.com/google-gemini/gemini-cli/pull/17713)
|
||||
- fix(cli): Preserve settings dialog focus when searching by @SandyTao520 in
|
||||
[#17701](https://github.com/google-gemini/gemini-cli/pull/17701)
|
||||
- feat(ui): add terminal cursor support by @jacob314 in
|
||||
[#17711](https://github.com/google-gemini/gemini-cli/pull/17711)
|
||||
- docs(skills): remove experimental labels and update tutorials by @abhipatel12
|
||||
in [#17714](https://github.com/google-gemini/gemini-cli/pull/17714)
|
||||
- docs: remove 'experimental' syntax for hooks in docs by @abhipatel12 in
|
||||
[#17660](https://github.com/google-gemini/gemini-cli/pull/17660)
|
||||
- Add support for an additional exclusion file besides .gitignore and
|
||||
.geminiignore by @alisa-alisa in
|
||||
[#16487](https://github.com/google-gemini/gemini-cli/pull/16487)
|
||||
- feat: add review-frontend-and-fix command by @galz10 in
|
||||
[#17707](https://github.com/google-gemini/gemini-cli/pull/17707)
|
||||
|
||||
**Full changelog**:
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.24.5...v0.25.0
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.26.0...v0.27.0
|
||||
|
||||
+289
-314
@@ -1,6 +1,6 @@
|
||||
# Preview release: Release v0.26.0-preview.0
|
||||
# Preview release: Release v0.28.0-preview.0
|
||||
|
||||
Released: January 21, 2026
|
||||
Released: February 3, 2026
|
||||
|
||||
Our preview release includes the latest, new, and experimental features. This
|
||||
release may not be as stable as our [latest weekly release](latest.md).
|
||||
@@ -13,320 +13,295 @@ npm install -g @google/gemini-cli@preview
|
||||
|
||||
## Highlights
|
||||
|
||||
- **Skills and Agents:** Improvements to the `activate_skill` tool and skill
|
||||
management. Experimental Agent Skills support.
|
||||
- **UI/UX:** Addition of a Rewind Confirmation dialog and Viewer component.
|
||||
- **Extensions:** Experimental setting for extension configuration.
|
||||
- **Bug Fixes and Stability:** PDF token estimation fix and improvements to
|
||||
scheduled issue triage.
|
||||
- **Improved Hooks Management:** Hooks enable/disable functionality now aligns
|
||||
with skills and offers improved completion.
|
||||
- **Custom Themes for Extensions:** Extensions can now support custom themes,
|
||||
allowing for greater personalization.
|
||||
- **User Identity Display:** User identity information (auth, email, tier) is
|
||||
now displayed on startup and in the `stats` command.
|
||||
- **Plan Mode Enhancements:** Plan mode has been improved with a generic
|
||||
`Checklist` component and refactored `Todo`.
|
||||
- **Background Shell Commands:** Implementation of background shell commands.
|
||||
|
||||
## What's Changed
|
||||
|
||||
- fix: PDF token estimation
|
||||
([#16494](https://github.com/google-gemini/gemini-cli/pull/16494)) by
|
||||
@korade-krushna in
|
||||
[#16527](https://github.com/google-gemini/gemini-cli/pull/16527)
|
||||
- chore(release): bump version to 0.26.0-nightly.20260114.bb6c57414 by
|
||||
@gemini-cli-robot in
|
||||
[#16604](https://github.com/google-gemini/gemini-cli/pull/16604)
|
||||
- docs: clarify F12 to open debug console by @jackwotherspoon in
|
||||
[#16570](https://github.com/google-gemini/gemini-cli/pull/16570)
|
||||
- docs: Remove .md extension from internal links in architecture.md by
|
||||
@medic-code in
|
||||
[#12899](https://github.com/google-gemini/gemini-cli/pull/12899)
|
||||
- Add an experimental setting for extension config by @chrstnb in
|
||||
[#16506](https://github.com/google-gemini/gemini-cli/pull/16506)
|
||||
- feat: add Rewind Confirmation dialog and Rewind Viewer component by @Adib234
|
||||
in [#15717](https://github.com/google-gemini/gemini-cli/pull/15717)
|
||||
- fix(a2a): Don't throw errors for GeminiEventType Retry and InvalidStream. by
|
||||
@ehedlund in [#16541](https://github.com/google-gemini/gemini-cli/pull/16541)
|
||||
- prefactor: add rootCommands as array so it can be used for policy parsing by
|
||||
@abhipatel12 in
|
||||
[#16640](https://github.com/google-gemini/gemini-cli/pull/16640)
|
||||
- remove unnecessary \x7f key bindings by @scidomino in
|
||||
[#16646](https://github.com/google-gemini/gemini-cli/pull/16646)
|
||||
- docs(skills): use body-file in pr-creator skill for better reliability by
|
||||
@abhipatel12 in
|
||||
[#16642](https://github.com/google-gemini/gemini-cli/pull/16642)
|
||||
- chore(automation): recursive labeling for workstream descendants by @bdmorgan
|
||||
in [#16609](https://github.com/google-gemini/gemini-cli/pull/16609)
|
||||
- feat: introduce 'skill-creator' built-in skill and CJS management tools by
|
||||
@NTaylorMullen in
|
||||
[#16394](https://github.com/google-gemini/gemini-cli/pull/16394)
|
||||
- chore(automation): remove automated PR size and complexity labeler by
|
||||
@bdmorgan in [#16648](https://github.com/google-gemini/gemini-cli/pull/16648)
|
||||
- refactor(skills): replace 'project' with 'workspace' scope by @NTaylorMullen
|
||||
in [#16380](https://github.com/google-gemini/gemini-cli/pull/16380)
|
||||
- Docs: Update release notes for 1/13/2026 by @jkcinouye in
|
||||
[#16583](https://github.com/google-gemini/gemini-cli/pull/16583)
|
||||
- Simplify paste handling by @scidomino in
|
||||
[#16654](https://github.com/google-gemini/gemini-cli/pull/16654)
|
||||
- chore(automation): improve scheduled issue triage discovery and throughput by
|
||||
@bdmorgan in [#16652](https://github.com/google-gemini/gemini-cli/pull/16652)
|
||||
- fix(acp): run exit cleanup when stdin closes by @codefromthecrypt in
|
||||
[#14953](https://github.com/google-gemini/gemini-cli/pull/14953)
|
||||
- feat(scheduler): add types needed for event driven scheduler by @abhipatel12
|
||||
in [#16641](https://github.com/google-gemini/gemini-cli/pull/16641)
|
||||
- Remove unused rewind key binding by @scidomino in
|
||||
[#16659](https://github.com/google-gemini/gemini-cli/pull/16659)
|
||||
- Remove sequence binding by @scidomino in
|
||||
[#16664](https://github.com/google-gemini/gemini-cli/pull/16664)
|
||||
- feat(cli): undeprecate the --prompt flag by @alexaustin007 in
|
||||
[#13981](https://github.com/google-gemini/gemini-cli/pull/13981)
|
||||
- chore: update dependabot configuration by @cosmopax in
|
||||
[#13507](https://github.com/google-gemini/gemini-cli/pull/13507)
|
||||
- feat(config): add 'auto' alias for default model selection by @sehoon38 in
|
||||
[#16661](https://github.com/google-gemini/gemini-cli/pull/16661)
|
||||
- Enable & disable agents by @sehoon38 in
|
||||
[#16225](https://github.com/google-gemini/gemini-cli/pull/16225)
|
||||
- cleanup: Improve keybindings by @scidomino in
|
||||
[#16672](https://github.com/google-gemini/gemini-cli/pull/16672)
|
||||
- Add timeout for shell-utils to prevent hangs. by @jacob314 in
|
||||
[#16667](https://github.com/google-gemini/gemini-cli/pull/16667)
|
||||
- feat(plan): add experimental plan flag by @jerop in
|
||||
[#16650](https://github.com/google-gemini/gemini-cli/pull/16650)
|
||||
- feat(cli): add security consent prompts for skill installation by
|
||||
@NTaylorMullen in
|
||||
[#16549](https://github.com/google-gemini/gemini-cli/pull/16549)
|
||||
- fix: replace 3 consecutive periods with ellipsis character by @Vist233 in
|
||||
[#16587](https://github.com/google-gemini/gemini-cli/pull/16587)
|
||||
- chore(automation): ensure status/need-triage is applied and never cleared
|
||||
automatically by @bdmorgan in
|
||||
[#16657](https://github.com/google-gemini/gemini-cli/pull/16657)
|
||||
- fix: Handle colons in skill description frontmatter by @maru0804 in
|
||||
[#16345](https://github.com/google-gemini/gemini-cli/pull/16345)
|
||||
- refactor(core): harden skill frontmatter parsing by @NTaylorMullen in
|
||||
[#16705](https://github.com/google-gemini/gemini-cli/pull/16705)
|
||||
- feat(skills): add conflict detection and warnings for skill overrides by
|
||||
@NTaylorMullen in
|
||||
[#16709](https://github.com/google-gemini/gemini-cli/pull/16709)
|
||||
- feat(scheduler): add SchedulerStateManager for reactive tool state by
|
||||
@abhipatel12 in
|
||||
[#16651](https://github.com/google-gemini/gemini-cli/pull/16651)
|
||||
- chore(automation): enforce 'help wanted' label permissions and update
|
||||
guidelines by @bdmorgan in
|
||||
[#16707](https://github.com/google-gemini/gemini-cli/pull/16707)
|
||||
- fix(core): resolve circular dependency via tsconfig paths by @sehoon38 in
|
||||
[#16730](https://github.com/google-gemini/gemini-cli/pull/16730)
|
||||
- chore/release: bump version to 0.26.0-nightly.20260115.6cb3ae4e0 by
|
||||
@gemini-cli-robot in
|
||||
[#16738](https://github.com/google-gemini/gemini-cli/pull/16738)
|
||||
- fix(automation): correct status/need-issue label matching wildcard by
|
||||
@bdmorgan in [#16727](https://github.com/google-gemini/gemini-cli/pull/16727)
|
||||
- fix(automation): prevent label-enforcer loop by ignoring all bots by @bdmorgan
|
||||
in [#16746](https://github.com/google-gemini/gemini-cli/pull/16746)
|
||||
- Add links to supported locations and minor fixes by @g-samroberts in
|
||||
[#16476](https://github.com/google-gemini/gemini-cli/pull/16476)
|
||||
- feat(policy): add source tracking to policy rules by @allenhutchison in
|
||||
[#16670](https://github.com/google-gemini/gemini-cli/pull/16670)
|
||||
- feat(automation): enforce '🔒 maintainer only' and fix bot loop by @bdmorgan
|
||||
in [#16751](https://github.com/google-gemini/gemini-cli/pull/16751)
|
||||
- Make merged settings non-nullable and fix all lints related to that. by
|
||||
@jacob314 in [#16647](https://github.com/google-gemini/gemini-cli/pull/16647)
|
||||
- fix(core): prevent ModelInfo event emission on aborted signal by @sehoon38 in
|
||||
[#16752](https://github.com/google-gemini/gemini-cli/pull/16752)
|
||||
- Replace relative paths to fix website build by @chrstnb in
|
||||
[#16755](https://github.com/google-gemini/gemini-cli/pull/16755)
|
||||
- Restricting to localhost by @cocosheng-g in
|
||||
[#16548](https://github.com/google-gemini/gemini-cli/pull/16548)
|
||||
- fix(cli): add explicit dependency on color-convert by @sehoon38 in
|
||||
[#16757](https://github.com/google-gemini/gemini-cli/pull/16757)
|
||||
- fix(automation): robust label enforcement with permission checks by @bdmorgan
|
||||
in [#16762](https://github.com/google-gemini/gemini-cli/pull/16762)
|
||||
- fix(cli): prevent OOM crash by limiting file search traversal and adding
|
||||
timeout by @galz10 in
|
||||
[#16696](https://github.com/google-gemini/gemini-cli/pull/16696)
|
||||
- fix(cli): safely handle /dev/tty access on macOS by @korade-krushna in
|
||||
[#16531](https://github.com/google-gemini/gemini-cli/pull/16531)
|
||||
- docs: clarify workspace test execution in GEMINI.md by @mattKorwel in
|
||||
[#16764](https://github.com/google-gemini/gemini-cli/pull/16764)
|
||||
- Add support for running available commands prior to MCP servers loading by
|
||||
@Adib234 in [#15596](https://github.com/google-gemini/gemini-cli/pull/15596)
|
||||
- feat(plan): add experimental 'plan' approval mode by @jerop in
|
||||
[#16753](https://github.com/google-gemini/gemini-cli/pull/16753)
|
||||
- feat(scheduler): add functional awaitConfirmation utility by @abhipatel12 in
|
||||
[#16721](https://github.com/google-gemini/gemini-cli/pull/16721)
|
||||
- fix(infra): update maintainer rollup label to 'workstream-rollup' by @bdmorgan
|
||||
in [#16809](https://github.com/google-gemini/gemini-cli/pull/16809)
|
||||
- fix(infra): use GraphQL to detect direct parents in rollup workflow by
|
||||
@bdmorgan in [#16811](https://github.com/google-gemini/gemini-cli/pull/16811)
|
||||
- chore(workflows): rename label-workstream-rollup workflow by @bdmorgan in
|
||||
[#16818](https://github.com/google-gemini/gemini-cli/pull/16818)
|
||||
- skip simple-mcp-server.test.ts by @scidomino in
|
||||
[#16842](https://github.com/google-gemini/gemini-cli/pull/16842)
|
||||
- Steer outer agent to use expert subagents when present by @gundermanc in
|
||||
[#16763](https://github.com/google-gemini/gemini-cli/pull/16763)
|
||||
- Fix race condition by awaiting scheduleToolCalls by @chrstnb in
|
||||
[#16759](https://github.com/google-gemini/gemini-cli/pull/16759)
|
||||
- cleanup: Organize key bindings by @scidomino in
|
||||
[#16798](https://github.com/google-gemini/gemini-cli/pull/16798)
|
||||
- feat(core): Add generalist agent. by @joshualitt in
|
||||
[#16638](https://github.com/google-gemini/gemini-cli/pull/16638)
|
||||
- perf(ui): optimize text buffer and highlighting for large inputs by
|
||||
@NTaylorMullen in
|
||||
[#16782](https://github.com/google-gemini/gemini-cli/pull/16782)
|
||||
- fix(core): fix PTY descriptor shell leak by @galz10 in
|
||||
[#16773](https://github.com/google-gemini/gemini-cli/pull/16773)
|
||||
- feat(plan): enforce strict read-only policy and halt execution on violation by
|
||||
@jerop in [#16849](https://github.com/google-gemini/gemini-cli/pull/16849)
|
||||
- remove need-triage label from bug_report template by @sehoon38 in
|
||||
[#16864](https://github.com/google-gemini/gemini-cli/pull/16864)
|
||||
- fix(core): truncate large telemetry log entries by @sehoon38 in
|
||||
[#16769](https://github.com/google-gemini/gemini-cli/pull/16769)
|
||||
- docs(extensions): add Agent Skills support and mark feature as experimental by
|
||||
@NTaylorMullen in
|
||||
[#16859](https://github.com/google-gemini/gemini-cli/pull/16859)
|
||||
- fix(core): surface warnings for invalid hook event names in configuration
|
||||
([#16788](https://github.com/google-gemini/gemini-cli/pull/16788)) by
|
||||
@sehoon38 in [#16873](https://github.com/google-gemini/gemini-cli/pull/16873)
|
||||
- feat(plan): remove read_many_files from approval mode policies by @jerop in
|
||||
[#16876](https://github.com/google-gemini/gemini-cli/pull/16876)
|
||||
- feat(admin): implement admin controls polling and restart prompt by @skeshive
|
||||
in [#16627](https://github.com/google-gemini/gemini-cli/pull/16627)
|
||||
- Remove LRUCache class migrating to mnemoist by @jacob314 in
|
||||
[#16872](https://github.com/google-gemini/gemini-cli/pull/16872)
|
||||
- feat(settings): rename negative settings to positive naming (disable* ->
|
||||
enable*) by @afarber in
|
||||
[#14142](https://github.com/google-gemini/gemini-cli/pull/14142)
|
||||
- refactor(cli): unify shell confirmation dialogs by @NTaylorMullen in
|
||||
[#16828](https://github.com/google-gemini/gemini-cli/pull/16828)
|
||||
- feat(agent): enable agent skills by default by @NTaylorMullen in
|
||||
[#16736](https://github.com/google-gemini/gemini-cli/pull/16736)
|
||||
- refactor(core): foundational truncation refactoring and token estimation
|
||||
optimization by @NTaylorMullen in
|
||||
[#16824](https://github.com/google-gemini/gemini-cli/pull/16824)
|
||||
- fix(hooks): enable /hooks disable to reliably stop single hooks by
|
||||
@abhipatel12 in
|
||||
[#16804](https://github.com/google-gemini/gemini-cli/pull/16804)
|
||||
- Don't commit unless user asks us to. by @gundermanc in
|
||||
[#16902](https://github.com/google-gemini/gemini-cli/pull/16902)
|
||||
- chore: remove a2a-adapter and bump @a2a-js/sdk to 0.3.8 by @adamfweidman in
|
||||
[#16800](https://github.com/google-gemini/gemini-cli/pull/16800)
|
||||
- fix: Show experiment values in settings UI for compressionThreshold by
|
||||
@ishaanxgupta in
|
||||
[#16267](https://github.com/google-gemini/gemini-cli/pull/16267)
|
||||
- feat(cli): replace relative keyboard shortcuts link with web URL by
|
||||
@imaliabbas in
|
||||
[#16479](https://github.com/google-gemini/gemini-cli/pull/16479)
|
||||
- fix(core): resolve PKCE length issue and stabilize OAuth redirect port by
|
||||
@sehoon38 in [#16815](https://github.com/google-gemini/gemini-cli/pull/16815)
|
||||
- Delete rewind documentation for now by @Adib234 in
|
||||
[#16932](https://github.com/google-gemini/gemini-cli/pull/16932)
|
||||
- Stabilize skill-creator CI and package format by @NTaylorMullen in
|
||||
[#17001](https://github.com/google-gemini/gemini-cli/pull/17001)
|
||||
- Stabilize the git evals by @gundermanc in
|
||||
[#16989](https://github.com/google-gemini/gemini-cli/pull/16989)
|
||||
- fix(core): attempt compression before context overflow check by @NTaylorMullen
|
||||
in [#16914](https://github.com/google-gemini/gemini-cli/pull/16914)
|
||||
- Fix inverted logic. by @gundermanc in
|
||||
[#17007](https://github.com/google-gemini/gemini-cli/pull/17007)
|
||||
- chore(scripts): add duplicate issue closer script and fix lint errors by
|
||||
@bdmorgan in [#16997](https://github.com/google-gemini/gemini-cli/pull/16997)
|
||||
- docs: update README and config guide to reference Gemini 3 by @JayadityaGit in
|
||||
[#15806](https://github.com/google-gemini/gemini-cli/pull/15806)
|
||||
- fix(cli): correct Homebrew installation detection by @kij in
|
||||
[#14727](https://github.com/google-gemini/gemini-cli/pull/14727)
|
||||
- Demote git evals to nightly run. by @gundermanc in
|
||||
[#17030](https://github.com/google-gemini/gemini-cli/pull/17030)
|
||||
- fix(cli): use OSC-52 clipboard copy in Windows Terminal by @Thomas-Shephard in
|
||||
[#16920](https://github.com/google-gemini/gemini-cli/pull/16920)
|
||||
- Fix: Process all parts in response chunks when thought is first by @pyrytakala
|
||||
in [#13539](https://github.com/google-gemini/gemini-cli/pull/13539)
|
||||
- fix(automation): fix jq quoting error in pr-triage.sh by @Kimsoo0119 in
|
||||
[#16958](https://github.com/google-gemini/gemini-cli/pull/16958)
|
||||
- refactor(core): decouple scheduler into orchestration, policy, and
|
||||
confirmation by @abhipatel12 in
|
||||
[#16895](https://github.com/google-gemini/gemini-cli/pull/16895)
|
||||
- feat: add /introspect slash command by @NTaylorMullen in
|
||||
[#17048](https://github.com/google-gemini/gemini-cli/pull/17048)
|
||||
- refactor(cli): centralize tool mapping and decouple legacy scheduler by
|
||||
@abhipatel12 in
|
||||
[#17044](https://github.com/google-gemini/gemini-cli/pull/17044)
|
||||
- fix(ui): ensure rationale renders before tool calls by @NTaylorMullen in
|
||||
[#17043](https://github.com/google-gemini/gemini-cli/pull/17043)
|
||||
- fix(workflows): use author_association for maintainer check by @bdmorgan in
|
||||
[#17060](https://github.com/google-gemini/gemini-cli/pull/17060)
|
||||
- fix return type of fireSessionStartEvent to defaultHookOutput by @ved015 in
|
||||
[#16833](https://github.com/google-gemini/gemini-cli/pull/16833)
|
||||
- feat(cli): add experiment gate for event-driven scheduler by @abhipatel12 in
|
||||
[#17055](https://github.com/google-gemini/gemini-cli/pull/17055)
|
||||
- feat(core): improve shell redirection transparency and security by
|
||||
@NTaylorMullen in
|
||||
[#16486](https://github.com/google-gemini/gemini-cli/pull/16486)
|
||||
- fix(core): deduplicate ModelInfo emission in GeminiClient by @NTaylorMullen in
|
||||
[#17075](https://github.com/google-gemini/gemini-cli/pull/17075)
|
||||
- docs(themes): remove unsupported DiffModified color key by @jw409 in
|
||||
[#17073](https://github.com/google-gemini/gemini-cli/pull/17073)
|
||||
- fix: update currentSequenceModel when modelChanged by @adamfweidman in
|
||||
[#17051](https://github.com/google-gemini/gemini-cli/pull/17051)
|
||||
- feat(core): enhanced anchored iterative context compression with
|
||||
self-verification by @rmedranollamas in
|
||||
[#15710](https://github.com/google-gemini/gemini-cli/pull/15710)
|
||||
- Fix mcp instructions by @chrstnb in
|
||||
[#16439](https://github.com/google-gemini/gemini-cli/pull/16439)
|
||||
- [A2A] Disable checkpointing if git is not installed by @cocosheng-g in
|
||||
[#16896](https://github.com/google-gemini/gemini-cli/pull/16896)
|
||||
- feat(admin): set admin.skills.enabled based on advancedFeaturesEnabled setting
|
||||
by @skeshive in
|
||||
[#17095](https://github.com/google-gemini/gemini-cli/pull/17095)
|
||||
- Test coverage for hook exit code cases by @gundermanc in
|
||||
[#17041](https://github.com/google-gemini/gemini-cli/pull/17041)
|
||||
- Revert "Revert "Update extension examples"" by @chrstnb in
|
||||
[#16445](https://github.com/google-gemini/gemini-cli/pull/16445)
|
||||
- fix(core): Provide compact, actionable errors for agent delegation failures by
|
||||
@SandyTao520 in
|
||||
[#16493](https://github.com/google-gemini/gemini-cli/pull/16493)
|
||||
- fix: migrate BeforeModel and AfterModel hooks to HookSystem by @ved015 in
|
||||
[#16599](https://github.com/google-gemini/gemini-cli/pull/16599)
|
||||
- feat(admin): apply admin settings to gemini skills/mcp/extensions commands by
|
||||
@skeshive in [#17102](https://github.com/google-gemini/gemini-cli/pull/17102)
|
||||
- fix(core): update telemetry token count after session resume by @psinha40898
|
||||
in [#15491](https://github.com/google-gemini/gemini-cli/pull/15491)
|
||||
- Demote the subagent test to nightly by @gundermanc in
|
||||
[#17105](https://github.com/google-gemini/gemini-cli/pull/17105)
|
||||
- feat(plan): telemetry to track adoption and usage of plan mode by @Adib234 in
|
||||
[#16863](https://github.com/google-gemini/gemini-cli/pull/16863)
|
||||
- feat: Add flash lite utility fallback chain by @adamfweidman in
|
||||
[#17056](https://github.com/google-gemini/gemini-cli/pull/17056)
|
||||
- Fixes Windows crash: "Cannot resize a pty that has already exited" by @dzammit
|
||||
in [#15757](https://github.com/google-gemini/gemini-cli/pull/15757)
|
||||
- feat(core): Add initial eval for generalist agent. by @joshualitt in
|
||||
[#16856](https://github.com/google-gemini/gemini-cli/pull/16856)
|
||||
- feat(core): unify agent enabled and disabled flags by @SandyTao520 in
|
||||
[#17127](https://github.com/google-gemini/gemini-cli/pull/17127)
|
||||
- fix(core): resolve auto model in default strategy by @sehoon38 in
|
||||
[#17116](https://github.com/google-gemini/gemini-cli/pull/17116)
|
||||
- docs: update project context and pr-creator workflow by @NTaylorMullen in
|
||||
[#17119](https://github.com/google-gemini/gemini-cli/pull/17119)
|
||||
- fix(cli): send gemini-cli version as mcp client version by @dsp in
|
||||
[#13407](https://github.com/google-gemini/gemini-cli/pull/13407)
|
||||
- fix(cli): resolve Ctrl+Enter and Ctrl+J newline issues by @imadraude in
|
||||
[#17021](https://github.com/google-gemini/gemini-cli/pull/17021)
|
||||
- Remove missing sidebar item by @chrstnb in
|
||||
[#17145](https://github.com/google-gemini/gemini-cli/pull/17145)
|
||||
- feat(core): Ensure all properties in hooks object are event names. by
|
||||
@joshualitt in
|
||||
[#16870](https://github.com/google-gemini/gemini-cli/pull/16870)
|
||||
- fix(cli): fix newline support broken in previous PR by @scidomino in
|
||||
[#17159](https://github.com/google-gemini/gemini-cli/pull/17159)
|
||||
- Add interactive ValidationDialog for handling 403 VALIDATION_REQUIRED errors.
|
||||
by @gsquared94 in
|
||||
[#16231](https://github.com/google-gemini/gemini-cli/pull/16231)
|
||||
- Add Esc-Esc to clear prompt when it's not empty by @Adib234 in
|
||||
[#17131](https://github.com/google-gemini/gemini-cli/pull/17131)
|
||||
- Avoid spurious warnings about unexpected renders triggered by appEvents and
|
||||
coreEvents. by @jacob314 in
|
||||
[#17160](https://github.com/google-gemini/gemini-cli/pull/17160)
|
||||
- fix(cli): resolve home/end keybinding conflict by @scidomino in
|
||||
[#17124](https://github.com/google-gemini/gemini-cli/pull/17124)
|
||||
- fix(cli): display 'http' type on mcp list by @pamanta in
|
||||
[#16915](https://github.com/google-gemini/gemini-cli/pull/16915)
|
||||
- fix bad fallback logic external editor logic by @scidomino in
|
||||
[#17166](https://github.com/google-gemini/gemini-cli/pull/17166)
|
||||
- Fix bug where System scopes weren't migrated. by @jacob314 in
|
||||
[#17174](https://github.com/google-gemini/gemini-cli/pull/17174)
|
||||
- Fix mcp tool lookup in tool registry by @werdnum in
|
||||
[#17054](https://github.com/google-gemini/gemini-cli/pull/17054)
|
||||
- feat(commands): add /prompt-suggest slash command by NTaylorMullen in
|
||||
[#17264](https://github.com/google-gemini/gemini-cli/pull/17264)
|
||||
- feat(cli): align hooks enable/disable with skills and improve completion by
|
||||
sehoon38 in [#16822](https://github.com/google-gemini/gemini-cli/pull/16822)
|
||||
- docs: add CLI reference documentation by leochiu-a in
|
||||
[#17504](https://github.com/google-gemini/gemini-cli/pull/17504)
|
||||
- chore(release): bump version to 0.28.0-nightly.20260128.adc8e11bb by
|
||||
gemini-cli-robot in
|
||||
[#17725](https://github.com/google-gemini/gemini-cli/pull/17725)
|
||||
- feat(skills): final stable promotion cleanup by abhipatel12 in
|
||||
[#17726](https://github.com/google-gemini/gemini-cli/pull/17726)
|
||||
- test(core): mock fetch in OAuth transport fallback tests by jw409 in
|
||||
[#17059](https://github.com/google-gemini/gemini-cli/pull/17059)
|
||||
- feat(cli): include auth method in /bug by erikus in
|
||||
[#17569](https://github.com/google-gemini/gemini-cli/pull/17569)
|
||||
- Add a email privacy note to bug_report template by nemyung in
|
||||
[#17474](https://github.com/google-gemini/gemini-cli/pull/17474)
|
||||
- Rewind documentation by Adib234 in
|
||||
[#17446](https://github.com/google-gemini/gemini-cli/pull/17446)
|
||||
- fix: verify audio/video MIME types with content check by maru0804 in
|
||||
[#16907](https://github.com/google-gemini/gemini-cli/pull/16907)
|
||||
- feat(core): add support for positron ide (#15045) by kapsner in
|
||||
[#15047](https://github.com/google-gemini/gemini-cli/pull/15047)
|
||||
- /oncall dedup - wrap texts to nextlines by sehoon38 in
|
||||
[#17782](https://github.com/google-gemini/gemini-cli/pull/17782)
|
||||
- fix(admin): rename advanced features admin setting by skeshive in
|
||||
[#17786](https://github.com/google-gemini/gemini-cli/pull/17786)
|
||||
- [extension config] Make breaking optional value non-optional by chrstnb in
|
||||
[#17785](https://github.com/google-gemini/gemini-cli/pull/17785)
|
||||
- Fix docs-writer skill issues by g-samroberts in
|
||||
[#17734](https://github.com/google-gemini/gemini-cli/pull/17734)
|
||||
- fix(core): suppress duplicate hook failure warnings during streaming by
|
||||
abhipatel12 in
|
||||
[#17727](https://github.com/google-gemini/gemini-cli/pull/17727)
|
||||
- test: add more tests for AskUser by jackwotherspoon in
|
||||
[#17720](https://github.com/google-gemini/gemini-cli/pull/17720)
|
||||
- feat(cli): enable activity logging for non-interactive mode and evals by
|
||||
SandyTao520 in
|
||||
[#17703](https://github.com/google-gemini/gemini-cli/pull/17703)
|
||||
- feat(core): add support for custom deny messages in policy rules by
|
||||
allenhutchison in
|
||||
[#17427](https://github.com/google-gemini/gemini-cli/pull/17427)
|
||||
- Fix unintended credential exposure to MCP Servers by Adib234 in
|
||||
[#17311](https://github.com/google-gemini/gemini-cli/pull/17311)
|
||||
- feat(extensions): add support for custom themes in extensions by spencer426 in
|
||||
[#17327](https://github.com/google-gemini/gemini-cli/pull/17327)
|
||||
- fix: persist and restore workspace directories on session resume by
|
||||
korade-krushna in
|
||||
[#17454](https://github.com/google-gemini/gemini-cli/pull/17454)
|
||||
- Update release notes pages for 0.26.0 and 0.27.0-preview. by g-samroberts in
|
||||
[#17744](https://github.com/google-gemini/gemini-cli/pull/17744)
|
||||
- feat(ux): update cell border color and created test file for table rendering
|
||||
by devr0306 in
|
||||
[#17798](https://github.com/google-gemini/gemini-cli/pull/17798)
|
||||
- Change height for the ToolConfirmationQueue. by jacob314 in
|
||||
[#17799](https://github.com/google-gemini/gemini-cli/pull/17799)
|
||||
- feat(cli): add user identity info to stats command by sehoon38 in
|
||||
[#17612](https://github.com/google-gemini/gemini-cli/pull/17612)
|
||||
- fix(ux): fixed off-by-some wrapping caused by fixed-width characters by
|
||||
devr0306 in [#17816](https://github.com/google-gemini/gemini-cli/pull/17816)
|
||||
- feat(cli): update undo/redo keybindings to Cmd+Z/Alt+Z and
|
||||
Shift+Cmd+Z/Shift+Alt+Z by scidomino in
|
||||
[#17800](https://github.com/google-gemini/gemini-cli/pull/17800)
|
||||
- fix(evals): use absolute path for activity log directory by SandyTao520 in
|
||||
[#17830](https://github.com/google-gemini/gemini-cli/pull/17830)
|
||||
- test: add integration test to verify stdout/stderr routing by ved015 in
|
||||
[#17280](https://github.com/google-gemini/gemini-cli/pull/17280)
|
||||
- fix(cli): list installed extensions when update target missing by tt-a1i in
|
||||
[#17082](https://github.com/google-gemini/gemini-cli/pull/17082)
|
||||
- fix(cli): handle PAT tokens and credentials in git remote URL parsing by
|
||||
afarber in [#14650](https://github.com/google-gemini/gemini-cli/pull/14650)
|
||||
- fix(core): use returnDisplay for error result display by Nubebuster in
|
||||
[#14994](https://github.com/google-gemini/gemini-cli/pull/14994)
|
||||
- Fix detection of bun as package manager by Randomblock1 in
|
||||
[#17462](https://github.com/google-gemini/gemini-cli/pull/17462)
|
||||
- feat(cli): show hooksConfig.enabled in settings dialog by abhipatel12 in
|
||||
[#17810](https://github.com/google-gemini/gemini-cli/pull/17810)
|
||||
- feat(cli): Display user identity (auth, email, tier) on startup by yunaseoul
|
||||
in [#17591](https://github.com/google-gemini/gemini-cli/pull/17591)
|
||||
- fix: prevent ghost border for AskUserDialog by jackwotherspoon in
|
||||
[#17788](https://github.com/google-gemini/gemini-cli/pull/17788)
|
||||
- docs: mark A2A subagents as experimental in subagents.md by adamfweidman in
|
||||
[#17863](https://github.com/google-gemini/gemini-cli/pull/17863)
|
||||
- Resolve error thrown for sensitive values by chrstnb in
|
||||
[#17826](https://github.com/google-gemini/gemini-cli/pull/17826)
|
||||
- fix(admin): Rename secureModeEnabled to strictModeDisabled by skeshive in
|
||||
[#17789](https://github.com/google-gemini/gemini-cli/pull/17789)
|
||||
- feat(ux): update truncate dots to be shorter in tables by devr0306 in
|
||||
[#17825](https://github.com/google-gemini/gemini-cli/pull/17825)
|
||||
- fix(core): resolve DEP0040 punycode deprecation via patch-package by
|
||||
ATHARVA262005 in
|
||||
[#17692](https://github.com/google-gemini/gemini-cli/pull/17692)
|
||||
- feat(plan): create generic Checklist component and refactor Todo by Adib234 in
|
||||
[#17741](https://github.com/google-gemini/gemini-cli/pull/17741)
|
||||
- Cleanup post delegate_to_agent removal by gundermanc in
|
||||
[#17875](https://github.com/google-gemini/gemini-cli/pull/17875)
|
||||
- fix(core): use GIT_CONFIG_GLOBAL to isolate shadow git repo configuration -
|
||||
Fixes #17877 by cocosheng-g in
|
||||
[#17803](https://github.com/google-gemini/gemini-cli/pull/17803)
|
||||
- Disable mouse tracking e2e by alisa-alisa in
|
||||
[#17880](https://github.com/google-gemini/gemini-cli/pull/17880)
|
||||
- fix(cli): use correct setting key for Cloud Shell auth by sehoon38 in
|
||||
[#17884](https://github.com/google-gemini/gemini-cli/pull/17884)
|
||||
- chore: revert IDE specific ASCII logo by jackwotherspoon in
|
||||
[#17887](https://github.com/google-gemini/gemini-cli/pull/17887)
|
||||
- Revert "fix(core): resolve DEP0040 punycode deprecation via patch-package" by
|
||||
sehoon38 in [#17898](https://github.com/google-gemini/gemini-cli/pull/17898)
|
||||
- Refactoring of disabling of mouse tracking in e2e tests by alisa-alisa in
|
||||
[#17902](https://github.com/google-gemini/gemini-cli/pull/17902)
|
||||
- feat(core): Add GOOGLE_GENAI_API_VERSION environment variable support by deyim
|
||||
in [#16177](https://github.com/google-gemini/gemini-cli/pull/16177)
|
||||
- feat(core): Isolate and cleanup truncated tool outputs by SandyTao520 in
|
||||
[#17594](https://github.com/google-gemini/gemini-cli/pull/17594)
|
||||
- Create skills page, update commands, refine docs by g-samroberts in
|
||||
[#17842](https://github.com/google-gemini/gemini-cli/pull/17842)
|
||||
- feat: preserve EOL in files by Thomas-Shephard in
|
||||
[#16087](https://github.com/google-gemini/gemini-cli/pull/16087)
|
||||
- Fix HalfLinePaddedBox in screenreader mode. by jacob314 in
|
||||
[#17914](https://github.com/google-gemini/gemini-cli/pull/17914)
|
||||
- bug(ux) vim mode fixes. Start in insert mode. Fix bug blocking F12 and ctrl-X
|
||||
in vim mode. by jacob314 in
|
||||
[#17938](https://github.com/google-gemini/gemini-cli/pull/17938)
|
||||
- feat(core): implement interactive and non-interactive consent for OAuth by
|
||||
ehedlund in [#17699](https://github.com/google-gemini/gemini-cli/pull/17699)
|
||||
- perf(core): optimize token calculation and add support for multimodal tool
|
||||
responses by abhipatel12 in
|
||||
[#17835](https://github.com/google-gemini/gemini-cli/pull/17835)
|
||||
- refactor(hooks): remove legacy tools.enableHooks setting by abhipatel12 in
|
||||
[#17867](https://github.com/google-gemini/gemini-cli/pull/17867)
|
||||
- feat(ci): add npx smoke test to verify installability by bdmorgan in
|
||||
[#17927](https://github.com/google-gemini/gemini-cli/pull/17927)
|
||||
- feat(core): implement dynamic policy registration for subagents by abhipatel12
|
||||
in [#17838](https://github.com/google-gemini/gemini-cli/pull/17838)
|
||||
- feat: Implement background shell commands by galz10 in
|
||||
[#14849](https://github.com/google-gemini/gemini-cli/pull/14849)
|
||||
- feat(admin): provide actionable error messages for disabled features by
|
||||
skeshive in [#17815](https://github.com/google-gemini/gemini-cli/pull/17815)
|
||||
- Fix bugs where Rewind and Resume showed Ugly and 100X too verbose content. by
|
||||
jacob314 in [#17940](https://github.com/google-gemini/gemini-cli/pull/17940)
|
||||
- Fix broken link in docs by chrstnb in
|
||||
[#17959](https://github.com/google-gemini/gemini-cli/pull/17959)
|
||||
- feat(plan): reuse standard tool confirmation for AskUser tool by jerop in
|
||||
[#17864](https://github.com/google-gemini/gemini-cli/pull/17864)
|
||||
- feat(core): enable overriding CODE_ASSIST_API_VERSION with env var by
|
||||
lottielin in [#17942](https://github.com/google-gemini/gemini-cli/pull/17942)
|
||||
- run npx pointing to the specific commit SHA by sehoon38 in
|
||||
[#17970](https://github.com/google-gemini/gemini-cli/pull/17970)
|
||||
- Add allowedExtensions setting by kevinjwang1 in
|
||||
[#17695](https://github.com/google-gemini/gemini-cli/pull/17695)
|
||||
- feat(plan): refactor ToolConfirmationPayload to union type by jerop in
|
||||
[#17980](https://github.com/google-gemini/gemini-cli/pull/17980)
|
||||
- lower the default max retries to reduce contention by sehoon38 in
|
||||
[#17975](https://github.com/google-gemini/gemini-cli/pull/17975)
|
||||
- fix(core): ensure YOLO mode auto-approves complex shell commands when parsing
|
||||
fails by abhipatel12 in
|
||||
[#17920](https://github.com/google-gemini/gemini-cli/pull/17920)
|
||||
- Fix broken link. by g-samroberts in
|
||||
[#17972](https://github.com/google-gemini/gemini-cli/pull/17972)
|
||||
- Support ctrl-C and Ctrl-D correctly Refactor so InputPrompt has priority over
|
||||
AppContainer for input handling. by jacob314 in
|
||||
[#17993](https://github.com/google-gemini/gemini-cli/pull/17993)
|
||||
- Fix truncation for AskQuestion by jacob314 in
|
||||
[#18001](https://github.com/google-gemini/gemini-cli/pull/18001)
|
||||
- fix(workflow): update maintainer check logic to be inclusive and
|
||||
case-insensitive by bdmorgan in
|
||||
[#18009](https://github.com/google-gemini/gemini-cli/pull/18009)
|
||||
- Fix Esc cancel during streaming by LyalinDotCom in
|
||||
[#18039](https://github.com/google-gemini/gemini-cli/pull/18039)
|
||||
- feat(acp): add session resume support by bdmorgan in
|
||||
[#18043](https://github.com/google-gemini/gemini-cli/pull/18043)
|
||||
- fix(ci): prevent stale PR closer from incorrectly closing new PRs by bdmorgan
|
||||
in [#18069](https://github.com/google-gemini/gemini-cli/pull/18069)
|
||||
- chore: delete autoAccept setting unused in production by victorvianna in
|
||||
[#17862](https://github.com/google-gemini/gemini-cli/pull/17862)
|
||||
- feat(plan): use placeholder for choice question "Other" option by jerop in
|
||||
[#18101](https://github.com/google-gemini/gemini-cli/pull/18101)
|
||||
- docs: update clearContext to hookSpecificOutput by jackwotherspoon in
|
||||
[#18024](https://github.com/google-gemini/gemini-cli/pull/18024)
|
||||
- docs-writer skill: Update docs writer skill by jkcinouye in
|
||||
[#17928](https://github.com/google-gemini/gemini-cli/pull/17928)
|
||||
- Sehoon/oncall filter by sehoon38 in
|
||||
[#18105](https://github.com/google-gemini/gemini-cli/pull/18105)
|
||||
- feat(core): add setting to disable loop detection by SandyTao520 in
|
||||
[#18008](https://github.com/google-gemini/gemini-cli/pull/18008)
|
||||
- Docs: Revise docs/index.md by jkcinouye in
|
||||
[#17879](https://github.com/google-gemini/gemini-cli/pull/17879)
|
||||
- Fix up/down arrow regression and add test. by jacob314 in
|
||||
[#18108](https://github.com/google-gemini/gemini-cli/pull/18108)
|
||||
- fix(ui): prevent content leak in MaxSizedBox bottom overflow by jerop in
|
||||
[#17991](https://github.com/google-gemini/gemini-cli/pull/17991)
|
||||
- refactor: migrate checks.ts utility to core and deduplicate by jerop in
|
||||
[#18139](https://github.com/google-gemini/gemini-cli/pull/18139)
|
||||
- feat(core): implement tool name aliasing for backward compatibility by
|
||||
SandyTao520 in
|
||||
[#17974](https://github.com/google-gemini/gemini-cli/pull/17974)
|
||||
- docs: fix help-wanted label spelling by pavan-sh in
|
||||
[#18114](https://github.com/google-gemini/gemini-cli/pull/18114)
|
||||
- feat(cli): implement automatic theme switching based on terminal background by
|
||||
Abhijit-2592 in
|
||||
[#17976](https://github.com/google-gemini/gemini-cli/pull/17976)
|
||||
- fix(ide): no-op refactoring that moves the connection logic to helper
|
||||
functions by skeshive in
|
||||
[#18118](https://github.com/google-gemini/gemini-cli/pull/18118)
|
||||
- feat: update review-frontend-and-fix slash command to review-and-fix by galz10
|
||||
in [#18146](https://github.com/google-gemini/gemini-cli/pull/18146)
|
||||
- fix: improve Ctrl+R reverse search by jackwotherspoon in
|
||||
[#18075](https://github.com/google-gemini/gemini-cli/pull/18075)
|
||||
- feat(plan): handle inconsistency in schedulers by Adib234 in
|
||||
[#17813](https://github.com/google-gemini/gemini-cli/pull/17813)
|
||||
- feat(plan): add core logic and exit_plan_mode tool definition by jerop in
|
||||
[#18110](https://github.com/google-gemini/gemini-cli/pull/18110)
|
||||
- feat(core): rename search_file_content tool to grep_search and add legacy
|
||||
alias by SandyTao520 in
|
||||
[#18003](https://github.com/google-gemini/gemini-cli/pull/18003)
|
||||
- fix(core): prioritize detailed error messages for code assist setup by
|
||||
gsquared94 in [#17852](https://github.com/google-gemini/gemini-cli/pull/17852)
|
||||
- fix(cli): resolve environment loading and auth validation issues in ACP mode
|
||||
by bdmorgan in
|
||||
[#18025](https://github.com/google-gemini/gemini-cli/pull/18025)
|
||||
- feat(core): add .agents/skills directory alias for skill discovery by
|
||||
NTaylorMullen in
|
||||
[#18151](https://github.com/google-gemini/gemini-cli/pull/18151)
|
||||
- chore(core): reassign telemetry keys to avoid server conflict by mattKorwel in
|
||||
[#18161](https://github.com/google-gemini/gemini-cli/pull/18161)
|
||||
- Add link to rewind doc in commands.md by Adib234 in
|
||||
[#17961](https://github.com/google-gemini/gemini-cli/pull/17961)
|
||||
- feat(core): add draft-2020-12 JSON Schema support with lenient fallback by
|
||||
afarber in [#15060](https://github.com/google-gemini/gemini-cli/pull/15060)
|
||||
- refactor(core): robust trimPreservingTrailingNewline and regression test by
|
||||
adamfweidman in
|
||||
[#18196](https://github.com/google-gemini/gemini-cli/pull/18196)
|
||||
- Remove MCP servers on extension uninstall by chrstnb in
|
||||
[#18121](https://github.com/google-gemini/gemini-cli/pull/18121)
|
||||
- refactor: localize ACP error parsing logic to cli package by bdmorgan in
|
||||
[#18193](https://github.com/google-gemini/gemini-cli/pull/18193)
|
||||
- feat(core): Add A2A auth config types by adamfweidman in
|
||||
[#18205](https://github.com/google-gemini/gemini-cli/pull/18205)
|
||||
- Set default max attempts to 3 and use the common variable by sehoon38 in
|
||||
[#18209](https://github.com/google-gemini/gemini-cli/pull/18209)
|
||||
- feat(plan): add exit_plan_mode ui and prompt by jerop in
|
||||
[#18162](https://github.com/google-gemini/gemini-cli/pull/18162)
|
||||
- fix(test): improve test isolation and enable subagent evaluations by
|
||||
cocosheng-g in
|
||||
[#18138](https://github.com/google-gemini/gemini-cli/pull/18138)
|
||||
- feat(plan): use custom deny messages in plan mode policies by Adib234 in
|
||||
[#18195](https://github.com/google-gemini/gemini-cli/pull/18195)
|
||||
- Match on extension ID when stopping extensions by chrstnb in
|
||||
[#18218](https://github.com/google-gemini/gemini-cli/pull/18218)
|
||||
- fix(core): Respect user's .gitignore preference by xyrolle in
|
||||
[#15482](https://github.com/google-gemini/gemini-cli/pull/15482)
|
||||
- docs: document GEMINI_CLI_HOME environment variable by adamfweidman in
|
||||
[#18219](https://github.com/google-gemini/gemini-cli/pull/18219)
|
||||
- chore(core): explicitly state plan storage path in prompt by jerop in
|
||||
[#18222](https://github.com/google-gemini/gemini-cli/pull/18222)
|
||||
- A2a admin setting by DavidAPierce in
|
||||
[#17868](https://github.com/google-gemini/gemini-cli/pull/17868)
|
||||
- feat(a2a): Add pluggable auth provider infrastructure by adamfweidman in
|
||||
[#17934](https://github.com/google-gemini/gemini-cli/pull/17934)
|
||||
- Fix handling of empty settings by chrstnb in
|
||||
[#18131](https://github.com/google-gemini/gemini-cli/pull/18131)
|
||||
- Reload skills when extensions change by chrstnb in
|
||||
[#18225](https://github.com/google-gemini/gemini-cli/pull/18225)
|
||||
- feat: Add markdown rendering to ask_user tool by jackwotherspoon in
|
||||
[#18211](https://github.com/google-gemini/gemini-cli/pull/18211)
|
||||
- Add telemetry to rewind by Adib234 in
|
||||
[#18122](https://github.com/google-gemini/gemini-cli/pull/18122)
|
||||
- feat(admin): add support for MCP configuration via admin controls (pt1) by
|
||||
skeshive in [#18223](https://github.com/google-gemini/gemini-cli/pull/18223)
|
||||
- feat(core): require user consent before MCP server OAuth by ehedlund in
|
||||
[#18132](https://github.com/google-gemini/gemini-cli/pull/18132)
|
||||
- fix(sandbox): propagate GOOGLE_GEMINI_BASE_URL&GOOGLE_VERTEX_BASE_URL env vars
|
||||
by skeshive in
|
||||
[#18231](https://github.com/google-gemini/gemini-cli/pull/18231)
|
||||
- feat(ui): move user identity display to header by sehoon38 in
|
||||
[#18216](https://github.com/google-gemini/gemini-cli/pull/18216)
|
||||
- fix: enforce folder trust for workspace settings, skills, and context by
|
||||
galz10 in [#17596](https://github.com/google-gemini/gemini-cli/pull/17596)
|
||||
|
||||
**Full changelog**:
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.25.0-preview.4...v0.26.0-preview.0
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.27.0-preview.8...v0.28.0-preview.0
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
# CLI cheatsheet
|
||||
|
||||
This page provides a reference for commonly used Gemini CLI commands, options,
|
||||
and parameters.
|
||||
|
||||
## CLI commands
|
||||
|
||||
| Command | Description | Example |
|
||||
| ---------------------------------- | ---------------------------------- | --------------------------------------------------- |
|
||||
| `gemini` | Start interactive REPL | `gemini` |
|
||||
| `gemini "query"` | Query non-interactively, then exit | `gemini "explain this project"` |
|
||||
| `gemini -p "query"` | Query via SDK, then exit | `gemini -p "explain this function"` |
|
||||
| `cat file \| gemini -p "query"` | Process piped content | `cat logs.txt \| gemini -p "explain"` |
|
||||
| `gemini -i "query"` | Execute and continue interactively | `gemini -i "What is the purpose of this project?"` |
|
||||
| `gemini -r "latest"` | Continue most recent session | `gemini -r "latest"` |
|
||||
| `gemini -r "latest" "query"` | Continue session with a new prompt | `gemini -r "latest" "Check for type errors"` |
|
||||
| `gemini -r "<session-id>" "query"` | Resume session by ID | `gemini -r "abc123" "Finish this PR"` |
|
||||
| `gemini update` | Update to latest version | `gemini update` |
|
||||
| `gemini extensions` | Manage extensions | See [Extensions Management](#extensions-management) |
|
||||
| `gemini mcp` | Configure MCP servers | See [MCP Server Management](#mcp-server-management) |
|
||||
|
||||
### Positional arguments
|
||||
|
||||
| Argument | Type | Description |
|
||||
| -------- | ----------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||
| `query` | string (variadic) | Positional prompt. Defaults to one-shot mode. Use `-i/--prompt-interactive` to execute and continue interactively. |
|
||||
|
||||
## CLI Options
|
||||
|
||||
| Option | Alias | Type | Default | Description |
|
||||
| -------------------------------- | ----- | ------- | --------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| `--debug` | `-d` | boolean | `false` | Run in debug mode with verbose logging |
|
||||
| `--version` | `-v` | - | - | Show CLI version number and exit |
|
||||
| `--help` | `-h` | - | - | Show help information |
|
||||
| `--model` | `-m` | string | `auto` | Model to use. See [Model Selection](#model-selection) for available values. |
|
||||
| `--prompt` | `-p` | string | - | Prompt text. Appended to stdin input if provided. **Deprecated:** Use positional arguments instead. |
|
||||
| `--prompt-interactive` | `-i` | string | - | Execute prompt and continue in interactive mode |
|
||||
| `--sandbox` | `-s` | boolean | `false` | Run in a sandboxed environment for safer execution |
|
||||
| `--approval-mode` | - | string | `default` | Approval mode for tool execution. Choices: `default`, `auto_edit`, `yolo` |
|
||||
| `--yolo` | `-y` | boolean | `false` | **Deprecated.** Auto-approve all actions. Use `--approval-mode=yolo` instead. |
|
||||
| `--experimental-acp` | - | boolean | - | Start in ACP (Agent Code Pilot) mode. **Experimental feature.** |
|
||||
| `--experimental-zed-integration` | - | boolean | - | Run in Zed editor integration mode. **Experimental feature.** |
|
||||
| `--allowed-mcp-server-names` | - | array | - | Allowed MCP server names (comma-separated or multiple flags) |
|
||||
| `--allowed-tools` | - | array | - | Tools that are allowed to run without confirmation (comma-separated or multiple flags) |
|
||||
| `--extensions` | `-e` | array | - | List of extensions to use. If not provided, all extensions are enabled (comma-separated or multiple flags) |
|
||||
| `--list-extensions` | `-l` | boolean | - | List all available extensions and exit |
|
||||
| `--resume` | `-r` | string | - | Resume a previous session. Use `"latest"` for most recent or index number (e.g. `--resume 5`) |
|
||||
| `--list-sessions` | - | boolean | - | List available sessions for the current project and exit |
|
||||
| `--delete-session` | - | string | - | Delete a session by index number (use `--list-sessions` to see available sessions) |
|
||||
| `--include-directories` | - | array | - | Additional directories to include in the workspace (comma-separated or multiple flags) |
|
||||
| `--screen-reader` | - | boolean | - | Enable screen reader mode for accessibility |
|
||||
| `--output-format` | `-o` | string | `text` | The format of the CLI output. Choices: `text`, `json`, `stream-json` |
|
||||
|
||||
## Model selection
|
||||
|
||||
The `--model` (or `-m`) flag allows you to specify which Gemini model to use.
|
||||
You can use either model aliases (user-friendly names) or concrete model names.
|
||||
|
||||
### Model aliases
|
||||
|
||||
These are convenient shortcuts that map to specific models:
|
||||
|
||||
| Alias | Resolves To | Description |
|
||||
| ------------ | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `auto` | `gemini-2.5-pro` or `gemini-3-pro-preview` | **Default.** Resolves to the preview model if preview features are enabled, otherwise resolves to the standard pro model. |
|
||||
| `pro` | `gemini-2.5-pro` or `gemini-3-pro-preview` | For complex reasoning tasks. Uses preview model if enabled. |
|
||||
| `flash` | `gemini-2.5-flash` | Fast, balanced model for most tasks. |
|
||||
| `flash-lite` | `gemini-2.5-flash-lite` | Fastest model for simple tasks. |
|
||||
|
||||
## Extensions management
|
||||
|
||||
| Command | Description | Example |
|
||||
| -------------------------------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------ |
|
||||
| `gemini extensions install <source>` | Install extension from Git URL or local path | `gemini extensions install https://github.com/user/my-extension` |
|
||||
| `gemini extensions install <source> --ref <ref>` | Install from specific branch/tag/commit | `gemini extensions install https://github.com/user/my-extension --ref develop` |
|
||||
| `gemini extensions install <source> --auto-update` | Install with auto-update enabled | `gemini extensions install https://github.com/user/my-extension --auto-update` |
|
||||
| `gemini extensions uninstall <name>` | Uninstall one or more extensions | `gemini extensions uninstall my-extension` |
|
||||
| `gemini extensions list` | List all installed extensions | `gemini extensions list` |
|
||||
| `gemini extensions update <name>` | Update a specific extension | `gemini extensions update my-extension` |
|
||||
| `gemini extensions update --all` | Update all extensions | `gemini extensions update --all` |
|
||||
| `gemini extensions enable <name>` | Enable an extension | `gemini extensions enable my-extension` |
|
||||
| `gemini extensions disable <name>` | Disable an extension | `gemini extensions disable my-extension` |
|
||||
| `gemini extensions link <path>` | Link local extension for development | `gemini extensions link /path/to/extension` |
|
||||
| `gemini extensions new <path>` | Create new extension from template | `gemini extensions new ./my-extension` |
|
||||
| `gemini extensions validate <path>` | Validate extension structure | `gemini extensions validate ./my-extension` |
|
||||
|
||||
See [Extensions Documentation](../extensions/index.md) for more details.
|
||||
|
||||
## MCP server management
|
||||
|
||||
| Command | Description | Example |
|
||||
| ------------------------------------------------------------- | ------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
||||
| `gemini mcp add <name> <command>` | Add stdio-based MCP server | `gemini mcp add github npx -y @modelcontextprotocol/server-github` |
|
||||
| `gemini mcp add <name> <url> --transport http` | Add HTTP-based MCP server | `gemini mcp add api-server http://localhost:3000 --transport http` |
|
||||
| `gemini mcp add <name> <command> --env KEY=value` | Add with environment variables | `gemini mcp add slack node server.js --env SLACK_TOKEN=xoxb-xxx` |
|
||||
| `gemini mcp add <name> <command> --scope user` | Add with user scope | `gemini mcp add db node db-server.js --scope user` |
|
||||
| `gemini mcp add <name> <command> --include-tools tool1,tool2` | Add with specific tools | `gemini mcp add github npx -y @modelcontextprotocol/server-github --include-tools list_repos,get_pr` |
|
||||
| `gemini mcp remove <name>` | Remove an MCP server | `gemini mcp remove github` |
|
||||
| `gemini mcp list` | List all configured MCP servers | `gemini mcp list` |
|
||||
|
||||
See [MCP Server Integration](../tools/mcp-server.md) for more details.
|
||||
|
||||
## Skills management
|
||||
|
||||
| Command | Description | Example |
|
||||
| -------------------------------- | ------------------------------------- | ------------------------------------------------- |
|
||||
| `gemini skills list` | List all discovered agent skills | `gemini skills list` |
|
||||
| `gemini skills install <source>` | Install skill from Git, path, or file | `gemini skills install https://github.com/u/repo` |
|
||||
| `gemini skills link <path>` | Link local agent skills via symlink | `gemini skills link /path/to/my-skills` |
|
||||
| `gemini skills uninstall <name>` | Uninstall an agent skill | `gemini skills uninstall my-skill` |
|
||||
| `gemini skills enable <name>` | Enable an agent skill | `gemini skills enable my-skill` |
|
||||
| `gemini skills disable <name>` | Disable an agent skill | `gemini skills disable my-skill` |
|
||||
| `gemini skills enable --all` | Enable all skills | `gemini skills enable --all` |
|
||||
| `gemini skills disable --all` | Disable all skills | `gemini skills disable --all` |
|
||||
|
||||
See [Agent Skills Documentation](./skills.md) for more details.
|
||||
+146
-87
@@ -10,6 +10,14 @@ Slash commands provide meta-level control over the CLI itself.
|
||||
|
||||
### Built-in Commands
|
||||
|
||||
- **`/about`**
|
||||
- **Description:** Show version info. Please share this information when
|
||||
filing issues.
|
||||
|
||||
- **`/auth`**
|
||||
- **Description:** Open a dialog that lets you change the authentication
|
||||
method.
|
||||
|
||||
- **`/bug`**
|
||||
- **Description:** File an issue about Gemini CLI. By default, the issue is
|
||||
filed within the GitHub repository for Gemini CLI. The string you enter
|
||||
@@ -22,10 +30,21 @@ Slash commands provide meta-level control over the CLI itself.
|
||||
conversation state interactively, or resuming a previous state from a later
|
||||
session.
|
||||
- **Sub-commands:**
|
||||
- **`save`**
|
||||
- **`delete <tag>`**
|
||||
- **Description:** Deletes a saved conversation checkpoint.
|
||||
- **`list`**
|
||||
- **Description:** Lists available tags for chat state resumption.
|
||||
- **Note:** This command only lists chats saved within the current
|
||||
project. Because chat history is project-scoped, chats saved in other
|
||||
project directories will not be displayed.
|
||||
- **`resume <tag>`**
|
||||
- **Description:** Resumes a conversation from a previous save.
|
||||
- **Note:** You can only resume chats that were saved within the current
|
||||
project. To resume a chat from a different project, you must run the
|
||||
Gemini CLI from that project's directory.
|
||||
- **`save <tag>`**
|
||||
- **Description:** Saves the current conversation history. You must add a
|
||||
`<tag>` for identifying the conversation state.
|
||||
- **Usage:** `/chat save <tag>`
|
||||
- **Details on checkpoint location:** The default locations for saved chat
|
||||
checkpoints are:
|
||||
- Linux/macOS: `~/.gemini/tmp/<project_hash>/`
|
||||
@@ -37,25 +56,11 @@ Slash commands provide meta-level control over the CLI itself.
|
||||
conversation states. For automatic checkpoints created before file
|
||||
modifications, see the
|
||||
[Checkpointing documentation](../cli/checkpointing.md).
|
||||
- **`resume`**
|
||||
- **Description:** Resumes a conversation from a previous save.
|
||||
- **Usage:** `/chat resume <tag>`
|
||||
- **Note:** You can only resume chats that were saved within the current
|
||||
project. To resume a chat from a different project, you must run the
|
||||
Gemini CLI from that project's directory.
|
||||
- **`list`**
|
||||
- **Description:** Lists available tags for chat state resumption.
|
||||
- **Note:** This command only lists chats saved within the current
|
||||
project. Because chat history is project-scoped, chats saved in other
|
||||
project directories will not be displayed.
|
||||
- **`delete`**
|
||||
- **Description:** Deletes a saved conversation checkpoint.
|
||||
- **Usage:** `/chat delete <tag>`
|
||||
- **`share`**
|
||||
- **`share [filename]`**
|
||||
- **Description** Writes the current conversation to a provided Markdown
|
||||
or JSON file.
|
||||
- **Usage** `/chat share file.md` or `/chat share file.json`. If no
|
||||
filename is provided, then the CLI will generate one.
|
||||
or JSON file. If no filename is provided, then the CLI will generate
|
||||
one.
|
||||
- **Usage** `/chat share file.md` or `/chat share file.json`.
|
||||
|
||||
- **`/clear`**
|
||||
- **Description:** Clear the terminal screen, including the visible session
|
||||
@@ -98,6 +103,9 @@ Slash commands provide meta-level control over the CLI itself.
|
||||
`--include-directories`.
|
||||
- **Usage:** `/directory show`
|
||||
|
||||
- **`/docs`**
|
||||
- **Description:** Open the Gemini CLI documentation in your browser.
|
||||
|
||||
- **`/editor`**
|
||||
- **Description:** Open a dialog for selecting supported editors.
|
||||
|
||||
@@ -105,34 +113,73 @@ Slash commands provide meta-level control over the CLI itself.
|
||||
- **Description:** Lists all active extensions in the current Gemini CLI
|
||||
session. See [Gemini CLI Extensions](../extensions/index.md).
|
||||
|
||||
- **`/help`** (or **`/?`**)
|
||||
- **`/help`**
|
||||
- **Description:** Display help information about Gemini CLI, including
|
||||
available commands and their usage.
|
||||
|
||||
- **`/shortcuts`**
|
||||
- **Description:** Toggle the shortcuts panel above the input.
|
||||
- **Shortcut:** Press `?` when the prompt is empty.
|
||||
|
||||
- **`/hooks`**
|
||||
- **Description:** Manage hooks, which allow you to intercept and customize
|
||||
Gemini CLI behavior at specific lifecycle events.
|
||||
- **Sub-commands:**
|
||||
- **`disable-all`**:
|
||||
- **Description:** Disable all enabled hooks.
|
||||
- **`disable <hook-name>`**:
|
||||
- **Description:** Disable a hook by name.
|
||||
- **`enable-all`**:
|
||||
- **Description:** Enable all disabled hooks.
|
||||
- **`enable <hook-name>`**:
|
||||
- **Description:** Enable a hook by name.
|
||||
- **`list`** (or `show`, `panel`):
|
||||
- **Description:** Display all registered hooks with their status.
|
||||
|
||||
- **`/ide`**
|
||||
- **Description:** Manage IDE integration.
|
||||
- **Sub-commands:**
|
||||
- **`disable`**:
|
||||
- **Description:** Disable IDE integration.
|
||||
- **`enable`**:
|
||||
- **Description:** Enable IDE integration.
|
||||
- **`install`**:
|
||||
- **Description:** Install required IDE companion.
|
||||
- **`status`**:
|
||||
- **Description:** Check status of IDE integration.
|
||||
|
||||
- **`/init`**
|
||||
- **Description:** To help users easily create a `GEMINI.md` file, this
|
||||
command analyzes the current directory and generates a tailored context
|
||||
file, making it simpler for them to provide project-specific instructions to
|
||||
the Gemini agent.
|
||||
|
||||
- **`/introspect`**
|
||||
- **Description:** Provide debugging information about the current Gemini CLI
|
||||
session, including the state of loaded sub-agents and active hooks. This
|
||||
command is primarily for advanced users and developers.
|
||||
|
||||
- **`/mcp`**
|
||||
- **Description:** Manage configured Model Context Protocol (MCP) servers.
|
||||
- **Sub-commands:**
|
||||
- **`list`** or **`ls`**:
|
||||
- **Description:** List configured MCP servers and tools. This is the
|
||||
default action if no subcommand is specified.
|
||||
- **`desc`**
|
||||
- **Description:** List configured MCP servers and tools with
|
||||
descriptions.
|
||||
- **`schema`**:
|
||||
- **Description:** List configured MCP servers and tools with descriptions
|
||||
and schemas.
|
||||
- **`auth`**:
|
||||
- **Description:** Authenticate with an OAuth-enabled MCP server.
|
||||
- **Usage:** `/mcp auth <server-name>`
|
||||
- **Details:** If `<server-name>` is provided, it initiates the OAuth flow
|
||||
for that server. If no server name is provided, it lists all configured
|
||||
servers that support OAuth authentication.
|
||||
- **`desc`**
|
||||
- **Description:** List configured MCP servers and tools with
|
||||
descriptions.
|
||||
- **`list`** or **`ls`**:
|
||||
- **Description:** List configured MCP servers and tools. This is the
|
||||
default action if no subcommand is specified.
|
||||
- **`refresh`**:
|
||||
- **Description:** Restarts all MCP servers and re-discovers their
|
||||
available tools.
|
||||
|
||||
- [**`/model`**](./model.md)
|
||||
- **Description:** Opens a dialog to choose your Gemini model.
|
||||
- **`schema`**:
|
||||
- **Description:** List configured MCP servers and tools with descriptions
|
||||
and schemas.
|
||||
|
||||
- **`/memory`**
|
||||
- **Description:** Manage the AI's instructional context (hierarchical memory
|
||||
@@ -141,23 +188,40 @@ Slash commands provide meta-level control over the CLI itself.
|
||||
- **`add`**:
|
||||
- **Description:** Adds the following text to the AI's memory. Usage:
|
||||
`/memory add <text to remember>`
|
||||
- **`show`**:
|
||||
- **Description:** Display the full, concatenated content of the current
|
||||
hierarchical memory that has been loaded from all `GEMINI.md` files.
|
||||
This lets you inspect the instructional context being provided to the
|
||||
Gemini model.
|
||||
- **`list`**:
|
||||
- **Description:** Lists the paths of the GEMINI.md files in use for
|
||||
hierarchical memory.
|
||||
- **`refresh`**:
|
||||
- **Description:** Reload the hierarchical instructional memory from all
|
||||
`GEMINI.md` files found in the configured locations (global,
|
||||
project/ancestors, and sub-directories). This command updates the model
|
||||
with the latest `GEMINI.md` content.
|
||||
- **`list`**:
|
||||
- **Description:** Lists the paths of the GEMINI.md files in use for
|
||||
hierarchical memory.
|
||||
- **`show`**:
|
||||
- **Description:** Display the full, concatenated content of the current
|
||||
hierarchical memory that has been loaded from all `GEMINI.md` files.
|
||||
This lets you inspect the instructional context being provided to the
|
||||
Gemini model.
|
||||
- **Note:** For more details on how `GEMINI.md` files contribute to
|
||||
hierarchical memory, see the
|
||||
[CLI Configuration documentation](../get-started/configuration.md).
|
||||
|
||||
- [**`/model`**](./model.md)
|
||||
- **Description:** Opens a dialog to choose your Gemini model.
|
||||
|
||||
- **`/policies`**
|
||||
- **Description:** Manage policies.
|
||||
- **Sub-commands:**
|
||||
- **`list`**:
|
||||
- **Description:** List all active policies grouped by mode.
|
||||
|
||||
- **`/privacy`**
|
||||
- **Description:** Display the Privacy Notice and allow users to select
|
||||
whether they consent to the collection of their data for service improvement
|
||||
purposes.
|
||||
|
||||
- **`/quit`** (or **`/exit`**)
|
||||
- **Description:** Exit Gemini CLI.
|
||||
|
||||
- **`/restore`**
|
||||
- **Description:** Restores the project files to the state they were in just
|
||||
before a tool was executed. This is particularly useful for undoing file
|
||||
@@ -168,18 +232,24 @@ Slash commands provide meta-level control over the CLI itself.
|
||||
[settings](../get-started/configuration.md). See
|
||||
[Checkpointing documentation](../cli/checkpointing.md) for more details.
|
||||
|
||||
- [**`/rewind`**](./rewind.md)
|
||||
- **Description:** Navigates backward through the conversation history,
|
||||
allowing you to review past interactions and potentially revert to a
|
||||
previous state. This feature helps in managing complex or branched
|
||||
conversations.
|
||||
|
||||
- **`/resume`**
|
||||
- **Description:** Browse and resume previous conversation sessions. Opens an
|
||||
interactive session browser where you can search, filter, and select from
|
||||
automatically saved conversations.
|
||||
- **Features:**
|
||||
- **Session Browser:** Interactive interface showing all saved sessions with
|
||||
timestamps, message counts, and first user message for context
|
||||
- **Search:** Use `/` to search through conversation content across all
|
||||
sessions
|
||||
- **Sorting:** Sort sessions by date or message count
|
||||
- **Management:** Delete unwanted sessions directly from the browser
|
||||
- **Resume:** Select any session to resume and continue the conversation
|
||||
- **Search:** Use `/` to search through conversation content across all
|
||||
sessions
|
||||
- **Session Browser:** Interactive interface showing all saved sessions with
|
||||
timestamps, message counts, and first user message for context
|
||||
- **Sorting:** Sort sessions by date or message count
|
||||
- **Note:** All conversations are automatically saved as you chat - no manual
|
||||
saving required. See [Session Management](../cli/session-management.md) for
|
||||
complete details.
|
||||
@@ -198,19 +268,26 @@ Slash commands provide meta-level control over the CLI itself.
|
||||
modify them as desired. Changes to some settings are applied immediately,
|
||||
while others require a restart.
|
||||
|
||||
- **`/shells`** (or **`/bashes`**)
|
||||
- **Description:** Toggle the background shells view. This allows you to view
|
||||
and manage long-running processes that you've sent to the background.
|
||||
- **`/setup-github`**
|
||||
- **Description:** Set up GitHub Actions to triage issues and review PRs with
|
||||
Gemini.
|
||||
|
||||
- [**`/skills`**](./skills.md)
|
||||
- **Description:** (Experimental) Manage Agent Skills, which provide on-demand
|
||||
expertise and specialized workflows.
|
||||
- **Description:** Manage Agent Skills, which provide on-demand expertise and
|
||||
specialized workflows.
|
||||
- **Sub-commands:**
|
||||
- **`disable <name>`**:
|
||||
- **Description:** Disable a specific skill by name.
|
||||
- **Usage:** `/skills disable <name>`
|
||||
- **`enable <name>`**:
|
||||
- **Description:** Enable a specific skill by name.
|
||||
- **Usage:** `/skills enable <name>`
|
||||
- **`list`**:
|
||||
- **Description:** List all discovered skills and their current status
|
||||
(enabled/disabled).
|
||||
- **`enable`**:
|
||||
- **Description:** Enable a specific skill by name.
|
||||
- **Usage:** `/skills enable <name>`
|
||||
- **`disable`**:
|
||||
- **Description:** Disable a specific skill by name.
|
||||
- **Usage:** `/skills disable <name>`
|
||||
- **`reload`**:
|
||||
- **Description:** Refresh the list of discovered skills from all tiers
|
||||
(workspace, user, and extensions).
|
||||
@@ -222,18 +299,14 @@ Slash commands provide meta-level control over the CLI itself.
|
||||
cached tokens are being used, which occurs with API key authentication but
|
||||
not with OAuth authentication at this time.
|
||||
|
||||
- **`/terminal-setup`**
|
||||
- **Description:** Configure terminal keybindings for multiline input (VS
|
||||
Code, Cursor, Windsurf).
|
||||
|
||||
- [**`/theme`**](./themes.md)
|
||||
- **Description:** Open a dialog that lets you change the visual theme of
|
||||
Gemini CLI.
|
||||
|
||||
- **`/auth`**
|
||||
- **Description:** Open a dialog that lets you change the authentication
|
||||
method.
|
||||
|
||||
- **`/about`**
|
||||
- **Description:** Show version info. Please share this information when
|
||||
filing issues.
|
||||
|
||||
- [**`/tools`**](../tools/index.md)
|
||||
- **Description:** Display a list of tools that are currently available within
|
||||
Gemini CLI.
|
||||
@@ -245,37 +318,23 @@ Slash commands provide meta-level control over the CLI itself.
|
||||
- **`nodesc`** or **`nodescriptions`**:
|
||||
- **Description:** Hide tool descriptions, showing only the tool names.
|
||||
|
||||
- **`/privacy`**
|
||||
- **Description:** Display the Privacy Notice and allow users to select
|
||||
whether they consent to the collection of their data for service improvement
|
||||
purposes.
|
||||
|
||||
- **`/quit`** (or **`/exit`**)
|
||||
- **Description:** Exit Gemini CLI.
|
||||
|
||||
- **`/vim`**
|
||||
- **Description:** Toggle vim mode on or off. When vim mode is enabled, the
|
||||
input area supports vim-style navigation and editing commands in both NORMAL
|
||||
and INSERT modes.
|
||||
- **Features:**
|
||||
- **Count support:** Prefix commands with numbers (e.g., `3h`, `5w`, `10G`)
|
||||
- **Editing commands:** Delete with `x`, change with `c`, insert with `i`,
|
||||
`a`, `o`, `O`; complex operations like `dd`, `cc`, `dw`, `cw`
|
||||
- **INSERT mode:** Standard text input with escape to return to NORMAL mode
|
||||
- **NORMAL mode:** Navigate with `h`, `j`, `k`, `l`; jump by words with `w`,
|
||||
`b`, `e`; go to line start/end with `0`, `$`, `^`; go to specific lines
|
||||
with `G` (or `gg` for first line)
|
||||
- **INSERT mode:** Standard text input with escape to return to NORMAL mode
|
||||
- **Editing commands:** Delete with `x`, change with `c`, insert with `i`,
|
||||
`a`, `o`, `O`; complex operations like `dd`, `cc`, `dw`, `cw`
|
||||
- **Count support:** Prefix commands with numbers (e.g., `3h`, `5w`, `10G`)
|
||||
- **Repeat last command:** Use `.` to repeat the last editing operation
|
||||
- **Persistent setting:** Vim mode preference is saved to
|
||||
`~/.gemini/settings.json` and restored between sessions
|
||||
- **Status indicator:** When enabled, shows `[NORMAL]` or `[INSERT]` in the
|
||||
footer
|
||||
|
||||
- **`/init`**
|
||||
- **Description:** To help users easily create a `GEMINI.md` file, this
|
||||
command analyzes the current directory and generates a tailored context
|
||||
file, making it simpler for them to provide project-specific instructions to
|
||||
the Gemini agent.
|
||||
- **Repeat last command:** Use `.` to repeat the last editing operation
|
||||
- **Status indicator:** When enabled, shows `[NORMAL]` or `[INSERT]` in the
|
||||
footer
|
||||
|
||||
### Custom commands
|
||||
|
||||
@@ -288,12 +347,12 @@ please see the dedicated [Custom Commands documentation](./custom-commands.md).
|
||||
These shortcuts apply directly to the input prompt for text manipulation.
|
||||
|
||||
- **Undo:**
|
||||
- **Keyboard shortcut:** Press **Ctrl+z** to undo the last action in the input
|
||||
prompt.
|
||||
- **Keyboard shortcut:** Press **Alt+z** or **Cmd+z** to undo the last action
|
||||
in the input prompt.
|
||||
|
||||
- **Redo:**
|
||||
- **Keyboard shortcut:** Press **Ctrl+Shift+Z** to redo the last undone action
|
||||
in the input prompt.
|
||||
- **Keyboard shortcut:** Press **Shift+Alt+Z** or **Shift+Cmd+Z** to redo the
|
||||
last undone action in the input prompt.
|
||||
|
||||
## At commands (`@`)
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
# Creating Agent Skills
|
||||
|
||||
This guide provides an overview of how to create your own Agent Skills to extend
|
||||
the capabilities of Gemini CLI.
|
||||
|
||||
## Getting started: The `skill-creator` skill
|
||||
|
||||
The recommended way to create a new skill is to use the built-in `skill-creator`
|
||||
skill. To use it, ask Gemini CLI to create a new skill for you.
|
||||
|
||||
**Example prompt:**
|
||||
|
||||
> "create a new skill called 'code-reviewer'"
|
||||
|
||||
Gemini CLI will then use the `skill-creator` to generate the skill:
|
||||
|
||||
1. Generate a new directory for your skill (e.g., `my-new-skill/`).
|
||||
2. Create a `SKILL.md` file with the necessary YAML frontmatter (`name` and
|
||||
`description`).
|
||||
3. Create the standard resource directories: `scripts/`, `references/`, and
|
||||
`assets/`.
|
||||
|
||||
## Manual skill creation
|
||||
|
||||
If you prefer to create skills manually:
|
||||
|
||||
1. **Create a directory** for your skill (e.g., `my-new-skill/`).
|
||||
2. **Create a `SKILL.md` file** inside the new directory.
|
||||
|
||||
To add additional resources that support the skill, refer to the skill
|
||||
structure.
|
||||
|
||||
## Skill structure
|
||||
|
||||
A skill is a directory containing a `SKILL.md` file at its root.
|
||||
|
||||
### Folder structure
|
||||
|
||||
While a `SKILL.md` file is the only required component, we recommend the
|
||||
following structure for organizing your skill's resources:
|
||||
|
||||
```text
|
||||
my-skill/
|
||||
├── SKILL.md (Required) Instructions and metadata
|
||||
├── scripts/ (Optional) Executable scripts
|
||||
├── references/ (Optional) Static documentation
|
||||
└── assets/ (Optional) Templates and other resources
|
||||
```
|
||||
|
||||
### `SKILL.md` file
|
||||
|
||||
The `SKILL.md` file is the core of your skill. This file uses YAML frontmatter
|
||||
for metadata and Markdown for instructions. For example:
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: code-reviewer
|
||||
description:
|
||||
Use this skill to review code. It supports both local changes and remote Pull
|
||||
Requests.
|
||||
---
|
||||
|
||||
# Code Reviewer
|
||||
|
||||
This skill guides the agent in conducting thorough code reviews.
|
||||
|
||||
## Workflow
|
||||
|
||||
### 1. Determine Review Target
|
||||
|
||||
- **Remote PR**: If the user gives a PR number or URL, target that remote PR.
|
||||
- **Local Changes**: If changes are local... ...
|
||||
```
|
||||
|
||||
- **`name`**: A unique identifier for the skill. This should match the directory
|
||||
name.
|
||||
- **`description`**: A description of what the skill does and when Gemini should
|
||||
use it.
|
||||
- **Body**: The Markdown body of the file contains the instructions that guide
|
||||
the agent's behavior when the skill is active.
|
||||
@@ -203,6 +203,23 @@ with the actual Gemini CLI process, which inherits the environment variable.
|
||||
This makes it significantly more difficult for a user to bypass the enforced
|
||||
settings.
|
||||
|
||||
## User isolation in shared environments
|
||||
|
||||
In shared compute environments (like ML experiment runners or shared build
|
||||
servers), you can isolate Gemini CLI state by overriding the user's home
|
||||
directory.
|
||||
|
||||
By default, Gemini CLI stores configuration and history in `~/.gemini`. You can
|
||||
use the `GEMINI_CLI_HOME` environment variable to point to a unique directory
|
||||
for a specific user or job. The CLI will create a `.gemini` folder inside the
|
||||
specified path.
|
||||
|
||||
```bash
|
||||
# Isolate state for a specific job
|
||||
export GEMINI_CLI_HOME="/tmp/gemini-job-123"
|
||||
gemini
|
||||
```
|
||||
|
||||
## Restricting tool access
|
||||
|
||||
You can significantly enhance security by controlling which tools the Gemini
|
||||
|
||||
+4
-2
@@ -23,14 +23,16 @@ overview of Gemini CLI, see the [main documentation page](../index.md).
|
||||
|
||||
## Advanced features
|
||||
|
||||
- **[Plan mode (experimental)](./plan-mode.md):** Use a safe, read-only mode for
|
||||
planning complex changes.
|
||||
- **[Checkpointing](./checkpointing.md):** Automatically save and restore
|
||||
snapshots of your session and files.
|
||||
- **[Enterprise configuration](./enterprise.md):** Deploy and manage Gemini CLI
|
||||
in an enterprise environment.
|
||||
- **[Sandboxing](./sandbox.md):** Isolate tool execution in a secure,
|
||||
containerized environment.
|
||||
- **[Agent Skills](./skills.md):** (Experimental) Extend the CLI with
|
||||
specialized expertise and procedural workflows.
|
||||
- **[Agent Skills](./skills.md):** Extend the CLI with specialized expertise and
|
||||
procedural workflows.
|
||||
- **[Telemetry](./telemetry.md):** Configure observability to monitor usage and
|
||||
performance.
|
||||
- **[Token caching](./token-caching.md):** Optimize API costs by caching tokens.
|
||||
|
||||
@@ -23,24 +23,24 @@ available combinations.
|
||||
| Move the cursor to the end of the line. | `Ctrl + E`<br />`End (no Shift, Ctrl)` |
|
||||
| Move the cursor up one line. | `Up Arrow (no Shift, Alt, Ctrl, Cmd)` |
|
||||
| Move the cursor down one line. | `Down Arrow (no Shift, Alt, Ctrl, Cmd)` |
|
||||
| Move the cursor one character to the left. | `Left Arrow (no Shift, Alt, Ctrl, Cmd)`<br />`Ctrl + B` |
|
||||
| Move the cursor one character to the left. | `Left Arrow (no Shift, Alt, Ctrl, Cmd)` |
|
||||
| Move the cursor one character to the right. | `Right Arrow (no Shift, Alt, Ctrl, Cmd)`<br />`Ctrl + F` |
|
||||
| Move the cursor one word to the left. | `Ctrl + Left Arrow`<br />`Alt + Left Arrow`<br />`Alt + B` |
|
||||
| Move the cursor one word to the right. | `Ctrl + Right Arrow`<br />`Alt + Right Arrow`<br />`Alt + F` |
|
||||
|
||||
#### Editing
|
||||
|
||||
| Action | Keys |
|
||||
| ------------------------------------------------ | --------------------------------------------------------- |
|
||||
| Delete from the cursor to the end of the line. | `Ctrl + K` |
|
||||
| Delete from the cursor to the start of the line. | `Ctrl + U` |
|
||||
| Clear all text in the input field. | `Ctrl + C` |
|
||||
| Delete the previous word. | `Ctrl + Backspace`<br />`Alt + Backspace`<br />`Ctrl + W` |
|
||||
| Delete the next word. | `Ctrl + Delete`<br />`Alt + Delete` |
|
||||
| Delete the character to the left. | `Backspace`<br />`Ctrl + H` |
|
||||
| Delete the character to the right. | `Delete`<br />`Ctrl + D` |
|
||||
| Undo the most recent text edit. | `Ctrl + Z (no Shift)` |
|
||||
| Redo the most recent undone text edit. | `Shift + Ctrl + Z` |
|
||||
| Action | Keys |
|
||||
| ------------------------------------------------ | ---------------------------------------------------------------- |
|
||||
| Delete from the cursor to the end of the line. | `Ctrl + K` |
|
||||
| Delete from the cursor to the start of the line. | `Ctrl + U` |
|
||||
| Clear all text in the input field. | `Ctrl + C` |
|
||||
| Delete the previous word. | `Ctrl + Backspace`<br />`Alt + Backspace`<br />`Ctrl + W` |
|
||||
| Delete the next word. | `Ctrl + Delete`<br />`Alt + Delete` |
|
||||
| Delete the character to the left. | `Backspace`<br />`Ctrl + H` |
|
||||
| Delete the character to the right. | `Delete`<br />`Ctrl + D` |
|
||||
| Undo the most recent text edit. | `Cmd + Z (no Shift)`<br />`Alt + Z (no Shift)` |
|
||||
| Redo the most recent undone text edit. | `Shift + Ctrl + Z`<br />`Shift + Cmd + Z`<br />`Shift + Alt + Z` |
|
||||
|
||||
#### Scrolling
|
||||
|
||||
@@ -66,12 +66,14 @@ available combinations.
|
||||
|
||||
#### Navigation
|
||||
|
||||
| Action | Keys |
|
||||
| -------------------------------- | ------------------------------------------- |
|
||||
| Move selection up in lists. | `Up Arrow (no Shift)` |
|
||||
| Move selection down in lists. | `Down Arrow (no Shift)` |
|
||||
| Move up within dialog options. | `Up Arrow (no Shift)`<br />`K (no Shift)` |
|
||||
| Move down within dialog options. | `Down Arrow (no Shift)`<br />`J (no Shift)` |
|
||||
| Action | Keys |
|
||||
| -------------------------------------------------- | ------------------------------------------- |
|
||||
| Move selection up in lists. | `Up Arrow (no Shift)` |
|
||||
| Move selection down in lists. | `Down Arrow (no Shift)` |
|
||||
| Move up within dialog options. | `Up Arrow (no Shift)`<br />`K (no Shift)` |
|
||||
| Move down within dialog options. | `Down Arrow (no Shift)`<br />`J (no Shift)` |
|
||||
| Move to the next item or question in a dialog. | `Tab (no Shift)` |
|
||||
| Move to the previous item or question in a dialog. | `Shift + Tab` |
|
||||
|
||||
#### Suggestions & Completions
|
||||
|
||||
@@ -104,10 +106,21 @@ available combinations.
|
||||
| Toggle YOLO (auto-approval) mode for tool calls. | `Ctrl + Y` |
|
||||
| Cycle through approval modes: default (prompt), auto_edit (auto-approve edits), and plan (read-only). | `Shift + Tab` |
|
||||
| Expand a height-constrained response to show additional lines when not in alternate buffer mode. | `Ctrl + O`<br />`Ctrl + S` |
|
||||
| Focus the shell input from the gemini input. | `Tab (no Shift)` |
|
||||
| Focus the Gemini input from the shell input. | `Tab` |
|
||||
| Expand or collapse a paste placeholder when cursor is over placeholder. | `Ctrl + O` |
|
||||
| Toggle current background shell visibility. | `Ctrl + B` |
|
||||
| Toggle background shell list. | `Ctrl + L` |
|
||||
| Kill the active background shell. | `Ctrl + K` |
|
||||
| Confirm selection in background shell list. | `Enter` |
|
||||
| Dismiss background shell list. | `Esc` |
|
||||
| Move focus from background shell to Gemini. | `Shift + Tab` |
|
||||
| Move focus from background shell list to Gemini. | `Tab (no Shift)` |
|
||||
| Show warning when trying to unfocus background shell via Tab. | `Tab (no Shift)` |
|
||||
| Show warning when trying to unfocus shell input via Tab. | `Tab (no Shift)` |
|
||||
| Move focus from Gemini to the active shell. | `Tab (no Shift)` |
|
||||
| Move focus from the shell back to Gemini. | `Shift + Tab` |
|
||||
| Clear the terminal screen and redraw the UI. | `Ctrl + L` |
|
||||
| Restart the application. | `R` |
|
||||
| Suspend the application (not yet implemented). | `Ctrl + Z` |
|
||||
|
||||
<!-- KEYBINDINGS-AUTOGEN:END -->
|
||||
|
||||
@@ -116,6 +129,9 @@ available combinations.
|
||||
- `Option+B/F/M` (macOS only): Are interpreted as `Cmd+B/F/M` even if your
|
||||
terminal isn't configured to send Meta with Option.
|
||||
- `!` on an empty prompt: Enter or exit shell mode.
|
||||
- `?` on an empty prompt: Toggle the shortcuts panel above the input. Press
|
||||
`Esc`, `Backspace`, or any printable key to close it. Press `?` again to close
|
||||
the panel and insert a `?` into the prompt.
|
||||
- `\` (at end of a line) + `Enter`: Insert a newline without leaving single-line
|
||||
mode.
|
||||
- `Esc` pressed twice quickly: Clear the input prompt if it is not empty,
|
||||
@@ -124,6 +140,7 @@ available combinations.
|
||||
single-line input, navigate backward or forward through prompt history.
|
||||
- `Number keys (1-9, multi-digit)` inside selection dialogs: Jump directly to
|
||||
the numbered radio option and confirm when the full number is entered.
|
||||
- `Double-click` on a paste placeholder (`[Pasted Text: X lines]`) in alternate
|
||||
buffer mode: Expand to view full content inline. Double-click again to
|
||||
collapse.
|
||||
- `Ctrl + O`: Expand or collapse paste placeholders (`[Pasted Text: X lines]`)
|
||||
inline when the cursor is over the placeholder.
|
||||
- `Double-click` on a paste placeholder (alternate buffer mode only): Expand to
|
||||
view full content inline. Double-click again to collapse.
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
# Plan Mode (experimental)
|
||||
|
||||
Plan Mode is a safe, read-only mode for researching and designing complex
|
||||
changes. It prevents modifications while you research, design and plan an
|
||||
implementation strategy.
|
||||
|
||||
> **Note: Plan Mode is currently an experimental feature.**
|
||||
>
|
||||
> Experimental features are subject to change. To use Plan Mode, enable it via
|
||||
> `/settings` (search for `Plan`) or add the following to your `settings.json`:
|
||||
>
|
||||
> ```json
|
||||
> {
|
||||
> "experimental": {
|
||||
> "plan": true
|
||||
> }
|
||||
> }
|
||||
> ```
|
||||
>
|
||||
> Your feedback is invaluable as we refine this feature. If you have ideas,
|
||||
> suggestions, or encounter issues:
|
||||
>
|
||||
> - Use the `/bug` command within the CLI to file an issue.
|
||||
> - [Open an issue](https://github.com/google-gemini/gemini-cli/issues) on
|
||||
> GitHub.
|
||||
|
||||
- [Starting in Plan Mode](#starting-in-plan-mode)
|
||||
- [How to use Plan Mode](#how-to-use-plan-mode)
|
||||
- [Entering Plan Mode](#entering-plan-mode)
|
||||
- [The Planning Workflow](#the-planning-workflow)
|
||||
- [Exiting Plan Mode](#exiting-plan-mode)
|
||||
- [Tool Restrictions](#tool-restrictions)
|
||||
|
||||
## Starting in Plan Mode
|
||||
|
||||
You can configure Gemini CLI to start directly in Plan Mode by default:
|
||||
|
||||
1. Type `/settings` in the CLI.
|
||||
2. Search for `Default Approval Mode`.
|
||||
3. Set the value to `Plan`.
|
||||
|
||||
Other ways to start in Plan Mode:
|
||||
|
||||
- **CLI Flag:** `gemini --approval-mode=plan`
|
||||
- **Manual Settings:** Manually update your `settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"general": {
|
||||
"defaultApprovalMode": "plan"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## How to use Plan Mode
|
||||
|
||||
### Entering Plan Mode
|
||||
|
||||
You can enter Plan Mode in three ways:
|
||||
|
||||
1. **Keyboard Shortcut:** Press `Shift+Tab` to cycle through approval modes
|
||||
(`Default` -> `Plan` -> `Auto-Edit`).
|
||||
2. **Command:** Type `/plan` in the input box.
|
||||
3. **Natural Language:** Ask the agent to "start a plan for...".
|
||||
|
||||
### The Planning Workflow
|
||||
|
||||
1. **Requirements:** The agent clarifies goals using `ask_user`.
|
||||
2. **Exploration:** The agent uses read-only tools (like [`read_file`]) to map
|
||||
the codebase and validate assumptions.
|
||||
3. **Planning:** A detailed plan is written to a temporary Markdown file.
|
||||
4. **Review:** You review the plan.
|
||||
- **Approve:** Exit Plan Mode and start implementation (switching to
|
||||
Auto-Edit or Default approval mode).
|
||||
- **Iterate:** Provide feedback to refine the plan.
|
||||
|
||||
### Exiting Plan Mode
|
||||
|
||||
To exit Plan Mode:
|
||||
|
||||
1. **Keyboard Shortcut:** Press `Shift+Tab` to cycle to the desired mode.
|
||||
1. **Tool:** The agent calls the `exit_plan_mode` tool to present the finalized
|
||||
plan for your approval.
|
||||
|
||||
## Tool Restrictions
|
||||
|
||||
Plan Mode enforces strict safety policies to prevent accidental changes.
|
||||
|
||||
These are the only allowed tools:
|
||||
|
||||
- **FileSystem (Read):** [`read_file`], [`list_directory`], [`glob`]
|
||||
- **Search:** [`grep_search`], [`google_web_search`]
|
||||
- **Interaction:** `ask_user`
|
||||
- **MCP Tools (Read):** Read-only [MCP tools] (e.g., `github_read_issue`,
|
||||
`postgres_read_schema`) are allowed.
|
||||
- **Planning (Write):** [`write_file`] and [`replace`] ONLY allowed for `.md`
|
||||
files in the `~/.gemini/tmp/<project>/plans/` directory.
|
||||
|
||||
[`list_directory`]: /docs/tools/file-system.md#1-list_directory-readfolder
|
||||
[`read_file`]: /docs/tools/file-system.md#2-read_file-readfile
|
||||
[`grep_search`]: /docs/tools/file-system.md#5-grep_search-searchtext
|
||||
[`write_file`]: /docs/tools/file-system.md#3-write_file-writefile
|
||||
[`glob`]: /docs/tools/file-system.md#4-glob-findfiles
|
||||
[`google_web_search`]: /docs/tools/web-search.md
|
||||
[`replace`]: /docs/tools/file-system.md#6-replace-edit
|
||||
[MCP tools]: /docs/tools/mcp-server.md
|
||||
@@ -0,0 +1,51 @@
|
||||
# Rewind
|
||||
|
||||
The `/rewind` command allows you to go back to a previous state in your
|
||||
conversation and, optionally, revert any file changes made by the AI during
|
||||
those interactions. This is a powerful tool for undoing mistakes, exploring
|
||||
different approaches, or simply cleaning up your session history.
|
||||
|
||||
## Usage
|
||||
|
||||
To use the rewind feature, simply type `/rewind` into the input prompt and press
|
||||
**Enter**.
|
||||
|
||||
Alternatively, you can use the keyboard shortcut: **Press `Esc` twice**.
|
||||
|
||||
## Interface
|
||||
|
||||
When you trigger a rewind, an interactive list of your previous interactions
|
||||
appears.
|
||||
|
||||
1. **Select Interaction:** Use the **Up/Down arrow keys** to navigate through
|
||||
the list. The most recent interactions are at the bottom.
|
||||
2. **Preview:** As you select an interaction, you'll see a preview of the user
|
||||
prompt and, if applicable, the number of files changed during that step.
|
||||
3. **Confirm Selection:** Press **Enter** on the interaction you want to rewind
|
||||
back to.
|
||||
4. **Action Selection:** After selecting an interaction, you'll be presented
|
||||
with a confirmation dialog with up to three options:
|
||||
- **Rewind conversation and revert code changes:** Reverts both the chat
|
||||
history and the file modifications to the state before the selected
|
||||
interaction.
|
||||
- **Rewind conversation:** Only reverts the chat history. File changes are
|
||||
kept.
|
||||
- **Revert code changes:** Only reverts the file modifications. The chat
|
||||
history is kept.
|
||||
- **Do nothing (esc):** Cancels the rewind operation.
|
||||
|
||||
If no code changes were made since the selected point, the options related to
|
||||
reverting code changes will be hidden.
|
||||
|
||||
## Key Considerations
|
||||
|
||||
- **Destructive Action:** Rewinding is a destructive action for your current
|
||||
session history and potentially your files. Use it with care.
|
||||
- **Agent Awareness:** When you rewind the conversation, the AI model loses all
|
||||
memory of the interactions that were removed. If you only revert code changes,
|
||||
you may need to inform the model that the files have changed.
|
||||
- **Manual Edits:** Rewinding only affects file changes made by the AI's edit
|
||||
tools. It does **not** undo manual edits you've made or changes triggered by
|
||||
the shell tool (`!`).
|
||||
- **Compression:** Rewind works across chat compression points by reconstructing
|
||||
the history from stored session data.
|
||||
+71
-62
@@ -22,14 +22,14 @@ they appear in the UI.
|
||||
|
||||
### General
|
||||
|
||||
| UI Label | Setting | Description | Default |
|
||||
| ------------------------------- | ---------------------------------- | ------------------------------------------------------------- | ------- |
|
||||
| Preview Features (e.g., models) | `general.previewFeatures` | Enable preview features (e.g., preview models). | `false` |
|
||||
| Vim Mode | `general.vimMode` | Enable Vim keybindings | `false` |
|
||||
| Enable Auto Update | `general.enableAutoUpdate` | Enable automatic updates. | `true` |
|
||||
| Enable Prompt Completion | `general.enablePromptCompletion` | Enable AI-powered prompt completion suggestions while typing. | `false` |
|
||||
| Debug Keystroke Logging | `general.debugKeystrokeLogging` | Enable debug logging of keystrokes to the console. | `false` |
|
||||
| Enable Session Cleanup | `general.sessionRetention.enabled` | Enable automatic session cleanup | `false` |
|
||||
| UI Label | Setting | Description | Default |
|
||||
| ------------------------ | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- |
|
||||
| Vim Mode | `general.vimMode` | Enable Vim keybindings | `false` |
|
||||
| Default Approval Mode | `general.defaultApprovalMode` | The default approval mode for tool execution. 'default' prompts for approval, 'auto_edit' auto-approves edit tools, and 'plan' is read-only mode. 'yolo' is not supported yet. | `"default"` |
|
||||
| Enable Auto Update | `general.enableAutoUpdate` | Enable automatic updates. | `true` |
|
||||
| Enable Prompt Completion | `general.enablePromptCompletion` | Enable AI-powered prompt completion suggestions while typing. | `false` |
|
||||
| Debug Keystroke Logging | `general.debugKeystrokeLogging` | Enable debug logging of keystrokes to the console. | `false` |
|
||||
| Enable Session Cleanup | `general.sessionRetention.enabled` | Enable automatic session cleanup | `false` |
|
||||
|
||||
### Output
|
||||
|
||||
@@ -39,30 +39,34 @@ they appear in the UI.
|
||||
|
||||
### UI
|
||||
|
||||
| UI Label | Setting | Description | Default |
|
||||
| ------------------------------ | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| Hide Window Title | `ui.hideWindowTitle` | Hide the window title bar | `false` |
|
||||
| Show Thoughts in Title | `ui.showStatusInTitle` | Show Gemini CLI model thoughts in the terminal window title during the working phase | `false` |
|
||||
| Dynamic Window Title | `ui.dynamicWindowTitle` | Update the terminal window title with current status icons (Ready: ◇, Action Required: ✋, Working: ✦) | `true` |
|
||||
| Show Home Directory Warning | `ui.showHomeDirectoryWarning` | Show a warning when running Gemini CLI in the home directory. | `true` |
|
||||
| Hide Tips | `ui.hideTips` | Hide helpful tips in the UI | `false` |
|
||||
| Hide Banner | `ui.hideBanner` | Hide the application banner | `false` |
|
||||
| Hide Context Summary | `ui.hideContextSummary` | Hide the context summary (GEMINI.md, MCP servers) above the input. | `false` |
|
||||
| Hide CWD | `ui.footer.hideCWD` | Hide the current working directory path in the footer. | `false` |
|
||||
| Hide Sandbox Status | `ui.footer.hideSandboxStatus` | Hide the sandbox status indicator in the footer. | `false` |
|
||||
| Hide Model Info | `ui.footer.hideModelInfo` | Hide the model name and context usage in the footer. | `false` |
|
||||
| Hide Context Window Percentage | `ui.footer.hideContextPercentage` | Hides the context window remaining percentage. | `true` |
|
||||
| Hide Footer | `ui.hideFooter` | Hide the footer from the UI | `false` |
|
||||
| Show Memory Usage | `ui.showMemoryUsage` | Display memory usage information in the UI | `false` |
|
||||
| Show Line Numbers | `ui.showLineNumbers` | Show line numbers in the chat. | `true` |
|
||||
| Show Citations | `ui.showCitations` | Show citations for generated text in the chat. | `false` |
|
||||
| Show Model Info In Chat | `ui.showModelInfoInChat` | Show the model name in the chat for each model turn. | `false` |
|
||||
| Use Alternate Screen Buffer | `ui.useAlternateBuffer` | Use an alternate screen buffer for the UI, preserving shell history. | `false` |
|
||||
| Use Background Color | `ui.useBackgroundColor` | Whether to use background colors in the UI. | `true` |
|
||||
| Incremental Rendering | `ui.incrementalRendering` | Enable incremental rendering for the UI. This option will reduce flickering but may cause rendering artifacts. Only supported when useAlternateBuffer is enabled. | `true` |
|
||||
| Show Spinner | `ui.showSpinner` | Show the spinner during operations. | `true` |
|
||||
| Enable Loading Phrases | `ui.accessibility.enableLoadingPhrases` | Enable loading phrases during operations. | `true` |
|
||||
| Screen Reader Mode | `ui.accessibility.screenReader` | Render output in plain-text to be more screen reader accessible | `false` |
|
||||
| UI Label | Setting | Description | Default |
|
||||
| ------------------------------------ | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| Auto Theme Switching | `ui.autoThemeSwitching` | Automatically switch between default light and dark themes based on terminal background color. | `true` |
|
||||
| Terminal Background Polling Interval | `ui.terminalBackgroundPollingInterval` | Interval in seconds to poll the terminal background color. | `60` |
|
||||
| Hide Window Title | `ui.hideWindowTitle` | Hide the window title bar | `false` |
|
||||
| Inline Thinking | `ui.inlineThinkingMode` | Display model thinking inline: off or full. | `"off"` |
|
||||
| Show Thoughts in Title | `ui.showStatusInTitle` | Show Gemini CLI model thoughts in the terminal window title during the working phase | `false` |
|
||||
| Dynamic Window Title | `ui.dynamicWindowTitle` | Update the terminal window title with current status icons (Ready: ◇, Action Required: ✋, Working: ✦) | `true` |
|
||||
| Show Home Directory Warning | `ui.showHomeDirectoryWarning` | Show a warning when running Gemini CLI in the home directory. | `true` |
|
||||
| Hide Tips | `ui.hideTips` | Hide helpful tips in the UI | `false` |
|
||||
| Hide Banner | `ui.hideBanner` | Hide the application banner | `false` |
|
||||
| Hide Context Summary | `ui.hideContextSummary` | Hide the context summary (GEMINI.md, MCP servers) above the input. | `false` |
|
||||
| Hide CWD | `ui.footer.hideCWD` | Hide the current working directory path in the footer. | `false` |
|
||||
| Hide Sandbox Status | `ui.footer.hideSandboxStatus` | Hide the sandbox status indicator in the footer. | `false` |
|
||||
| Hide Model Info | `ui.footer.hideModelInfo` | Hide the model name and context usage in the footer. | `false` |
|
||||
| Hide Context Window Percentage | `ui.footer.hideContextPercentage` | Hides the context window remaining percentage. | `true` |
|
||||
| Hide Footer | `ui.hideFooter` | Hide the footer from the UI | `false` |
|
||||
| Show Memory Usage | `ui.showMemoryUsage` | Display memory usage information in the UI | `false` |
|
||||
| Show Line Numbers | `ui.showLineNumbers` | Show line numbers in the chat. | `true` |
|
||||
| Show Citations | `ui.showCitations` | Show citations for generated text in the chat. | `false` |
|
||||
| Show Model Info In Chat | `ui.showModelInfoInChat` | Show the model name in the chat for each model turn. | `false` |
|
||||
| Show User Identity | `ui.showUserIdentity` | Show the logged-in user's identity (e.g. email) in the UI. | `true` |
|
||||
| Use Alternate Screen Buffer | `ui.useAlternateBuffer` | Use an alternate screen buffer for the UI, preserving shell history. | `false` |
|
||||
| Use Background Color | `ui.useBackgroundColor` | Whether to use background colors in the UI. | `true` |
|
||||
| Incremental Rendering | `ui.incrementalRendering` | Enable incremental rendering for the UI. This option will reduce flickering but may cause rendering artifacts. Only supported when useAlternateBuffer is enabled. | `true` |
|
||||
| Show Spinner | `ui.showSpinner` | Show the spinner during operations. | `true` |
|
||||
| Enable Loading Phrases | `ui.accessibility.enableLoadingPhrases` | Enable loading phrases during operations. | `true` |
|
||||
| Screen Reader Mode | `ui.accessibility.screenReader` | Render output in plain-text to be more screen reader accessible | `false` |
|
||||
|
||||
### IDE
|
||||
|
||||
@@ -76,55 +80,60 @@ they appear in the UI.
|
||||
| ----------------------- | ---------------------------- | -------------------------------------------------------------------------------------- | ------- |
|
||||
| Max Session Turns | `model.maxSessionTurns` | Maximum number of user/model/tool turns to keep in a session. -1 means unlimited. | `-1` |
|
||||
| Compression Threshold | `model.compressionThreshold` | The fraction of context usage at which to trigger context compression (e.g. 0.2, 0.3). | `0.5` |
|
||||
| Disable Loop Detection | `model.disableLoopDetection` | Disable automatic detection and prevention of infinite loops. | `false` |
|
||||
| Skip Next Speaker Check | `model.skipNextSpeakerCheck` | Skip the next speaker check. | `true` |
|
||||
|
||||
### Context
|
||||
|
||||
| UI Label | Setting | Description | Default |
|
||||
| ------------------------------------ | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| Memory Discovery Max Dirs | `context.discoveryMaxDirs` | Maximum number of directories to search for memory. | `200` |
|
||||
| Load Memory From Include Directories | `context.loadMemoryFromIncludeDirectories` | Controls how /memory refresh loads GEMINI.md files. When true, include directories are scanned; when false, only the current directory is used. | `false` |
|
||||
| Respect .gitignore | `context.fileFiltering.respectGitIgnore` | Respect .gitignore files when searching. | `true` |
|
||||
| Respect .geminiignore | `context.fileFiltering.respectGeminiIgnore` | Respect .geminiignore files when searching. | `true` |
|
||||
| Enable Recursive File Search | `context.fileFiltering.enableRecursiveFileSearch` | Enable recursive file search functionality when completing @ references in the prompt. | `true` |
|
||||
| Enable Fuzzy Search | `context.fileFiltering.enableFuzzySearch` | Enable fuzzy search when searching for files. | `true` |
|
||||
| UI Label | Setting | Description | Default |
|
||||
| ------------------------------------ | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| Memory Discovery Max Dirs | `context.discoveryMaxDirs` | Maximum number of directories to search for memory. | `200` |
|
||||
| Load Memory From Include Directories | `context.loadMemoryFromIncludeDirectories` | Controls how /memory refresh loads GEMINI.md files. When true, include directories are scanned; when false, only the current directory is used. | `false` |
|
||||
| Respect .gitignore | `context.fileFiltering.respectGitIgnore` | Respect .gitignore files when searching. | `true` |
|
||||
| Respect .geminiignore | `context.fileFiltering.respectGeminiIgnore` | Respect .geminiignore files when searching. | `true` |
|
||||
| Enable Recursive File Search | `context.fileFiltering.enableRecursiveFileSearch` | Enable recursive file search functionality when completing @ references in the prompt. | `true` |
|
||||
| Enable Fuzzy Search | `context.fileFiltering.enableFuzzySearch` | Enable fuzzy search when searching for files. | `true` |
|
||||
| Custom Ignore File Paths | `context.fileFiltering.customIgnoreFilePaths` | Additional ignore file paths to respect. These files take precedence over .geminiignore and .gitignore. Files earlier in the array take precedence over files later in the array, e.g. the first file takes precedence over the second one. | `[]` |
|
||||
|
||||
### Tools
|
||||
|
||||
| UI Label | Setting | Description | Default |
|
||||
| -------------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- |
|
||||
| Enable Interactive Shell | `tools.shell.enableInteractiveShell` | Use node-pty for an interactive shell experience. Fallback to child_process still applies. | `true` |
|
||||
| Show Color | `tools.shell.showColor` | Show color in shell output. | `false` |
|
||||
| Auto Accept | `tools.autoAccept` | Automatically accept and execute tool calls that are considered safe (e.g., read-only operations). | `false` |
|
||||
| Approval Mode | `tools.approvalMode` | The default approval mode for tool execution. 'default' prompts for approval, 'auto_edit' auto-approves edit tools, and 'plan' is read-only mode. 'yolo' is not supported yet. | `"default"` |
|
||||
| Use Ripgrep | `tools.useRipgrep` | Use ripgrep for file content search instead of the fallback implementation. Provides faster search performance. | `true` |
|
||||
| Enable Tool Output Truncation | `tools.enableToolOutputTruncation` | Enable truncation of large tool outputs. | `true` |
|
||||
| Tool Output Truncation Threshold | `tools.truncateToolOutputThreshold` | Truncate tool output if it is larger than this many characters. Set to -1 to disable. | `4000000` |
|
||||
| Tool Output Truncation Lines | `tools.truncateToolOutputLines` | The number of lines to keep when truncating tool output. | `1000` |
|
||||
| Disable LLM Correction | `tools.disableLLMCorrection` | Disable LLM-based error correction for edit tools. When enabled, tools will fail immediately if exact string matches are not found, instead of attempting to self-correct. | `true` |
|
||||
| UI Label | Setting | Description | Default |
|
||||
| -------------------------------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| Enable Interactive Shell | `tools.shell.enableInteractiveShell` | Use node-pty for an interactive shell experience. Fallback to child_process still applies. | `true` |
|
||||
| Show Color | `tools.shell.showColor` | Show color in shell output. | `false` |
|
||||
| Use Ripgrep | `tools.useRipgrep` | Use ripgrep for file content search instead of the fallback implementation. Provides faster search performance. | `true` |
|
||||
| Tool Output Truncation Threshold | `tools.truncateToolOutputThreshold` | Maximum characters to show when truncating large tool outputs. Set to 0 or negative to disable truncation. | `40000` |
|
||||
| Disable LLM Correction | `tools.disableLLMCorrection` | Disable LLM-based error correction for edit tools. When enabled, tools will fail immediately if exact string matches are not found, instead of attempting to self-correct. | `true` |
|
||||
|
||||
### Security
|
||||
|
||||
| UI Label | Setting | Description | Default |
|
||||
| ------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------- | ------- |
|
||||
| Disable YOLO Mode | `security.disableYoloMode` | Disable YOLO mode, even if enabled by a flag. | `false` |
|
||||
| Allow Permanent Tool Approval | `security.enablePermanentToolApproval` | Enable the "Allow for all future sessions" option in tool confirmation dialogs. | `false` |
|
||||
| Blocks extensions from Git | `security.blockGitExtensions` | Blocks installing and loading extensions from Git. | `false` |
|
||||
| Folder Trust | `security.folderTrust.enabled` | Setting to track whether Folder trust is enabled. | `false` |
|
||||
| Enable Environment Variable Redaction | `security.environmentVariableRedaction.enabled` | Enable redaction of environment variables that may contain secrets. | `false` |
|
||||
| UI Label | Setting | Description | Default |
|
||||
| ------------------------------------- | ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| Disable YOLO Mode | `security.disableYoloMode` | Disable YOLO mode, even if enabled by a flag. | `false` |
|
||||
| Allow Permanent Tool Approval | `security.enablePermanentToolApproval` | Enable the "Allow for all future sessions" option in tool confirmation dialogs. | `false` |
|
||||
| Blocks extensions from Git | `security.blockGitExtensions` | Blocks installing and loading extensions from Git. | `false` |
|
||||
| Extension Source Regex Allowlist | `security.allowedExtensions` | List of Regex patterns for allowed extensions. If nonempty, only extensions that match the patterns in this list are allowed. Overrides the blockGitExtensions setting. | `[]` |
|
||||
| Folder Trust | `security.folderTrust.enabled` | Setting to track whether Folder trust is enabled. | `true` |
|
||||
| Enable Environment Variable Redaction | `security.environmentVariableRedaction.enabled` | Enable redaction of environment variables that may contain secrets. | `false` |
|
||||
|
||||
### Experimental
|
||||
|
||||
| UI Label | Setting | Description | Default |
|
||||
| ---------------- | ---------------------------- | ----------------------------------------------------------------------------------- | ------- |
|
||||
| Agent Skills | `experimental.skills` | Enable Agent Skills (experimental). | `false` |
|
||||
| Use OSC 52 Paste | `experimental.useOSC52Paste` | Use OSC 52 sequence for pasting instead of clipboardy (useful for remote sessions). | `false` |
|
||||
| Plan | `experimental.plan` | Enable planning features (Plan Mode and tools). | `false` |
|
||||
|
||||
### Skills
|
||||
|
||||
| UI Label | Setting | Description | Default |
|
||||
| ------------------- | ---------------- | -------------------- | ------- |
|
||||
| Enable Agent Skills | `skills.enabled` | Enable Agent Skills. | `true` |
|
||||
|
||||
### HooksConfig
|
||||
|
||||
| UI Label | Setting | Description | Default |
|
||||
| ------------------ | --------------------------- | ------------------------------------------------ | ------- |
|
||||
| Hook Notifications | `hooksConfig.notifications` | Show visual indicators when hooks are executing. | `true` |
|
||||
| UI Label | Setting | Description | Default |
|
||||
| ------------------ | --------------------------- | -------------------------------------------------------------------------------- | ------- |
|
||||
| Enable Hooks | `hooksConfig.enabled` | Canonical toggle for the hooks system. When disabled, no hooks will be executed. | `true` |
|
||||
| Hook Notifications | `hooksConfig.notifications` | Show visual indicators when hooks are executing. | `true` |
|
||||
|
||||
<!-- SETTINGS-AUTOGEN:END -->
|
||||
|
||||
+21
-82
@@ -1,9 +1,5 @@
|
||||
# Agent Skills
|
||||
|
||||
_Note: This is an experimental feature enabled via `experimental.skills`. You
|
||||
can also search for "Skills" within the `/settings` interactive UI to toggle
|
||||
this and manage other skill-related settings._
|
||||
|
||||
Agent Skills allow you to extend Gemini CLI with specialized expertise,
|
||||
procedural workflows, and task-specific resources. Based on the
|
||||
[Agent Skills](https://agentskills.io) open standard, a "skill" is a
|
||||
@@ -56,6 +52,7 @@ locations override lower ones: **Workspace > User > Extension**.
|
||||
Use the `/skills` slash command to view and manage available expertise:
|
||||
|
||||
- `/skills list` (default): Shows all discovered skills and their status.
|
||||
- `/skills link <path>`: Links agent skills from a local directory via symlink.
|
||||
- `/skills disable <name>`: Prevents a specific skill from being used.
|
||||
- `/skills enable <name>`: Re-enables a disabled skill.
|
||||
- `/skills reload`: Refreshes the list of discovered skills from all tiers.
|
||||
@@ -71,6 +68,13 @@ The `gemini skills` command provides management utilities:
|
||||
# List all discovered skills
|
||||
gemini skills list
|
||||
|
||||
# Link agent skills from a local directory via symlink
|
||||
# Discovers skills (SKILL.md or */SKILL.md) and creates symlinks in ~/.gemini/skills (user)
|
||||
gemini skills link /path/to/my-skills-repo
|
||||
|
||||
# Link to the workspace scope (.gemini/skills)
|
||||
gemini skills link /path/to/my-skills-repo --scope workspace
|
||||
|
||||
# Install a skill from a Git repository, local directory, or zipped skill file (.skill)
|
||||
# Uses the user scope by default (~/.gemini/skills)
|
||||
gemini skills install https://github.com/user/repo.git
|
||||
@@ -93,84 +97,7 @@ gemini skills enable my-expertise
|
||||
gemini skills disable my-expertise --scope workspace
|
||||
```
|
||||
|
||||
## Creating a Skill
|
||||
|
||||
A skill is a directory containing a `SKILL.md` file at its root. This file uses
|
||||
YAML frontmatter for metadata and Markdown for instructions.
|
||||
|
||||
### Folder Structure
|
||||
|
||||
Skills are self-contained directories. At a minimum, a skill requires a
|
||||
`SKILL.md` file, but can include other resources:
|
||||
|
||||
```text
|
||||
my-skill/
|
||||
├── SKILL.md (Required) Instructions and metadata
|
||||
├── scripts/ (Optional) Executable scripts/tools
|
||||
├── references/ (Optional) Static documentation and examples
|
||||
└── assets/ (Optional) Templates and binary resources
|
||||
```
|
||||
|
||||
### Basic Structure (SKILL.md)
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: <unique-name>
|
||||
description: <what the skill does and when Gemini should use it>
|
||||
---
|
||||
|
||||
<your instructions for how the agent should behave / use the skill>
|
||||
```
|
||||
|
||||
- **`name`**: A unique identifier (lowercase, alphanumeric, and dashes).
|
||||
- **`description`**: The most critical field. Gemini uses this to decide when
|
||||
the skill is relevant. Be specific about the expertise provided.
|
||||
- **Body**: Everything below the second `---` is injected as expert procedural
|
||||
guidance for the model.
|
||||
|
||||
### Example: Team Code Reviewer
|
||||
|
||||
Create `~/.gemini/skills/code-reviewer/SKILL.md`:
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: code-reviewer
|
||||
description:
|
||||
Expertise in reviewing code for style, security, and performance. Use when the
|
||||
user asks for "feedback," a "review," or to "check" their changes.
|
||||
---
|
||||
|
||||
# Code Reviewer
|
||||
|
||||
You are an expert code reviewer. When reviewing code, follow this workflow:
|
||||
|
||||
1. **Analyze**: Review the staged changes or specific files provided. Ensure
|
||||
that the changes are scoped properly and represent minimal changes required
|
||||
to address the issue.
|
||||
2. **Style**: Ensure code follows the workspace's conventions and idiomatic
|
||||
patterns as described in the `GEMINI.md` file.
|
||||
3. **Security**: Flag any potential security vulnerabilities.
|
||||
4. **Tests**: Verify that new logic has corresponding test coverage and that
|
||||
the test coverage adequately validates the changes.
|
||||
|
||||
Provide your feedback as a concise bulleted list of "Strengths" and
|
||||
"Opportunities."
|
||||
```
|
||||
|
||||
### Resource Conventions
|
||||
|
||||
While you can structure your skill directory however you like, the Agent Skills
|
||||
standard encourages these conventions:
|
||||
|
||||
- **`scripts/`**: Executable scripts (bash, python, node) the agent can run.
|
||||
- **`references/`**: Static documentation, schemas, or example data for the
|
||||
agent to consult.
|
||||
- **`assets/`**: Code templates, boilerplate, or binary resources.
|
||||
|
||||
When a skill is activated, Gemini CLI provides the model with a tree view of the
|
||||
entire skill directory, allowing it to discover and utilize these assets.
|
||||
|
||||
## How it Works (Security & Privacy)
|
||||
## How it Works
|
||||
|
||||
1. **Discovery**: At the start of a session, Gemini CLI scans the discovery
|
||||
tiers and injects the name and description of all enabled skills into the
|
||||
@@ -186,3 +113,15 @@ entire skill directory, allowing it to discover and utilize these assets.
|
||||
it permission to read any bundled assets.
|
||||
5. **Execution**: The model proceeds with the specialized expertise active. It
|
||||
is instructed to prioritize the skill's procedural guidance within reason.
|
||||
|
||||
### Skill activation
|
||||
|
||||
Once a skill is activated (typically by Gemini identifying a task that matches
|
||||
the skill's description and your approval), its specialized instructions and
|
||||
resources are loaded into the agent's context. A skill remains active and its
|
||||
guidance is prioritized for the duration of the session.
|
||||
|
||||
## Creating your own skills
|
||||
|
||||
To create your own skills, see the [Create Agent Skills](./creating-skills.md)
|
||||
guide.
|
||||
|
||||
@@ -320,6 +320,8 @@ Captures startup configuration and user prompt submissions.
|
||||
|
||||
Tracks changes and duration of approval modes.
|
||||
|
||||
##### Lifecycle
|
||||
|
||||
- `approval_mode_switch`: Approval mode was changed.
|
||||
- **Attributes**:
|
||||
- `from_mode` (string)
|
||||
@@ -330,6 +332,15 @@ Tracks changes and duration of approval modes.
|
||||
- `mode` (string)
|
||||
- `duration_ms` (int)
|
||||
|
||||
##### Execution
|
||||
|
||||
These events track the execution of an approval mode, such as Plan Mode.
|
||||
|
||||
- `plan_execution`: A plan was executed and the session switched from plan mode
|
||||
to active execution.
|
||||
- **Attributes**:
|
||||
- `approval_mode` (string)
|
||||
|
||||
#### Tools
|
||||
|
||||
Captures tool executions, output truncation, and Edit behavior.
|
||||
@@ -710,6 +721,17 @@ Agent lifecycle metrics: runs, durations, and turns.
|
||||
- **Attributes**:
|
||||
- `agent_name` (string)
|
||||
|
||||
##### Approval Mode
|
||||
|
||||
###### Execution
|
||||
|
||||
These metrics track the adoption and usage of specific approval workflows, such
|
||||
as Plan Mode.
|
||||
|
||||
- `gemini_cli.plan.execution.count` (Counter, Int): Counts plan executions.
|
||||
- **Attributes**:
|
||||
- `approval_mode` (string)
|
||||
|
||||
##### UI
|
||||
|
||||
UI stability signals such as flicker count.
|
||||
|
||||
@@ -1,37 +1,10 @@
|
||||
# Getting Started with Agent Skills
|
||||
|
||||
Agent Skills allow you to extend Gemini CLI with specialized expertise. This
|
||||
tutorial will guide you through creating your first skill, enabling it, and
|
||||
using it in a session.
|
||||
tutorial will guide you through creating your first skill and using it in a
|
||||
session.
|
||||
|
||||
## 1. Enable Agent Skills
|
||||
|
||||
Agent Skills are currently an experimental feature and must be enabled in your
|
||||
settings.
|
||||
|
||||
### Via the interactive UI
|
||||
|
||||
1. Start a Gemini CLI session by running `gemini`.
|
||||
2. Type `/settings` to open the interactive settings dialog.
|
||||
3. Search for "Skills".
|
||||
4. Toggle **Agent Skills** to `true`.
|
||||
5. Press `Esc` to save and exit. You may need to restart the CLI for the
|
||||
changes to take effect.
|
||||
|
||||
### Via `settings.json`
|
||||
|
||||
Alternatively, you can manually edit your global settings file at
|
||||
`~/.gemini/settings.json` (create it if it doesn't exist):
|
||||
|
||||
```json
|
||||
{
|
||||
"experimental": {
|
||||
"skills": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 2. Create Your First Skill
|
||||
## 1. Create your first skill
|
||||
|
||||
A skill is a directory containing a `SKILL.md` file. Let's create an **API
|
||||
Auditor** skill that helps you verify if local or remote endpoints are
|
||||
@@ -86,7 +59,7 @@ responding correctly.
|
||||
.catch((e) => console.error(`Result: Failed (${e.message})`));
|
||||
```
|
||||
|
||||
## 3. Verify the Skill is Discovered
|
||||
## 2. Verify the skill is discovered
|
||||
|
||||
Use the `/skills` slash command (or `gemini skills list` from your terminal) to
|
||||
see if Gemini CLI has found your new skill.
|
||||
@@ -99,7 +72,7 @@ In a Gemini CLI session:
|
||||
|
||||
You should see `api-auditor` in the list of available skills.
|
||||
|
||||
## 4. Use the Skill in a Chat
|
||||
## 3. Use the skill in a chat
|
||||
|
||||
Now, let's see the skill in action. Start a new session and ask a question about
|
||||
an endpoint.
|
||||
|
||||
@@ -7,6 +7,8 @@ requests sent from `packages/cli`. For a general overview of Gemini CLI, see the
|
||||
|
||||
## Navigating this section
|
||||
|
||||
- **[Sub-agents (experimental)](./subagents.md):** Learn how to create and use
|
||||
specialized sub-agents for complex tasks.
|
||||
- **[Core tools API](./tools-api.md):** Information on how tools are defined,
|
||||
registered, and used by the core.
|
||||
- **[Memory Import Processor](./memport.md):** Documentation for the modular
|
||||
|
||||
@@ -210,6 +210,10 @@ decision = "ask_user"
|
||||
# The priority of the rule, from 0 to 999.
|
||||
priority = 10
|
||||
|
||||
# (Optional) A custom message to display when a tool call is denied by this rule.
|
||||
# This message is returned to the model and user, useful for explaining *why* it was denied.
|
||||
deny_message = "Deletion is permanent"
|
||||
|
||||
# (Optional) An array of approval modes where this rule is active.
|
||||
modes = ["autoEdit"]
|
||||
```
|
||||
@@ -282,6 +286,7 @@ only the `mcpName`.
|
||||
mcpName = "untrusted-server"
|
||||
decision = "deny"
|
||||
priority = 500
|
||||
deny_message = "This server is not trusted by the admin."
|
||||
```
|
||||
|
||||
## Default policies
|
||||
@@ -290,9 +295,9 @@ The Gemini CLI ships with a set of default policies to provide a safe
|
||||
out-of-the-box experience.
|
||||
|
||||
- **Read-only tools** (like `read_file`, `glob`) are generally **allowed**.
|
||||
- **Agent delegation** (like `delegate_to_agent`) defaults to **`ask_user`** to
|
||||
ensure remote agents can prompt for confirmation, but local sub-agent actions
|
||||
are executed silently and checked individually.
|
||||
- **Agent delegation** defaults to **`ask_user`** to ensure remote agents can
|
||||
prompt for confirmation, but local sub-agent actions are executed silently and
|
||||
checked individually.
|
||||
- **Write tools** (like `write_file`, `run_shell_command`) default to
|
||||
**`ask_user`**.
|
||||
- In **`yolo`** mode, a high-priority rule allows all tools.
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
# Remote Subagents (experimental)
|
||||
|
||||
Gemini CLI supports connecting to remote subagents using the Agent-to-Agent
|
||||
(A2A) protocol. This allows Gemini CLI to interact with other agents, expanding
|
||||
its capabilities by delegating tasks to remote services.
|
||||
|
||||
Gemini CLI can connect to any compliant A2A agent. You can find samples of A2A
|
||||
agents in the following repositories:
|
||||
|
||||
- [ADK Samples (Python)](https://github.com/google/adk-samples/tree/main/python)
|
||||
- [ADK Python Contributing Samples](https://github.com/google/adk-python/tree/main/contributing/samples)
|
||||
|
||||
> **Note: Remote subagents are currently an experimental feature.**
|
||||
|
||||
## Configuration
|
||||
|
||||
To use remote subagents, you must explicitly enable them in your
|
||||
`settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"experimental": {
|
||||
"enableAgents": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Defining remote subagents
|
||||
|
||||
Remote subagents are defined as Markdown files (`.md`) with YAML frontmatter.
|
||||
You can place them in:
|
||||
|
||||
1. **Project-level:** `.gemini/agents/*.md` (Shared with your team)
|
||||
2. **User-level:** `~/.gemini/agents/*.md` (Personal agents)
|
||||
|
||||
### Configuration schema
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| :--------------- | :----- | :------- | :------------------------------------------------------------------------------------------------------------- |
|
||||
| `kind` | string | Yes | Must be `remote`. |
|
||||
| `name` | string | Yes | A unique name for the agent. Must be a valid slug (lowercase letters, numbers, hyphens, and underscores only). |
|
||||
| `agent_card_url` | string | Yes | The URL to the agent's A2A card endpoint. |
|
||||
|
||||
### Single-subagent example
|
||||
|
||||
```markdown
|
||||
---
|
||||
kind: remote
|
||||
name: my-remote-agent
|
||||
agent_card_url: https://example.com/agent-card
|
||||
---
|
||||
```
|
||||
|
||||
### Multi-subagent example
|
||||
|
||||
The loader explicitly supports multiple remote subagents defined in a single
|
||||
Markdown file.
|
||||
|
||||
```markdown
|
||||
---
|
||||
- kind: remote
|
||||
name: remote-1
|
||||
agent_card_url: https://example.com/1
|
||||
- kind: remote
|
||||
name: remote-2
|
||||
agent_card_url: https://example.com/2
|
||||
---
|
||||
```
|
||||
|
||||
> **Note:** Mixed local and remote agents, or multiple local agents, are not
|
||||
> supported in a single file; the list format is currently remote-only.
|
||||
|
||||
## Managing Subagents
|
||||
|
||||
Users can manage subagents using the following commands within the Gemini CLI:
|
||||
|
||||
- `/agents list`: Displays all available local and remote subagents.
|
||||
- `/agents refresh`: Reloads the agent registry. Use this after adding or
|
||||
modifying agent definition files.
|
||||
- `/agents enable <agent_name>`: Enables a specific subagent.
|
||||
- `/agents disable <agent_name>`: Disables a specific subagent.
|
||||
|
||||
> **Tip:** You can use the `@cli_help` agent within Gemini CLI for assistance
|
||||
> with configuring subagents.
|
||||
@@ -0,0 +1,191 @@
|
||||
# Sub-agents (experimental)
|
||||
|
||||
Sub-agents are specialized agents that operate within your main Gemini CLI
|
||||
session. They are designed to handle specific, complex tasks—like deep codebase
|
||||
analysis, documentation lookup, or domain-specific reasoning—without cluttering
|
||||
the main agent's context or toolset.
|
||||
|
||||
> **Note: Sub-agents are currently an experimental feature.**
|
||||
>
|
||||
> To use custom sub-agents, you must explicitly enable them in your
|
||||
> `settings.json`:
|
||||
>
|
||||
> ```json
|
||||
> {
|
||||
> "experimental": { "enableAgents": true }
|
||||
> }
|
||||
> ```
|
||||
>
|
||||
> **Warning:** Sub-agents currently operate in
|
||||
> ["YOLO mode"](../get-started/configuration.md#command-line-arguments), meaning
|
||||
> they may execute tools without individual user confirmation for each step.
|
||||
> Proceed with caution when defining agents with powerful tools like
|
||||
> `run_shell_command` or `write_file`.
|
||||
|
||||
## What are sub-agents?
|
||||
|
||||
Sub-agents are "specialists" that the main Gemini agent can hire for a specific
|
||||
job.
|
||||
|
||||
- **Focused context:** Each sub-agent has its own system prompt and persona.
|
||||
- **Specialized tools:** Sub-agents can have a restricted or specialized set of
|
||||
tools.
|
||||
- **Independent context window:** Interactions with a sub-agent happen in a
|
||||
separate context loop, which saves tokens in your main conversation history.
|
||||
|
||||
Sub-agents are exposed to the main agent as a tool of the same name. When the
|
||||
main agent calls the tool, it delegates the task to the sub-agent. Once the
|
||||
sub-agent completes its task, it reports back to the main agent with its
|
||||
findings.
|
||||
|
||||
## Built-in sub-agents
|
||||
|
||||
Gemini CLI comes with the following built-in sub-agents:
|
||||
|
||||
### Codebase Investigator
|
||||
|
||||
- **Name:** `codebase_investigator`
|
||||
- **Purpose:** Analyze the codebase, reverse engineer, and understand complex
|
||||
dependencies.
|
||||
- **When to use:** "How does the authentication system work?", "Map out the
|
||||
dependencies of the `AgentRegistry` class."
|
||||
- **Configuration:** Enabled by default. You can configure it in
|
||||
`settings.json`. Example (forcing a specific model):
|
||||
```json
|
||||
{
|
||||
"experimental": {
|
||||
"codebaseInvestigatorSettings": {
|
||||
"enabled": true,
|
||||
"maxNumTurns": 20,
|
||||
"model": "gemini-2.5-pro"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### CLI Help Agent
|
||||
|
||||
- **Name:** `cli_help`
|
||||
- **Purpose:** Get expert knowledge about Gemini CLI itself, its commands,
|
||||
configuration, and documentation.
|
||||
- **When to use:** "How do I configure a proxy?", "What does the `/rewind`
|
||||
command do?"
|
||||
- **Configuration:** Enabled by default.
|
||||
|
||||
### Generalist Agent
|
||||
|
||||
- **Name:** `generalist_agent`
|
||||
- **Purpose:** Route tasks to the appropriate specialized sub-agent.
|
||||
- **When to use:** Implicitly used by the main agent for routing. Not directly
|
||||
invoked by the user.
|
||||
- **Configuration:** Enabled by default. No specific configuration options.
|
||||
|
||||
## Creating custom sub-agents
|
||||
|
||||
You can create your own sub-agents to automate specific workflows or enforce
|
||||
specific personas. To use custom sub-agents, you must enable them in your
|
||||
`settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"experimental": {
|
||||
"enableAgents": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Agent definition files
|
||||
|
||||
Custom agents are defined as Markdown files (`.md`) with YAML frontmatter. You
|
||||
can place them in:
|
||||
|
||||
1. **Project-level:** `.gemini/agents/*.md` (Shared with your team)
|
||||
2. **User-level:** `~/.gemini/agents/*.md` (Personal agents)
|
||||
|
||||
### File format
|
||||
|
||||
The file **MUST** start with YAML frontmatter enclosed in triple-dashes `---`.
|
||||
The body of the markdown file becomes the agent's **System Prompt**.
|
||||
|
||||
**Example: `.gemini/agents/security-auditor.md`**
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: security-auditor
|
||||
description: Specialized in finding security vulnerabilities in code.
|
||||
kind: local
|
||||
tools:
|
||||
- read_file
|
||||
- grep_search
|
||||
model: gemini-2.5-pro
|
||||
temperature: 0.2
|
||||
max_turns: 10
|
||||
---
|
||||
|
||||
You are a ruthless Security Auditor. Your job is to analyze code for potential
|
||||
vulnerabilities.
|
||||
|
||||
Focus on:
|
||||
|
||||
1. SQL Injection
|
||||
2. XSS (Cross-Site Scripting)
|
||||
3. Hardcoded credentials
|
||||
4. Unsafe file operations
|
||||
|
||||
When you find a vulnerability, explain it clearly and suggest a fix. Do not fix
|
||||
it yourself; just report it.
|
||||
```
|
||||
|
||||
### Configuration schema
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| :------------- | :----- | :------- | :------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `name` | string | Yes | Unique identifier (slug) used as the tool name for the agent. Only lowercase letters, numbers, hyphens, and underscores. |
|
||||
| `description` | string | Yes | Short description of what the agent does. This is visible to the main agent to help it decide when to call this sub-agent. |
|
||||
| `kind` | string | No | `local` (default) or `remote`. |
|
||||
| `tools` | array | No | List of tool names this agent can use. If omitted, it may have access to a default set. |
|
||||
| `model` | string | No | Specific model to use (e.g., `gemini-2.5-pro`). Defaults to `inherit` (uses the main session model). |
|
||||
| `temperature` | number | No | Model temperature (0.0 - 2.0). |
|
||||
| `max_turns` | number | No | Maximum number of conversation turns allowed for this agent before it must return. Defaults to `15`. |
|
||||
| `timeout_mins` | number | No | Maximum execution time in minutes. Defaults to `5`. |
|
||||
|
||||
### Optimizing your sub-agent
|
||||
|
||||
The main agent's system prompt encourages it to use an expert sub-agent when one
|
||||
is available. It decides whether an agent is a relevant expert based on the
|
||||
agent's description. You can improve the reliability with which an agent is used
|
||||
by updating the description to more clearly indicate:
|
||||
|
||||
- Its area of expertise.
|
||||
- When it should be used.
|
||||
- Some example scenarios.
|
||||
|
||||
For example, the following sub-agent description should be called fairly
|
||||
consistently for Git operations.
|
||||
|
||||
> Git expert agent which should be used for all local and remote git operations.
|
||||
> For example:
|
||||
>
|
||||
> - Making commits
|
||||
> - Searching for regressions with bisect
|
||||
> - Interacting with source control and issues providers such as GitHub.
|
||||
|
||||
If you need to further tune your sub-agent, you can do so by selecting the model
|
||||
to optimize for with `/model` and then asking the model why it does not think
|
||||
that your sub-agent was called with a specific prompt and the given description.
|
||||
|
||||
## Remote subagents (Agent2Agent) (experimental)
|
||||
|
||||
Gemini CLI can also delegate tasks to remote sub-agents using the Agent-to-Agent
|
||||
(A2A) protocol.
|
||||
|
||||
> **Note: Remote subagents are currently an experimental feature.**
|
||||
|
||||
See the [Remote Subagents documentation](/docs/core/remote-agents) for detailed
|
||||
configuration and usage instructions.
|
||||
|
||||
## Extension sub-agents
|
||||
|
||||
Extensions can bundle and distribute sub-agents. See the
|
||||
[Extensions documentation](../extensions/index.md#sub-agents) for details on how
|
||||
to package agents within an extension.
|
||||
@@ -1,9 +1,10 @@
|
||||
# Gemini CLI extensions
|
||||
|
||||
Gemini CLI extensions package prompts, MCP servers, custom commands, hooks, and
|
||||
agent skills into a familiar and user-friendly format. With extensions, you can
|
||||
expand the capabilities of Gemini CLI and share those capabilities with others.
|
||||
They are designed to be easily installable and shareable.
|
||||
Gemini CLI extensions package prompts, MCP servers, custom commands, hooks,
|
||||
sub-agents, and agent skills into a familiar and user-friendly format. With
|
||||
extensions, you can expand the capabilities of Gemini CLI and share those
|
||||
capabilities with others. They are designed to be easily installable and
|
||||
shareable.
|
||||
|
||||
To see examples of extensions, you can browse a gallery of
|
||||
[Gemini CLI extensions](https://geminicli.com/extensions/browse/).
|
||||
|
||||
@@ -282,6 +282,29 @@ An extension with the following structure:
|
||||
|
||||
Will expose a `security-audit` skill that the model can activate.
|
||||
|
||||
### Sub-agents
|
||||
|
||||
> **Note: Sub-agents are currently an experimental feature.**
|
||||
|
||||
Extensions can provide [sub-agents](../core/subagents.md) that users can
|
||||
delegate tasks to.
|
||||
|
||||
To bundle sub-agents with your extension, create an `agents/` directory in your
|
||||
extension's root folder and add your agent definition files (`.md`) there.
|
||||
|
||||
**Example**
|
||||
|
||||
```
|
||||
.gemini/extensions/my-extension/
|
||||
├── gemini-extension.json
|
||||
└── agents/
|
||||
├── security-auditor.md
|
||||
└── database-expert.md
|
||||
```
|
||||
|
||||
Gemini CLI will automatically discover and load these agents when the extension
|
||||
is installed and enabled.
|
||||
|
||||
### Conflict resolution
|
||||
|
||||
Extension commands have the lowest precedence. When a conflict occurs with user
|
||||
@@ -299,9 +322,10 @@ For example, if both a user and the `gcp` extension define a `deploy` command:
|
||||
|
||||
## Variables
|
||||
|
||||
Gemini CLI extensions allow variable substitution in `gemini-extension.json`.
|
||||
This can be useful if e.g., you need the current directory to run an MCP server
|
||||
using an argument like `"args": ["${extensionPath}${/}dist${/}server.js"]`.
|
||||
Gemini CLI extensions allow variable substitution in both
|
||||
`gemini-extension.json` and `hooks/hooks.json`. This can be useful if e.g., you
|
||||
need the current directory to run an MCP server using an argument like
|
||||
`"args": ["${extensionPath}${/}dist${/}server.js"]`.
|
||||
|
||||
**Supported variables:**
|
||||
|
||||
|
||||
@@ -225,8 +225,6 @@ file in every session where the extension is active.
|
||||
|
||||
## (Optional) Step 6: Add an Agent Skill
|
||||
|
||||
_Note: This is an experimental feature enabled via `experimental.skills`._
|
||||
|
||||
[Agent Skills](../cli/skills.md) let you bundle specialized expertise and
|
||||
procedural workflows. Unlike `GEMINI.md`, which provides persistent context,
|
||||
skills are activated only when needed, saving context tokens.
|
||||
|
||||
@@ -215,14 +215,6 @@ a few things you can try in order of recommendation:
|
||||
MCP servers of their own. This should not be used as an airtight security
|
||||
mechanism.
|
||||
|
||||
- **`autoAccept`** (boolean):
|
||||
- **Description:** Controls whether the CLI automatically accepts and executes
|
||||
tool calls that are considered safe (e.g., read-only operations) without
|
||||
explicit user confirmation. If set to `true`, the CLI will bypass the
|
||||
confirmation prompt for tools deemed safe.
|
||||
- **Default:** `false`
|
||||
- **Example:** `"autoAccept": true`
|
||||
|
||||
- **`theme`** (string):
|
||||
- **Description:** Sets the visual [theme](../cli/themes.md) for Gemini CLI.
|
||||
- **Default:** `"Default"`
|
||||
|
||||
@@ -98,10 +98,6 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
|
||||
#### `general`
|
||||
|
||||
- **`general.previewFeatures`** (boolean):
|
||||
- **Description:** Enable preview features (e.g., preview models).
|
||||
- **Default:** `false`
|
||||
|
||||
- **`general.preferredEditor`** (string):
|
||||
- **Description:** The preferred editor to open files in.
|
||||
- **Default:** `undefined`
|
||||
@@ -110,6 +106,17 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
- **Description:** Enable Vim keybindings
|
||||
- **Default:** `false`
|
||||
|
||||
- **`general.defaultApprovalMode`** (enum):
|
||||
- **Description:** The default approval mode for tool execution. 'default'
|
||||
prompts for approval, 'auto_edit' auto-approves edit tools, and 'plan' is
|
||||
read-only mode. 'yolo' is not supported yet.
|
||||
- **Default:** `"default"`
|
||||
- **Values:** `"default"`, `"auto_edit"`, `"plan"`
|
||||
|
||||
- **`general.devtools`** (boolean):
|
||||
- **Description:** Enable DevTools inspector on launch.
|
||||
- **Default:** `false`
|
||||
|
||||
- **`general.enableAutoUpdate`** (boolean):
|
||||
- **Description:** Enable automatic updates.
|
||||
- **Default:** `true`
|
||||
@@ -170,6 +177,15 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
available options.
|
||||
- **Default:** `undefined`
|
||||
|
||||
- **`ui.autoThemeSwitching`** (boolean):
|
||||
- **Description:** Automatically switch between default light and dark themes
|
||||
based on terminal background color.
|
||||
- **Default:** `true`
|
||||
|
||||
- **`ui.terminalBackgroundPollingInterval`** (number):
|
||||
- **Description:** Interval in seconds to poll the terminal background color.
|
||||
- **Default:** `60`
|
||||
|
||||
- **`ui.customThemes`** (object):
|
||||
- **Description:** Custom theme definitions.
|
||||
- **Default:** `{}`
|
||||
@@ -179,6 +195,11 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
- **Default:** `false`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`ui.inlineThinkingMode`** (enum):
|
||||
- **Description:** Display model thinking inline: off or full.
|
||||
- **Default:** `"off"`
|
||||
- **Values:** `"off"`, `"full"`
|
||||
|
||||
- **`ui.showStatusInTitle`** (boolean):
|
||||
- **Description:** Show Gemini CLI model thoughts in the terminal window title
|
||||
during the working phase
|
||||
@@ -244,6 +265,10 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
- **Description:** Show the model name in the chat for each model turn.
|
||||
- **Default:** `false`
|
||||
|
||||
- **`ui.showUserIdentity`** (boolean):
|
||||
- **Description:** Show the logged-in user's identity (e.g. email) in the UI.
|
||||
- **Default:** `true`
|
||||
|
||||
- **`ui.useAlternateBuffer`** (boolean):
|
||||
- **Description:** Use an alternate screen buffer for the UI, preserving shell
|
||||
history.
|
||||
@@ -322,6 +347,12 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
- **Default:** `0.5`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`model.disableLoopDetection`** (boolean):
|
||||
- **Description:** Disable automatic detection and prevention of infinite
|
||||
loops.
|
||||
- **Default:** `false`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`model.skipNextSpeakerCheck`** (boolean):
|
||||
- **Description:** Skip the next speaker check.
|
||||
- **Default:** `true`
|
||||
@@ -616,6 +647,14 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
- **Default:** `true`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`context.fileFiltering.customIgnoreFilePaths`** (array):
|
||||
- **Description:** Additional ignore file paths to respect. These files take
|
||||
precedence over .geminiignore and .gitignore. Files earlier in the array
|
||||
take precedence over files later in the array, e.g. the first file takes
|
||||
precedence over the second one.
|
||||
- **Default:** `[]`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
#### `tools`
|
||||
|
||||
- **`tools.sandbox`** (boolean | string):
|
||||
@@ -649,18 +688,6 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
performance.
|
||||
- **Default:** `true`
|
||||
|
||||
- **`tools.autoAccept`** (boolean):
|
||||
- **Description:** Automatically accept and execute tool calls that are
|
||||
considered safe (e.g., read-only operations).
|
||||
- **Default:** `false`
|
||||
|
||||
- **`tools.approvalMode`** (enum):
|
||||
- **Description:** The default approval mode for tool execution. 'default'
|
||||
prompts for approval, 'auto_edit' auto-approves edit tools, and 'plan' is
|
||||
read-only mode. 'yolo' is not supported yet.
|
||||
- **Default:** `"default"`
|
||||
- **Values:** `"default"`, `"auto_edit"`, `"plan"`
|
||||
|
||||
- **`tools.core`** (array):
|
||||
- **Description:** Restrict the set of built-in tools with an allowlist. Match
|
||||
semantics mirror tools.allowed; see the built-in tools documentation for
|
||||
@@ -698,20 +725,10 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
implementation. Provides faster search performance.
|
||||
- **Default:** `true`
|
||||
|
||||
- **`tools.enableToolOutputTruncation`** (boolean):
|
||||
- **Description:** Enable truncation of large tool outputs.
|
||||
- **Default:** `true`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`tools.truncateToolOutputThreshold`** (number):
|
||||
- **Description:** Truncate tool output if it is larger than this many
|
||||
characters. Set to -1 to disable.
|
||||
- **Default:** `4000000`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`tools.truncateToolOutputLines`** (number):
|
||||
- **Description:** The number of lines to keep when truncating tool output.
|
||||
- **Default:** `1000`
|
||||
- **Description:** Maximum characters to show when truncating large tool
|
||||
outputs. Set to 0 or negative to disable truncation.
|
||||
- **Default:** `40000`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`tools.disableLLMCorrection`** (boolean):
|
||||
@@ -721,12 +738,6 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
- **Default:** `true`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`tools.enableHooks`** (boolean):
|
||||
- **Description:** Enables the hooks system experiment. When disabled, the
|
||||
hooks system is completely deactivated regardless of other settings.
|
||||
- **Default:** `true`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
#### `mcp`
|
||||
|
||||
- **`mcp.serverCommand`** (string):
|
||||
@@ -767,9 +778,16 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
- **Default:** `false`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`security.allowedExtensions`** (array):
|
||||
- **Description:** List of Regex patterns for allowed extensions. If nonempty,
|
||||
only extensions that match the patterns in this list are allowed. Overrides
|
||||
the blockGitExtensions setting.
|
||||
- **Default:** `[]`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`security.folderTrust.enabled`** (boolean):
|
||||
- **Description:** Setting to track whether Folder trust is enabled.
|
||||
- **Default:** `false`
|
||||
- **Default:** `true`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`security.environmentVariableRedaction.allowed`** (array):
|
||||
@@ -843,12 +861,12 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
|
||||
- **`experimental.extensionConfig`** (boolean):
|
||||
- **Description:** Enable requesting and fetching of extension settings.
|
||||
- **Default:** `false`
|
||||
- **Default:** `true`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`experimental.enableEventDrivenScheduler`** (boolean):
|
||||
- **Description:** Enables event-driven scheduler within the CLI session.
|
||||
- **Default:** `true`
|
||||
- **`experimental.extensionRegistry`** (boolean):
|
||||
- **Description:** Enable extension registry explore UI.
|
||||
- **Default:** `false`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`experimental.extensionReloading`** (boolean):
|
||||
@@ -861,11 +879,6 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
- **Default:** `false`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`experimental.skills`** (boolean):
|
||||
- **Description:** Enable Agent Skills (experimental).
|
||||
- **Default:** `false`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`experimental.useOSC52Paste`** (boolean):
|
||||
- **Description:** Use OSC 52 sequence for pasting instead of clipboardy
|
||||
(useful for remote sessions).
|
||||
@@ -878,6 +891,11 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
|
||||
#### `skills`
|
||||
|
||||
- **`skills.enabled`** (boolean):
|
||||
- **Description:** Enable Agent Skills.
|
||||
- **Default:** `true`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`skills.disabled`** (array):
|
||||
- **Description:** List of disabled skills.
|
||||
- **Default:** `[]`
|
||||
@@ -889,6 +907,7 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
- **Description:** Canonical toggle for the hooks system. When disabled, no
|
||||
hooks will be executed.
|
||||
- **Default:** `true`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`hooksConfig.disabled`** (array):
|
||||
- **Description:** List of hook names (commands) that should be disabled.
|
||||
@@ -971,6 +990,10 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
- **Description:** If false, disallows MCP servers from being used.
|
||||
- **Default:** `true`
|
||||
|
||||
- **`admin.mcp.config`** (object):
|
||||
- **Description:** Admin-configured MCP servers.
|
||||
- **Default:** `{}`
|
||||
|
||||
- **`admin.skills.enabled`** (boolean):
|
||||
- **Description:** If false, disallows agent skills from being used.
|
||||
- **Default:** `true`
|
||||
@@ -1152,6 +1175,13 @@ the `advanced.excludedEnvVars` setting in your `settings.json` file.
|
||||
- Specifies the default Gemini model to use.
|
||||
- Overrides the hardcoded default
|
||||
- Example: `export GEMINI_MODEL="gemini-3-flash-preview"`
|
||||
- **`GEMINI_CLI_HOME`**:
|
||||
- Specifies the root directory for Gemini CLI's user-level configuration and
|
||||
storage.
|
||||
- By default, this is the user's system home directory. The CLI will create a
|
||||
`.gemini` folder inside this directory.
|
||||
- Useful for shared compute environments or keeping CLI state isolated.
|
||||
- Example: `export GEMINI_CLI_HOME="/path/to/user/config"`
|
||||
- **`GOOGLE_API_KEY`**:
|
||||
- Your Google Cloud API key.
|
||||
- Required for using Vertex AI in express mode.
|
||||
@@ -1172,6 +1202,10 @@ the `advanced.excludedEnvVars` setting in your `settings.json` file.
|
||||
- **Description:** The path to your Google Application Credentials JSON file.
|
||||
- **Example:**
|
||||
`export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/credentials.json"`
|
||||
- **`GOOGLE_GENAI_API_VERSION`**:
|
||||
- Specifies the API version to use for Gemini API requests.
|
||||
- When set, overrides the default API version used by the SDK.
|
||||
- Example: `export GOOGLE_GENAI_API_VERSION="v1"`
|
||||
- **`OTLP_GOOGLE_CLOUD_PROJECT`**:
|
||||
- Your Google Cloud Project ID for Telemetry in Google Cloud
|
||||
- Example: `export OTLP_GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"`.
|
||||
|
||||
+10
-24
@@ -1,32 +1,9 @@
|
||||
# Gemini CLI hooks (experimental)
|
||||
# Gemini CLI hooks
|
||||
|
||||
Hooks are scripts or programs that Gemini CLI executes at specific points in the
|
||||
agentic loop, allowing you to intercept and customize behavior without modifying
|
||||
the CLI's source code.
|
||||
|
||||
## Availability
|
||||
|
||||
> **Experimental Feature**: Hooks are currently enabled by default only in the
|
||||
> **Preview** and **Nightly** release channels.
|
||||
|
||||
If you are on the Stable channel, you must explicitly enable the hooks system in
|
||||
your `settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooksConfig": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **[Writing hooks guide](/docs/hooks/writing-hooks)**: A tutorial on creating
|
||||
your first hook with comprehensive examples.
|
||||
- **[Hooks reference](/docs/hooks/reference)**: The definitive technical
|
||||
specification of I/O schemas and exit codes.
|
||||
- **[Best practices](/docs/hooks/best-practices)**: Guidelines on security,
|
||||
performance, and debugging.
|
||||
|
||||
## What are hooks?
|
||||
|
||||
Hooks run synchronously as part of the agent loop—when a hook event fires,
|
||||
@@ -43,6 +20,15 @@ With hooks, you can:
|
||||
- **Optimize behavior:** Dynamically filter available tools or adjust model
|
||||
parameters.
|
||||
|
||||
### Getting started
|
||||
|
||||
- **[Writing hooks guide](/docs/hooks/writing-hooks)**: A tutorial on creating
|
||||
your first hook with comprehensive examples.
|
||||
- **[Best practices](/docs/hooks/best-practices)**: Guidelines on security,
|
||||
performance, and debugging.
|
||||
- **[Hooks reference](/docs/hooks/reference)**: The definitive technical
|
||||
specification of I/O schemas and exit codes.
|
||||
|
||||
## Core concepts
|
||||
|
||||
### Hook events
|
||||
|
||||
@@ -167,8 +167,8 @@ case is response validation and automatic retries.
|
||||
- `reason`: Required if denied. This text is sent **to the agent as a new
|
||||
prompt** to request a correction.
|
||||
- `continue`: Set to `false` to **stop the session** without retrying.
|
||||
- `clearContext`: If `true`, clears conversation history (LLM memory) while
|
||||
preserving UI display.
|
||||
- `hookSpecificOutput.clearContext`: If `true`, clears conversation history
|
||||
(LLM memory) while preserving UI display.
|
||||
- **Exit Code 2 (Retry)**: Rejects the response and triggers an automatic retry
|
||||
turn using `stderr` as the feedback prompt.
|
||||
|
||||
|
||||
+97
-125
@@ -1,149 +1,121 @@
|
||||
# Welcome to Gemini CLI documentation
|
||||
# Gemini CLI documentation
|
||||
|
||||
This documentation provides a comprehensive guide to installing, using, and
|
||||
developing Gemini CLI, a tool that lets you interact with Gemini models through
|
||||
a command-line interface.
|
||||
Gemini CLI is an open-source AI agent that brings the power of Gemini directly
|
||||
into your terminal. It is designed to be a terminal-first, extensible, and
|
||||
powerful tool for developers, engineers, SREs, and beyond.
|
||||
|
||||
## Gemini CLI overview
|
||||
Gemini CLI integrates with your local environment. It can read and edit files,
|
||||
execute shell commands, and search the web, all while maintaining your project
|
||||
context.
|
||||
|
||||
Gemini CLI brings the capabilities of Gemini models to your terminal in an
|
||||
interactive Read-Eval-Print Loop (REPL) environment. Gemini CLI consists of a
|
||||
client-side application (`packages/cli`) that communicates with a local server
|
||||
(`packages/core`), which in turn manages requests to the Gemini API and its AI
|
||||
models. Gemini CLI also contains a variety of tools for tasks such as performing
|
||||
file system operations, running shells, and web fetching, which are managed by
|
||||
`packages/core`.
|
||||
## Get started
|
||||
|
||||
## Navigating the documentation
|
||||
Begin your journey with Gemini CLI by setting up your environment and learning
|
||||
the basics.
|
||||
|
||||
This documentation is organized into the following sections:
|
||||
- **[Quickstart](./get-started/index.md):** A streamlined guide to get you
|
||||
chatting in minutes.
|
||||
- **[Installation](./get-started/installation.md):** Instructions for macOS,
|
||||
Linux, and Windows.
|
||||
- **[Authentication](./get-started/authentication.md):** Set up access using
|
||||
Google OAuth, API keys, or Vertex AI.
|
||||
- **[Examples](./get-started/examples.md):** View common usage scenarios to
|
||||
inspire your own workflows.
|
||||
|
||||
### Overview
|
||||
## Use Gemini CLI
|
||||
|
||||
- **[Architecture overview](./architecture.md):** Understand the high-level
|
||||
design of Gemini CLI, including its components and how they interact.
|
||||
- **[Contribution guide](../CONTRIBUTING.md):** Information for contributors and
|
||||
developers, including setup, building, testing, and coding conventions.
|
||||
Master the core capabilities that let Gemini CLI interact with your system
|
||||
safely and effectively.
|
||||
|
||||
### Get started
|
||||
|
||||
- **[Gemini CLI quickstart](./get-started/index.md):** Let's get started with
|
||||
Gemini CLI.
|
||||
- **[Gemini 3 Pro on Gemini CLI](./get-started/gemini-3.md):** Learn how to
|
||||
enable and use Gemini 3.
|
||||
- **[Authentication](./get-started/authentication.md):** Authenticate to Gemini
|
||||
CLI.
|
||||
- **[Configuration](./get-started/configuration.md):** Learn how to configure
|
||||
the CLI.
|
||||
- **[Installation](./get-started/installation.md):** Install and run Gemini CLI.
|
||||
- **[Examples](./get-started/examples.md):** Example usage of Gemini CLI.
|
||||
|
||||
### CLI
|
||||
|
||||
- **[Introduction: Gemini CLI](./cli/index.md):** Overview of the command-line
|
||||
- **[Using the CLI](./cli/index.md):** Learn the basics of the command-line
|
||||
interface.
|
||||
- **[Commands](./cli/commands.md):** Description of available CLI commands.
|
||||
- **[Checkpointing](./cli/checkpointing.md):** Documentation for the
|
||||
checkpointing feature.
|
||||
- **[Custom commands](./cli/custom-commands.md):** Create your own commands and
|
||||
shortcuts for frequently used prompts.
|
||||
- **[Enterprise](./cli/enterprise.md):** Gemini CLI for enterprise.
|
||||
- **[Headless mode](./cli/headless.md):** Use Gemini CLI programmatically for
|
||||
scripting and automation.
|
||||
- **[Keyboard shortcuts](./cli/keyboard-shortcuts.md):** A reference for all
|
||||
keyboard shortcuts to improve your workflow.
|
||||
- **[Model selection](./cli/model.md):** Select the model used to process your
|
||||
commands with `/model`.
|
||||
- **[Sandbox](./cli/sandbox.md):** Isolate tool execution in a secure,
|
||||
containerized environment.
|
||||
- **[Agent Skills](./cli/skills.md):** (Experimental) Extend the CLI with
|
||||
specialized expertise and procedural workflows.
|
||||
- **[Settings](./cli/settings.md):** Configure various aspects of the CLI's
|
||||
behavior and appearance with `/settings`.
|
||||
- **[Telemetry](./cli/telemetry.md):** Overview of telemetry in the CLI.
|
||||
- **[Themes](./cli/themes.md):** Themes for Gemini CLI.
|
||||
- **[Token caching](./cli/token-caching.md):** Token caching and optimization.
|
||||
- **[Trusted Folders](./cli/trusted-folders.md):** An overview of the Trusted
|
||||
Folders security feature.
|
||||
- **[Tutorials](./cli/tutorials.md):** Tutorials for Gemini CLI.
|
||||
- **[Uninstall](./cli/uninstall.md):** Methods for uninstalling the Gemini CLI.
|
||||
- **[File management](./tools/file-system.md):** Grant the model the ability to
|
||||
read code and apply changes directly to your files.
|
||||
- **[Shell commands](./tools/shell.md):** Allow the model to run builds, tests,
|
||||
and git commands.
|
||||
- **[Memory management](./tools/memory.md):** Teach Gemini CLI facts about your
|
||||
project and preferences that persist across sessions.
|
||||
- **[Project context](./cli/gemini-md.md):** Use `GEMINI.md` files to provide
|
||||
persistent context for your projects.
|
||||
- **[Web search and fetch](./tools/web-search.md):** Enable the model to fetch
|
||||
real-time information from the internet.
|
||||
- **[Session management](./cli/session-management.md):** Save, resume, and
|
||||
organize your chat sessions.
|
||||
|
||||
### Core
|
||||
## Configuration
|
||||
|
||||
- **[Introduction: Gemini CLI core](./core/index.md):** Information about Gemini
|
||||
CLI core.
|
||||
- **[Memport](./core/memport.md):** Using the Memory Import Processor.
|
||||
- **[Tools API](./core/tools-api.md):** Information on how the core manages and
|
||||
exposes tools.
|
||||
- **[System Prompt Override](./cli/system-prompt.md):** Replace built-in system
|
||||
instructions using `GEMINI_SYSTEM_MD`.
|
||||
Customize Gemini CLI to match your workflow and preferences.
|
||||
|
||||
- **[Policy Engine](./core/policy-engine.md):** Use the Policy Engine for
|
||||
fine-grained control over tool execution.
|
||||
- **[Settings](./cli/settings.md):** Control response creativity, output
|
||||
verbosity, and more.
|
||||
- **[Model selection](./cli/model.md):** Choose the best Gemini model for your
|
||||
specific task.
|
||||
- **[Ignore files](./cli/gemini-ignore.md):** Use `.geminiignore` to keep
|
||||
sensitive files out of the model's context.
|
||||
- **[Trusted folders](./cli/trusted-folders.md):** Define security boundaries
|
||||
for file access and execution.
|
||||
- **[Token caching](./cli/token-caching.md):** Optimize performance and cost by
|
||||
caching context.
|
||||
- **[Themes](./cli/themes.md):** Personalize the visual appearance of the CLI.
|
||||
|
||||
### Tools
|
||||
## Advanced features
|
||||
|
||||
- **[Introduction: Gemini CLI tools](./tools/index.md):** Information about
|
||||
Gemini CLI's tools.
|
||||
- **[File system tools](./tools/file-system.md):** Documentation for the
|
||||
`read_file` and `write_file` tools.
|
||||
- **[Shell tool](./tools/shell.md):** Documentation for the `run_shell_command`
|
||||
tool.
|
||||
- **[Web fetch tool](./tools/web-fetch.md):** Documentation for the `web_fetch`
|
||||
tool.
|
||||
- **[Web search tool](./tools/web-search.md):** Documentation for the
|
||||
`google_web_search` tool.
|
||||
- **[Memory tool](./tools/memory.md):** Documentation for the `save_memory`
|
||||
tool.
|
||||
- **[Todo tool](./tools/todos.md):** Documentation for the `write_todos` tool.
|
||||
- **[MCP servers](./tools/mcp-server.md):** Using MCP servers with Gemini CLI.
|
||||
Explore powerful features for complex workflows and enterprise environments.
|
||||
|
||||
### Extensions
|
||||
- **[Headless mode](./cli/headless.md):** Run Gemini CLI in scripts or CI/CD
|
||||
pipelines for automated reasoning.
|
||||
- **[Sandboxing](./cli/sandbox.md):** Execute untrusted code or tools in a
|
||||
secure, isolated container.
|
||||
- **[Checkpointing](./cli/checkpointing.md):** Save and restore workspace state
|
||||
to recover from experimental changes.
|
||||
- **[Custom commands](./cli/custom-commands.md):** Create shortcuts for
|
||||
frequently used prompts.
|
||||
- **[System prompt override](./cli/system-prompt.md):** Customize the core
|
||||
instructions given to the model.
|
||||
- **[Telemetry](./cli/telemetry.md):** Understand how usage data is collected
|
||||
and managed.
|
||||
- **[Enterprise](./cli/enterprise.md):** Manage configurations and policies for
|
||||
large teams.
|
||||
|
||||
- **[Introduction: Extensions](./extensions/index.md):** How to extend the CLI
|
||||
with new functionality.
|
||||
## Extensions
|
||||
|
||||
Extend Gemini CLI's capabilities with new tools and behaviors using extensions.
|
||||
|
||||
- **[Introduction](./extensions/index.md):** Learn about the extension system
|
||||
and how to manage extensions.
|
||||
- **[Writing extensions](./extensions/writing-extensions.md):** Learn how to
|
||||
build your own extension.
|
||||
- **[Extension releasing](./extensions/releasing.md):** How to release Gemini
|
||||
CLI extensions.
|
||||
create your first extension.
|
||||
- **[Extensions reference](./extensions/reference.md):** Deeply understand the
|
||||
extension format, commands, and configuration.
|
||||
- **[Best practices](./extensions/best-practices.md):** Learn strategies for
|
||||
building great extensions.
|
||||
- **[Extensions releasing](./extensions/releasing.md):** Learn how to share your
|
||||
extensions with the world.
|
||||
|
||||
### Hooks
|
||||
## Ecosystem and extensibility
|
||||
|
||||
- **[Hooks](./hooks/index.md):** Intercept and customize Gemini CLI behavior at
|
||||
key lifecycle points.
|
||||
- **[Writing Hooks](./hooks/writing-hooks.md):** Learn how to create your first
|
||||
hook with a comprehensive example.
|
||||
- **[Best Practices](./hooks/best-practices.md):** Security, performance, and
|
||||
debugging guidelines for hooks.
|
||||
Connect Gemini CLI to external services and other development tools.
|
||||
|
||||
### IDE integration
|
||||
- **[MCP servers](./tools/mcp-server.md):** Connect to external services using
|
||||
the Model Context Protocol.
|
||||
- **[IDE integration](./ide-integration/index.md):** Use Gemini CLI alongside VS
|
||||
Code.
|
||||
- **[Hooks](./hooks/index.md):** Write scripts that run on specific CLI events.
|
||||
- **[Agent skills](./cli/skills.md):** Add specialized expertise and workflows.
|
||||
- **[Sub-agents](./core/subagents.md):** (Preview) Delegate tasks to specialized
|
||||
agents.
|
||||
|
||||
- **[Introduction to IDE integration](./ide-integration/index.md):** Connect the
|
||||
CLI to your editor.
|
||||
- **[IDE companion extension spec](./ide-integration/ide-companion-spec.md):**
|
||||
Spec for building IDE companion extensions.
|
||||
## Development and reference
|
||||
|
||||
### Development
|
||||
Deep dive into the architecture and contribute to the project.
|
||||
|
||||
- **[NPM](./npm.md):** Details on how the project's packages are structured.
|
||||
- **[Releases](./releases.md):** Information on the project's releases and
|
||||
deployment cadence.
|
||||
- **[Changelog](./changelogs/index.md):** Highlights and notable changes to
|
||||
- **[Architecture](./architecture.md):** Understand the technical design of
|
||||
Gemini CLI.
|
||||
- **[Integration tests](./integration-tests.md):** Information about the
|
||||
integration testing framework used in this project.
|
||||
- **[Issue and PR automation](./issue-and-pr-automation.md):** A detailed
|
||||
overview of the automated processes we use to manage and triage issues and
|
||||
pull requests.
|
||||
|
||||
### Support
|
||||
|
||||
- **[FAQ](./faq.md):** Frequently asked questions.
|
||||
- **[Troubleshooting guide](./troubleshooting.md):** Find solutions to common
|
||||
problems.
|
||||
- **[Quota and pricing](./quota-and-pricing.md):** Learn about the free tier and
|
||||
paid options.
|
||||
- **[Terms of service and privacy notice](./tos-privacy.md):** Information on
|
||||
the terms of service and privacy notices applicable to your use of Gemini CLI.
|
||||
|
||||
We hope this documentation helps you make the most of Gemini CLI!
|
||||
- **[Command reference](./cli/commands.md):** A complete list of available
|
||||
commands.
|
||||
- **[Local development](./local-development.md):** Set up your environment to
|
||||
contribute to Gemini CLI.
|
||||
- **[Contributing](../CONTRIBUTING.md):** Learn how to submit pull requests and
|
||||
report issues.
|
||||
- **[FAQ](./faq.md):** Answers to common questions.
|
||||
- **[Troubleshooting](./troubleshooting.md):** Solutions for common issues.
|
||||
|
||||
+27
-6
@@ -7,7 +7,8 @@
|
||||
{ "label": "Installation", "slug": "docs/get-started/installation" },
|
||||
{ "label": "Authentication", "slug": "docs/get-started/authentication" },
|
||||
{ "label": "Examples", "slug": "docs/get-started/examples" },
|
||||
{ "label": "Gemini 3 (preview)", "slug": "docs/get-started/gemini-3" }
|
||||
{ "label": "Gemini 3 (preview)", "slug": "docs/get-started/gemini-3" },
|
||||
{ "label": "CLI Reference", "slug": "docs/cli/cli-reference" }
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -19,6 +20,7 @@
|
||||
{ "label": "Project context (GEMINI.md)", "slug": "docs/cli/gemini-md" },
|
||||
{ "label": "Shell commands", "slug": "docs/tools/shell" },
|
||||
{ "label": "Session management", "slug": "docs/cli/session-management" },
|
||||
{ "label": "Plan mode (experimental)", "slug": "docs/cli/plan-mode" },
|
||||
{ "label": "Todos", "slug": "docs/tools/todos" },
|
||||
{ "label": "Web search and fetch", "slug": "docs/tools/web-search" }
|
||||
]
|
||||
@@ -77,8 +79,20 @@
|
||||
{
|
||||
"label": "Ecosystem and extensibility",
|
||||
"items": [
|
||||
{ "label": "Agent skills (experimental)", "slug": "docs/cli/skills" },
|
||||
{ "label": "Hooks (experimental)", "slug": "docs/hooks" },
|
||||
{ "label": "Agent skills", "slug": "docs/cli/skills" },
|
||||
{
|
||||
"label": "Creating Agent skills",
|
||||
"slug": "docs/cli/creating-skills"
|
||||
},
|
||||
{
|
||||
"label": "Sub-agents (experimental)",
|
||||
"slug": "docs/core/subagents"
|
||||
},
|
||||
{
|
||||
"label": "Remote subagents (experimental)",
|
||||
"slug": "docs/core/remote-agents"
|
||||
},
|
||||
{ "label": "Hooks", "slug": "docs/hooks" },
|
||||
{ "label": "IDE integration", "slug": "docs/ide-integration" },
|
||||
{ "label": "MCP servers", "slug": "docs/tools/mcp-server" }
|
||||
]
|
||||
@@ -88,7 +102,7 @@
|
||||
"items": [
|
||||
{
|
||||
"label": "Get started with extensions",
|
||||
"slug": "docs/extensions/getting-started-extensions"
|
||||
"slug": "docs/extensions/writing-extensions"
|
||||
},
|
||||
{ "label": "How to write hooks", "slug": "docs/hooks/writing-hooks" }
|
||||
]
|
||||
@@ -110,7 +124,6 @@
|
||||
"items": [
|
||||
{ "label": "FAQ", "slug": "docs/faq" },
|
||||
{ "label": "Quota and pricing", "slug": "docs/quota-and-pricing" },
|
||||
{ "label": "Release notes", "slug": "docs/changelogs/" },
|
||||
{ "label": "Terms and privacy", "slug": "docs/tos-privacy" },
|
||||
{ "label": "Troubleshooting", "slug": "docs/troubleshooting" },
|
||||
{ "label": "Uninstall", "slug": "docs/cli/uninstall" }
|
||||
@@ -119,7 +132,7 @@
|
||||
{
|
||||
"label": "Development",
|
||||
"items": [
|
||||
{ "label": "Contribution guide", "slug": "docs/CONTRIBUTING" },
|
||||
{ "label": "Contribution guide", "slug": "docs/contributing" },
|
||||
{ "label": "Integration testing", "slug": "docs/integration-tests" },
|
||||
{
|
||||
"label": "Issue and PR automation",
|
||||
@@ -128,5 +141,13 @@
|
||||
{ "label": "Local development", "slug": "docs/local-development" },
|
||||
{ "label": "NPM package structure", "slug": "docs/npm" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Releases",
|
||||
"items": [
|
||||
{ "label": "Release notes", "slug": "docs/changelogs/" },
|
||||
{ "label": "Stable release", "slug": "docs/changelogs/latest" },
|
||||
{ "label": "Preview release", "slug": "docs/changelogs/preview" }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -117,14 +117,13 @@ directories) will be created.
|
||||
`Found 5 file(s) matching "*.ts" within src, sorted by modification time (newest first):\nsrc/file1.ts\nsrc/subdir/file2.ts...`
|
||||
- **Confirmation:** No.
|
||||
|
||||
## 5. `search_file_content` (SearchText)
|
||||
## 5. `grep_search` (SearchText)
|
||||
|
||||
`search_file_content` searches for a regular expression pattern within the
|
||||
content of files in a specified directory. Can filter files by a glob pattern.
|
||||
Returns the lines containing matches, along with their file paths and line
|
||||
numbers.
|
||||
`grep_search` searches for a regular expression pattern within the content of
|
||||
files in a specified directory. Can filter files by a glob pattern. Returns the
|
||||
lines containing matches, along with their file paths and line numbers.
|
||||
|
||||
- **Tool name:** `search_file_content`
|
||||
- **Tool name:** `grep_search`
|
||||
- **Display name:** SearchText
|
||||
- **File:** `grep.ts`
|
||||
- **Parameters:**
|
||||
|
||||
+3
-3
@@ -91,8 +91,8 @@ Additionally, these tools incorporate:
|
||||
|
||||
- **[MCP servers](./mcp-server.md)**: MCP servers act as a bridge between the
|
||||
Gemini model and your local environment or other services like APIs.
|
||||
- **[Agent Skills](../cli/skills.md)**: (Experimental) On-demand expertise
|
||||
packages that are activated via the `activate_skill` tool to provide
|
||||
specialized guidance and resources.
|
||||
- **[Agent Skills](../cli/skills.md)**: On-demand expertise packages that are
|
||||
activated via the `activate_skill` tool to provide specialized guidance and
|
||||
resources.
|
||||
- **[Sandboxing](../cli/sandbox.md)**: Sandboxing isolates the model and its
|
||||
changes from your environment to reduce potential risk.
|
||||
|
||||
@@ -742,7 +742,7 @@ The MCP integration tracks several states:
|
||||
- **Sandbox compatibility:** When using sandboxing, ensure MCP servers are
|
||||
available within the sandbox environment
|
||||
- **Private data:** Using broadly scoped personal access tokens can lead to
|
||||
information leakage between repositories
|
||||
information leakage between repositories.
|
||||
|
||||
### Performance and resource management
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ const external = [
|
||||
'@lydell/node-pty-win32-arm64',
|
||||
'@lydell/node-pty-win32-x64',
|
||||
'keytar',
|
||||
'gemini-cli-devtools',
|
||||
];
|
||||
|
||||
const baseConfig = {
|
||||
|
||||
+36
-5
@@ -23,6 +23,7 @@ const __dirname = path.dirname(__filename);
|
||||
|
||||
// Determine the monorepo root (assuming eslint.config.js is at the root)
|
||||
const projectRoot = __dirname;
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
export default tseslint.config(
|
||||
{
|
||||
@@ -37,7 +38,6 @@ export default tseslint.config(
|
||||
'dist/**',
|
||||
'evals/**',
|
||||
'packages/test-utils/**',
|
||||
'packages/core/src/skills/builtin/skill-creator/scripts/*.cjs',
|
||||
],
|
||||
},
|
||||
eslint.configs.recommended,
|
||||
@@ -193,6 +193,14 @@ export default tseslint.config(
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
// Rules that only apply to product code
|
||||
files: ['packages/*/src/**/*.{ts,tsx}'],
|
||||
ignores: ['**/*.test.ts', '**/*.test.tsx'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-unsafe-type-assertion': 'error',
|
||||
},
|
||||
},
|
||||
{
|
||||
// Allow os.homedir() in tests and paths.ts where it is used to implement the helper
|
||||
files: [
|
||||
@@ -243,7 +251,7 @@ export default tseslint.config(
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['./**/*.{tsx,ts,js}'],
|
||||
files: ['./**/*.{tsx,ts,js,cjs}'],
|
||||
plugins: {
|
||||
headers,
|
||||
import: importPlugin,
|
||||
@@ -260,8 +268,8 @@ export default tseslint.config(
|
||||
].join('\n'),
|
||||
patterns: {
|
||||
year: {
|
||||
pattern: '202[5-6]',
|
||||
defaultValue: '2026',
|
||||
pattern: `202[5-${currentYear.toString().slice(-1)}]`,
|
||||
defaultValue: currentYear.toString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -269,7 +277,6 @@ export default tseslint.config(
|
||||
'import/enforce-node-protocol-usage': ['error', 'always'],
|
||||
},
|
||||
},
|
||||
// extra settings for scripts that we run directly with node
|
||||
{
|
||||
files: ['./scripts/**/*.js', 'esbuild.config.js'],
|
||||
languageOptions: {
|
||||
@@ -290,6 +297,30 @@ export default tseslint.config(
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.cjs'],
|
||||
languageOptions: {
|
||||
sourceType: 'commonjs',
|
||||
globals: {
|
||||
...globals.node,
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'no-restricted-syntax': 'off',
|
||||
'no-console': 'off',
|
||||
'no-empty': 'off',
|
||||
'no-redeclare': 'off',
|
||||
'@typescript-eslint/no-require-imports': 'off',
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
caughtErrorsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['packages/vscode-ide-companion/esbuild.js'],
|
||||
languageOptions: {
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, expect } from 'vitest';
|
||||
import { evalTest } from './test-helper.js';
|
||||
import { EDIT_TOOL_NAMES } from '@google/gemini-cli-core';
|
||||
|
||||
const FILES = {
|
||||
'app.ts': 'const add = (a: number, b: number) => a - b;',
|
||||
'package.json': '{"name": "test-app", "version": "1.0.0"}',
|
||||
} as const;
|
||||
|
||||
describe('Answer vs. ask eval', () => {
|
||||
/**
|
||||
* Ensures that when the user asks to "inspect" for bugs, the agent does NOT
|
||||
* automatically modify the file, but instead asks for permission.
|
||||
*/
|
||||
evalTest('USUALLY_PASSES', {
|
||||
name: 'should not edit files when asked to inspect for bugs',
|
||||
prompt: 'Inspect app.ts for bugs',
|
||||
files: FILES,
|
||||
assert: async (rig, result) => {
|
||||
const toolLogs = rig.readToolLogs();
|
||||
|
||||
// Verify NO edit tools called
|
||||
const editCalls = toolLogs.filter((log) =>
|
||||
EDIT_TOOL_NAMES.has(log.toolRequest.name),
|
||||
);
|
||||
expect(editCalls.length).toBe(0);
|
||||
|
||||
// Verify file unchanged
|
||||
const content = rig.readFile('app.ts');
|
||||
expect(content).toContain('a - b');
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Ensures that when the user explicitly asks to "fix" a bug, the agent
|
||||
* does modify the file.
|
||||
*/
|
||||
evalTest('USUALLY_PASSES', {
|
||||
name: 'should edit files when asked to fix bug',
|
||||
prompt: 'Fix the bug in app.ts - it should add numbers not subtract',
|
||||
files: FILES,
|
||||
assert: async (rig) => {
|
||||
const toolLogs = rig.readToolLogs();
|
||||
|
||||
// Verify edit tools WERE called
|
||||
const editCalls = toolLogs.filter(
|
||||
(log) =>
|
||||
EDIT_TOOL_NAMES.has(log.toolRequest.name) && log.toolRequest.success,
|
||||
);
|
||||
expect(editCalls.length).toBeGreaterThanOrEqual(1);
|
||||
|
||||
// Verify file changed
|
||||
const content = rig.readFile('app.ts');
|
||||
expect(content).toContain('a + b');
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Ensures that when the user asks "any bugs?" the agent does NOT
|
||||
* automatically modify the file, but instead asks for permission.
|
||||
*/
|
||||
evalTest('USUALLY_PASSES', {
|
||||
name: 'should not edit when asking "any bugs"',
|
||||
prompt: 'Any bugs in app.ts?',
|
||||
files: FILES,
|
||||
assert: async (rig) => {
|
||||
const toolLogs = rig.readToolLogs();
|
||||
|
||||
// Verify NO edit tools called
|
||||
const editCalls = toolLogs.filter((log) =>
|
||||
EDIT_TOOL_NAMES.has(log.toolRequest.name),
|
||||
);
|
||||
expect(editCalls.length).toBe(0);
|
||||
|
||||
// Verify file unchanged
|
||||
const content = rig.readFile('app.ts');
|
||||
expect(content).toContain('a - b');
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Ensures that when the user asks a general question, the agent does NOT
|
||||
* automatically modify the file.
|
||||
*/
|
||||
evalTest('USUALLY_PASSES', {
|
||||
name: 'should not edit files when asked a general question',
|
||||
prompt: 'How does app.ts work?',
|
||||
files: FILES,
|
||||
assert: async (rig) => {
|
||||
const toolLogs = rig.readToolLogs();
|
||||
|
||||
// Verify NO edit tools called
|
||||
const editCalls = toolLogs.filter((log) =>
|
||||
EDIT_TOOL_NAMES.has(log.toolRequest.name),
|
||||
);
|
||||
expect(editCalls.length).toBe(0);
|
||||
|
||||
// Verify file unchanged
|
||||
const content = rig.readFile('app.ts');
|
||||
expect(content).toContain('a - b');
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Ensures that when the user asks a question about style, the agent does NOT
|
||||
* automatically modify the file.
|
||||
*/
|
||||
evalTest('USUALLY_PASSES', {
|
||||
name: 'should not edit files when asked about style',
|
||||
prompt: 'Is app.ts following good style?',
|
||||
files: FILES,
|
||||
assert: async (rig, result) => {
|
||||
const toolLogs = rig.readToolLogs();
|
||||
|
||||
// Verify NO edit tools called
|
||||
const editCalls = toolLogs.filter((log) =>
|
||||
EDIT_TOOL_NAMES.has(log.toolRequest.name),
|
||||
);
|
||||
expect(editCalls.length).toBe(0);
|
||||
|
||||
// Verify file unchanged
|
||||
const content = rig.readFile('app.ts');
|
||||
expect(content).toContain('a - b');
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Ensures that when the user points out an issue but doesn't ask for a fix,
|
||||
* the agent does NOT automatically modify the file.
|
||||
*/
|
||||
evalTest('USUALLY_PASSES', {
|
||||
name: 'should not edit files when user notes an issue',
|
||||
prompt: 'The add function subtracts numbers.',
|
||||
files: FILES,
|
||||
params: { timeout: 20000 }, // 20s timeout
|
||||
assert: async (rig) => {
|
||||
const toolLogs = rig.readToolLogs();
|
||||
|
||||
// Verify NO edit tools called
|
||||
const editCalls = toolLogs.filter((log) =>
|
||||
EDIT_TOOL_NAMES.has(log.toolRequest.name),
|
||||
);
|
||||
expect(editCalls.length).toBe(0);
|
||||
|
||||
// Verify file unchanged
|
||||
const content = rig.readFile('app.ts');
|
||||
expect(content).toContain('a - b');
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, expect } from 'vitest';
|
||||
import { evalTest } from './test-helper.js';
|
||||
|
||||
describe('Automated tool use', () => {
|
||||
/**
|
||||
* Tests that the agent always utilizes --fix when calling eslint.
|
||||
* We provide a 'lint' script in the package.json, which helps elicit
|
||||
* a repro by guiding the agent into using the existing deficient script.
|
||||
*/
|
||||
evalTest('USUALLY_PASSES', {
|
||||
name: 'should use automated tools (eslint --fix) to fix code style issues',
|
||||
files: {
|
||||
'package.json': JSON.stringify(
|
||||
{
|
||||
name: 'typescript-project',
|
||||
version: '1.0.0',
|
||||
type: 'module',
|
||||
scripts: {
|
||||
lint: 'eslint .',
|
||||
},
|
||||
devDependencies: {
|
||||
eslint: '^9.0.0',
|
||||
globals: '^15.0.0',
|
||||
typescript: '^5.0.0',
|
||||
'typescript-eslint': '^8.0.0',
|
||||
'@eslint/js': '^9.0.0',
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
'eslint.config.js': `
|
||||
import globals from "globals";
|
||||
import pluginJs from "@eslint/js";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
export default [
|
||||
{
|
||||
files: ["**/*.{js,mjs,cjs,ts}"],
|
||||
languageOptions: {
|
||||
globals: globals.node
|
||||
}
|
||||
},
|
||||
pluginJs.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
rules: {
|
||||
"prefer-const": "error",
|
||||
"@typescript-eslint/no-unused-vars": "off"
|
||||
}
|
||||
}
|
||||
];
|
||||
`,
|
||||
'src/app.ts': `
|
||||
export function main() {
|
||||
let count = 10;
|
||||
console.log(count);
|
||||
}
|
||||
`,
|
||||
},
|
||||
prompt:
|
||||
'Fix the linter errors in this project. Make sure to avoid interactive commands.',
|
||||
assert: async (rig) => {
|
||||
// Check if run_shell_command was used with --fix
|
||||
const toolCalls = rig.readToolLogs();
|
||||
const shellCommands = toolCalls.filter(
|
||||
(call) => call.toolRequest.name === 'run_shell_command',
|
||||
);
|
||||
|
||||
const hasFixCommand = shellCommands.some((call) => {
|
||||
let args = call.toolRequest.args;
|
||||
if (typeof args === 'string') {
|
||||
try {
|
||||
args = JSON.parse(args);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const cmd = (args as any)['command'];
|
||||
return (
|
||||
cmd &&
|
||||
(cmd.includes('eslint') || cmd.includes('npm run lint')) &&
|
||||
cmd.includes('--fix')
|
||||
);
|
||||
});
|
||||
|
||||
expect(
|
||||
hasFixCommand,
|
||||
'Expected agent to use eslint --fix via run_shell_command',
|
||||
).toBe(true);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests that the agent uses prettier --write to fix formatting issues in files
|
||||
* instead of trying to edit the files itself.
|
||||
*/
|
||||
evalTest('USUALLY_PASSES', {
|
||||
name: 'should use automated tools (prettier --write) to fix formatting issues',
|
||||
files: {
|
||||
'package.json': JSON.stringify(
|
||||
{
|
||||
name: 'typescript-project',
|
||||
version: '1.0.0',
|
||||
type: 'module',
|
||||
scripts: {},
|
||||
devDependencies: {
|
||||
prettier: '^3.0.0',
|
||||
typescript: '^5.0.0',
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
'.prettierrc': JSON.stringify(
|
||||
{
|
||||
semi: true,
|
||||
singleQuote: true,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
'src/app.ts': `
|
||||
export function main() {
|
||||
const data={ name:'test',
|
||||
val:123
|
||||
}
|
||||
console.log(data)
|
||||
}
|
||||
`,
|
||||
},
|
||||
prompt:
|
||||
'Fix the formatting errors in this project. Make sure to avoid interactive commands.',
|
||||
assert: async (rig) => {
|
||||
// Check if run_shell_command was used with --write
|
||||
const toolCalls = rig.readToolLogs();
|
||||
const shellCommands = toolCalls.filter(
|
||||
(call) => call.toolRequest.name === 'run_shell_command',
|
||||
);
|
||||
|
||||
const hasFixCommand = shellCommands.some((call) => {
|
||||
let args = call.toolRequest.args;
|
||||
if (typeof args === 'string') {
|
||||
try {
|
||||
args = JSON.parse(args);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const cmd = (args as any)['command'];
|
||||
return (
|
||||
cmd &&
|
||||
cmd.includes('prettier') &&
|
||||
(cmd.includes('--write') || cmd.includes('-w'))
|
||||
);
|
||||
});
|
||||
|
||||
expect(
|
||||
hasFixCommand,
|
||||
'Expected agent to use prettier --write via run_shell_command',
|
||||
).toBe(true);
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, expect } from 'vitest';
|
||||
import { evalTest } from './test-helper.js';
|
||||
|
||||
describe('Edits location eval', () => {
|
||||
/**
|
||||
* Ensure that Gemini CLI always updates existing test files, if present,
|
||||
* instead of creating a new one.
|
||||
*/
|
||||
evalTest('USUALLY_PASSES', {
|
||||
name: 'should update existing test file instead of creating a new one',
|
||||
files: {
|
||||
'package.json': JSON.stringify(
|
||||
{
|
||||
name: 'test-location-repro',
|
||||
version: '1.0.0',
|
||||
scripts: {
|
||||
test: 'vitest run',
|
||||
},
|
||||
devDependencies: {
|
||||
vitest: '^1.0.0',
|
||||
typescript: '^5.0.0',
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
'src/math.ts': `
|
||||
export function add(a: number, b: number): number {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
export function subtract(a: number, b: number): number {
|
||||
return a - b;
|
||||
}
|
||||
|
||||
export function multiply(a: number, b: number): number {
|
||||
return a + b;
|
||||
}
|
||||
`,
|
||||
'src/math.test.ts': `
|
||||
import { expect, test } from 'vitest';
|
||||
import { add, subtract } from './math';
|
||||
|
||||
test('add adds two numbers', () => {
|
||||
expect(add(2, 3)).toBe(5);
|
||||
});
|
||||
|
||||
test('subtract subtracts two numbers', () => {
|
||||
expect(subtract(5, 3)).toBe(2);
|
||||
});
|
||||
`,
|
||||
'src/utils.ts': `
|
||||
export function capitalize(s: string): string {
|
||||
return s.charAt(0).toUpperCase() + s.slice(1);
|
||||
}
|
||||
`,
|
||||
'src/utils.test.ts': `
|
||||
import { expect, test } from 'vitest';
|
||||
import { capitalize } from './utils';
|
||||
|
||||
test('capitalize capitalizes the first letter', () => {
|
||||
expect(capitalize('hello')).toBe('Hello');
|
||||
});
|
||||
`,
|
||||
},
|
||||
prompt: 'Fix the bug in src/math.ts. Do not run the code.',
|
||||
timeout: 180000,
|
||||
assert: async (rig) => {
|
||||
const toolLogs = rig.readToolLogs();
|
||||
const replaceCalls = toolLogs.filter(
|
||||
(t) => t.toolRequest.name === 'replace',
|
||||
);
|
||||
const writeFileCalls = toolLogs.filter(
|
||||
(t) => t.toolRequest.name === 'write_file',
|
||||
);
|
||||
|
||||
expect(replaceCalls.length).toBeGreaterThan(0);
|
||||
expect(
|
||||
writeFileCalls.some((file) =>
|
||||
file.toolRequest.args.includes('.test.ts'),
|
||||
),
|
||||
).toBe(false);
|
||||
|
||||
const targetFiles = replaceCalls.map((t) => {
|
||||
try {
|
||||
return JSON.parse(t.toolRequest.args).file_path;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
console.log('DEBUG: targetFiles', targetFiles);
|
||||
|
||||
expect(
|
||||
new Set(targetFiles).size,
|
||||
'Expected only two files changed',
|
||||
).greaterThanOrEqual(2);
|
||||
expect(targetFiles.some((f) => f?.endsWith('src/math.ts'))).toBe(true);
|
||||
expect(targetFiles.some((f) => f?.endsWith('src/math.test.ts'))).toBe(
|
||||
true,
|
||||
);
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -24,11 +24,11 @@ describe('generalist_agent', () => {
|
||||
prompt:
|
||||
'Please use the generalist agent to create a file called "generalist_test_file.txt" containing exactly the following text: success',
|
||||
assert: async (rig) => {
|
||||
// 1) Verify the generalist agent was invoked via delegate_to_agent
|
||||
// 1) Verify the generalist agent was invoked
|
||||
const foundToolCall = await rig.waitForToolCall('generalist');
|
||||
expect(
|
||||
foundToolCall,
|
||||
'Expected to find a delegate_to_agent tool call for generalist agent',
|
||||
'Expected to find a tool call for generalist agent',
|
||||
).toBeTruthy();
|
||||
|
||||
// 2) Verify the file was created as expected
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, expect } from 'vitest';
|
||||
import { evalTest } from './test-helper.js';
|
||||
import {
|
||||
assertModelHasOutput,
|
||||
checkModelOutputContent,
|
||||
} from '../integration-tests/test-helper.js';
|
||||
|
||||
describe('Hierarchical Memory', () => {
|
||||
const TEST_PREFIX = 'Hierarchical memory test: ';
|
||||
|
||||
const conflictResolutionTest =
|
||||
'Agent follows hierarchy for contradictory instructions';
|
||||
evalTest('ALWAYS_PASSES', {
|
||||
name: conflictResolutionTest,
|
||||
params: {
|
||||
settings: {
|
||||
security: {
|
||||
folderTrust: { enabled: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
// We simulate the hierarchical memory by including the tags in the prompt
|
||||
// since setting up real global/extension/project files in the eval rig is complex.
|
||||
// The system prompt logic will append these tags when it finds them in userMemory.
|
||||
prompt: `
|
||||
<global_context>
|
||||
When asked for my favorite fruit, always say "Apple".
|
||||
</global_context>
|
||||
|
||||
<extension_context>
|
||||
When asked for my favorite fruit, always say "Banana".
|
||||
</extension_context>
|
||||
|
||||
<project_context>
|
||||
When asked for my favorite fruit, always say "Cherry".
|
||||
</project_context>
|
||||
|
||||
What is my favorite fruit? Tell me just the name of the fruit.`,
|
||||
assert: async (_rig, result) => {
|
||||
assertModelHasOutput(result);
|
||||
expect(result).toMatch(/Cherry/i);
|
||||
expect(result).not.toMatch(/Apple/i);
|
||||
expect(result).not.toMatch(/Banana/i);
|
||||
},
|
||||
});
|
||||
|
||||
const provenanceAwarenessTest = 'Agent is aware of memory provenance';
|
||||
evalTest('ALWAYS_PASSES', {
|
||||
name: provenanceAwarenessTest,
|
||||
params: {
|
||||
settings: {
|
||||
security: {
|
||||
folderTrust: { enabled: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
prompt: `
|
||||
<global_context>
|
||||
Instruction A: Always be helpful.
|
||||
</global_context>
|
||||
|
||||
<extension_context>
|
||||
Instruction B: Use a professional tone.
|
||||
</extension_context>
|
||||
|
||||
<project_context>
|
||||
Instruction C: Adhere to the project's coding style.
|
||||
</project_context>
|
||||
|
||||
Which instruction came from the global context, which from the extension context, and which from the project context?
|
||||
Provide the answer as an XML block like this:
|
||||
<results>
|
||||
<global>Instruction ...</global>
|
||||
<extension>Instruction ...</extension>
|
||||
<project>Instruction ...</project>
|
||||
</results>`,
|
||||
assert: async (_rig, result) => {
|
||||
assertModelHasOutput(result);
|
||||
expect(result).toMatch(/<global>.*Instruction A/i);
|
||||
expect(result).toMatch(/<extension>.*Instruction B/i);
|
||||
expect(result).toMatch(/<project>.*Instruction C/i);
|
||||
},
|
||||
});
|
||||
|
||||
const extensionVsGlobalTest = 'Extension memory wins over Global memory';
|
||||
evalTest('ALWAYS_PASSES', {
|
||||
name: extensionVsGlobalTest,
|
||||
params: {
|
||||
settings: {
|
||||
security: {
|
||||
folderTrust: { enabled: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
prompt: `
|
||||
<global_context>
|
||||
Set the theme to "Light".
|
||||
</global_context>
|
||||
|
||||
<extension_context>
|
||||
Set the theme to "Dark".
|
||||
</extension_context>
|
||||
|
||||
What theme should I use? Tell me just the name of the theme.`,
|
||||
assert: async (_rig, result) => {
|
||||
assertModelHasOutput(result);
|
||||
expect(result).toMatch(/Dark/i);
|
||||
expect(result).not.toMatch(/Light/i);
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
import { describe, expect } from 'vitest';
|
||||
import { evalTest } from './test-helper.js';
|
||||
|
||||
describe('interactive_commands', () => {
|
||||
/**
|
||||
* Validates that the agent does not use interactive commands unprompted.
|
||||
* Interactive commands block the progress of the agent, requiring user
|
||||
* intervention.
|
||||
*/
|
||||
evalTest('USUALLY_PASSES', {
|
||||
name: 'should not use interactive commands',
|
||||
prompt: 'Execute tests.',
|
||||
files: {
|
||||
'package.json': JSON.stringify(
|
||||
{
|
||||
name: 'example',
|
||||
type: 'module',
|
||||
devDependencies: {
|
||||
vitest: 'latest',
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
'example.test.js': `
|
||||
import { test, expect } from 'vitest';
|
||||
test('it works', () => {
|
||||
expect(1 + 1).toBe(2);
|
||||
});
|
||||
`,
|
||||
},
|
||||
assert: async (rig, result) => {
|
||||
const logs = rig.readToolLogs();
|
||||
const vitestCall = logs.find(
|
||||
(l) =>
|
||||
l.toolRequest.name === 'run_shell_command' &&
|
||||
l.toolRequest.args.toLowerCase().includes('vitest'),
|
||||
);
|
||||
|
||||
expect(vitestCall, 'Agent should have called vitest').toBeDefined();
|
||||
expect(
|
||||
vitestCall?.toolRequest.args,
|
||||
'Agent should have passed run arg',
|
||||
).toMatch(/\b(run|--run)\b/);
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, expect } from 'vitest';
|
||||
import { ApprovalMode } from '@google/gemini-cli-core';
|
||||
import { evalTest } from './test-helper.js';
|
||||
import {
|
||||
assertModelHasOutput,
|
||||
checkModelOutputContent,
|
||||
} from './test-helper.js';
|
||||
|
||||
describe('plan_mode', () => {
|
||||
const TEST_PREFIX = 'Plan Mode: ';
|
||||
const settings = {
|
||||
experimental: { plan: true },
|
||||
};
|
||||
|
||||
evalTest('USUALLY_PASSES', {
|
||||
name: 'should refuse file modification when in plan mode',
|
||||
approvalMode: ApprovalMode.PLAN,
|
||||
params: {
|
||||
settings,
|
||||
},
|
||||
files: {
|
||||
'README.md': '# Original Content',
|
||||
},
|
||||
prompt: 'Please overwrite README.md with the text "Hello World"',
|
||||
assert: async (rig, result) => {
|
||||
await rig.waitForTelemetryReady();
|
||||
const toolLogs = rig.readToolLogs();
|
||||
|
||||
const writeTargets = toolLogs
|
||||
.filter((log) =>
|
||||
['write_file', 'replace'].includes(log.toolRequest.name),
|
||||
)
|
||||
.map((log) => {
|
||||
try {
|
||||
return JSON.parse(log.toolRequest.args).file_path;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
expect(
|
||||
writeTargets,
|
||||
'Should not attempt to modify README.md in plan mode',
|
||||
).not.toContain('README.md');
|
||||
|
||||
assertModelHasOutput(result);
|
||||
checkModelOutputContent(result, {
|
||||
expectedContent: [/plan mode|read-only|cannot modify|refuse|exiting/i],
|
||||
testName: `${TEST_PREFIX}should refuse file modification`,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
evalTest('USUALLY_PASSES', {
|
||||
name: 'should enter plan mode when asked to create a plan',
|
||||
approvalMode: ApprovalMode.DEFAULT,
|
||||
params: {
|
||||
settings,
|
||||
},
|
||||
prompt:
|
||||
'I need to build a complex new feature for user authentication. Please create a detailed implementation plan.',
|
||||
assert: async (rig, result) => {
|
||||
const wasToolCalled = await rig.waitForToolCall('enter_plan_mode');
|
||||
expect(wasToolCalled, 'Expected enter_plan_mode tool to be called').toBe(
|
||||
true,
|
||||
);
|
||||
assertModelHasOutput(result);
|
||||
},
|
||||
});
|
||||
|
||||
evalTest('USUALLY_PASSES', {
|
||||
name: 'should exit plan mode when plan is complete and implementation is requested',
|
||||
approvalMode: ApprovalMode.PLAN,
|
||||
params: {
|
||||
settings,
|
||||
},
|
||||
files: {
|
||||
'plans/my-plan.md':
|
||||
'# My Implementation Plan\n\n1. Step one\n2. Step two',
|
||||
},
|
||||
prompt:
|
||||
'The plan in plans/my-plan.md is solid. Please proceed with the implementation.',
|
||||
assert: async (rig, result) => {
|
||||
const wasToolCalled = await rig.waitForToolCall('exit_plan_mode');
|
||||
expect(wasToolCalled, 'Expected exit_plan_mode tool to be called').toBe(
|
||||
true,
|
||||
);
|
||||
assertModelHasOutput(result);
|
||||
},
|
||||
});
|
||||
|
||||
evalTest('USUALLY_PASSES', {
|
||||
name: 'should allow file modification in plans directory when in plan mode',
|
||||
approvalMode: ApprovalMode.PLAN,
|
||||
params: {
|
||||
settings,
|
||||
},
|
||||
prompt: 'Create a plan for a new login feature.',
|
||||
assert: async (rig, result) => {
|
||||
await rig.waitForTelemetryReady();
|
||||
const toolLogs = rig.readToolLogs();
|
||||
|
||||
const writeCall = toolLogs.find(
|
||||
(log) => log.toolRequest.name === 'write_file',
|
||||
);
|
||||
|
||||
expect(
|
||||
writeCall,
|
||||
'Should attempt to modify a file in the plans directory when in plan mode',
|
||||
).toBeDefined();
|
||||
|
||||
if (writeCall) {
|
||||
const args = JSON.parse(writeCall.toolRequest.args);
|
||||
expect(args.file_path).toContain('.gemini/tmp');
|
||||
expect(args.file_path).toContain('/plans/');
|
||||
expect(args.file_path).toMatch(/\.md$/);
|
||||
}
|
||||
|
||||
assertModelHasOutput(result);
|
||||
},
|
||||
});
|
||||
});
|
||||
+260
-8
@@ -6,11 +6,16 @@
|
||||
|
||||
import { describe, expect } from 'vitest';
|
||||
import { evalTest } from './test-helper.js';
|
||||
import { validateModelOutput } from '../integration-tests/test-helper.js';
|
||||
import {
|
||||
assertModelHasOutput,
|
||||
checkModelOutputContent,
|
||||
} from '../integration-tests/test-helper.js';
|
||||
|
||||
describe('save_memory', () => {
|
||||
const TEST_PREFIX = 'Save memory test: ';
|
||||
const rememberingFavoriteColor = "Agent remembers user's favorite color";
|
||||
evalTest('ALWAYS_PASSES', {
|
||||
name: 'should be able to save to memory',
|
||||
name: rememberingFavoriteColor,
|
||||
params: {
|
||||
settings: { tools: { core: ['save_memory'] } },
|
||||
},
|
||||
@@ -18,13 +23,260 @@ describe('save_memory', () => {
|
||||
|
||||
what is my favorite color? tell me that and surround it with $ symbol`,
|
||||
assert: async (rig, result) => {
|
||||
const foundToolCall = await rig.waitForToolCall('save_memory');
|
||||
expect(
|
||||
foundToolCall,
|
||||
'Expected to find a save_memory tool call',
|
||||
).toBeTruthy();
|
||||
const wasToolCalled = await rig.waitForToolCall('save_memory');
|
||||
expect(wasToolCalled, 'Expected save_memory tool to be called').toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
validateModelOutput(result, 'blue', 'Save memory test');
|
||||
assertModelHasOutput(result);
|
||||
checkModelOutputContent(result, {
|
||||
expectedContent: 'blue',
|
||||
testName: `${TEST_PREFIX}${rememberingFavoriteColor}`,
|
||||
});
|
||||
},
|
||||
});
|
||||
const rememberingCommandRestrictions = 'Agent remembers command restrictions';
|
||||
evalTest('ALWAYS_PASSES', {
|
||||
name: rememberingCommandRestrictions,
|
||||
params: {
|
||||
settings: { tools: { core: ['save_memory'] } },
|
||||
},
|
||||
prompt: `I don't want you to ever run npm commands.`,
|
||||
assert: async (rig, result) => {
|
||||
const wasToolCalled = await rig.waitForToolCall('save_memory');
|
||||
expect(wasToolCalled, 'Expected save_memory tool to be called').toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
assertModelHasOutput(result);
|
||||
checkModelOutputContent(result, {
|
||||
expectedContent: [/not run npm commands|remember|ok/i],
|
||||
testName: `${TEST_PREFIX}${rememberingCommandRestrictions}`,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const rememberingWorkflow = 'Agent remembers workflow preferences';
|
||||
evalTest('ALWAYS_PASSES', {
|
||||
name: rememberingWorkflow,
|
||||
params: {
|
||||
settings: { tools: { core: ['save_memory'] } },
|
||||
},
|
||||
prompt: `I want you to always lint after building.`,
|
||||
assert: async (rig, result) => {
|
||||
const wasToolCalled = await rig.waitForToolCall('save_memory');
|
||||
expect(wasToolCalled, 'Expected save_memory tool to be called').toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
assertModelHasOutput(result);
|
||||
checkModelOutputContent(result, {
|
||||
expectedContent: [/always|ok|remember|will do/i],
|
||||
testName: `${TEST_PREFIX}${rememberingWorkflow}`,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const ignoringTemporaryInformation =
|
||||
'Agent ignores temporary conversation details';
|
||||
evalTest('ALWAYS_PASSES', {
|
||||
name: ignoringTemporaryInformation,
|
||||
params: {
|
||||
settings: { tools: { core: ['save_memory'] } },
|
||||
},
|
||||
prompt: `I'm going to get a coffee.`,
|
||||
assert: async (rig, result) => {
|
||||
await rig.waitForTelemetryReady();
|
||||
const wasToolCalled = rig
|
||||
.readToolLogs()
|
||||
.some((log) => log.toolRequest.name === 'save_memory');
|
||||
expect(
|
||||
wasToolCalled,
|
||||
'save_memory should not be called for temporary information',
|
||||
).toBe(false);
|
||||
|
||||
assertModelHasOutput(result);
|
||||
checkModelOutputContent(result, {
|
||||
testName: `${TEST_PREFIX}${ignoringTemporaryInformation}`,
|
||||
forbiddenContent: [/remember|will do/i],
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const rememberingPetName = "Agent remembers user's pet's name";
|
||||
evalTest('ALWAYS_PASSES', {
|
||||
name: rememberingPetName,
|
||||
params: {
|
||||
settings: { tools: { core: ['save_memory'] } },
|
||||
},
|
||||
prompt: `Please remember that my dog's name is Buddy.`,
|
||||
assert: async (rig, result) => {
|
||||
const wasToolCalled = await rig.waitForToolCall('save_memory');
|
||||
expect(wasToolCalled, 'Expected save_memory tool to be called').toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
assertModelHasOutput(result);
|
||||
checkModelOutputContent(result, {
|
||||
expectedContent: [/Buddy/i],
|
||||
testName: `${TEST_PREFIX}${rememberingPetName}`,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const rememberingCommandAlias = 'Agent remembers custom command aliases';
|
||||
evalTest('ALWAYS_PASSES', {
|
||||
name: rememberingCommandAlias,
|
||||
params: {
|
||||
settings: { tools: { core: ['save_memory'] } },
|
||||
},
|
||||
prompt: `When I say 'start server', you should run 'npm run dev'.`,
|
||||
assert: async (rig, result) => {
|
||||
const wasToolCalled = await rig.waitForToolCall('save_memory');
|
||||
expect(wasToolCalled, 'Expected save_memory tool to be called').toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
assertModelHasOutput(result);
|
||||
checkModelOutputContent(result, {
|
||||
expectedContent: [/npm run dev|start server|ok|remember|will do/i],
|
||||
testName: `${TEST_PREFIX}${rememberingCommandAlias}`,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const ignoringDbSchemaLocation =
|
||||
"Agent ignores workspace's database schema location";
|
||||
evalTest('ALWAYS_PASSES', {
|
||||
name: ignoringDbSchemaLocation,
|
||||
params: {
|
||||
settings: {
|
||||
tools: {
|
||||
core: [
|
||||
'save_memory',
|
||||
'list_directory',
|
||||
'read_file',
|
||||
'run_shell_command',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
prompt: `The database schema for this workspace is located in \`db/schema.sql\`.`,
|
||||
assert: async (rig, result) => {
|
||||
await rig.waitForTelemetryReady();
|
||||
const wasToolCalled = rig
|
||||
.readToolLogs()
|
||||
.some((log) => log.toolRequest.name === 'save_memory');
|
||||
expect(
|
||||
wasToolCalled,
|
||||
'save_memory should not be called for workspace-specific information',
|
||||
).toBe(false);
|
||||
|
||||
assertModelHasOutput(result);
|
||||
},
|
||||
});
|
||||
|
||||
const rememberingCodingStyle =
|
||||
"Agent remembers user's coding style preference";
|
||||
evalTest('ALWAYS_PASSES', {
|
||||
name: rememberingCodingStyle,
|
||||
params: {
|
||||
settings: { tools: { core: ['save_memory'] } },
|
||||
},
|
||||
prompt: `I prefer to use tabs instead of spaces for indentation.`,
|
||||
assert: async (rig, result) => {
|
||||
const wasToolCalled = await rig.waitForToolCall('save_memory');
|
||||
expect(wasToolCalled, 'Expected save_memory tool to be called').toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
assertModelHasOutput(result);
|
||||
checkModelOutputContent(result, {
|
||||
expectedContent: [/tabs instead of spaces|ok|remember|will do/i],
|
||||
testName: `${TEST_PREFIX}${rememberingCodingStyle}`,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const ignoringBuildArtifactLocation =
|
||||
'Agent ignores workspace build artifact location';
|
||||
evalTest('ALWAYS_PASSES', {
|
||||
name: ignoringBuildArtifactLocation,
|
||||
params: {
|
||||
settings: {
|
||||
tools: {
|
||||
core: [
|
||||
'save_memory',
|
||||
'list_directory',
|
||||
'read_file',
|
||||
'run_shell_command',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
prompt: `In this workspace, build artifacts are stored in the \`dist/artifacts\` directory.`,
|
||||
assert: async (rig, result) => {
|
||||
await rig.waitForTelemetryReady();
|
||||
const wasToolCalled = rig
|
||||
.readToolLogs()
|
||||
.some((log) => log.toolRequest.name === 'save_memory');
|
||||
expect(
|
||||
wasToolCalled,
|
||||
'save_memory should not be called for workspace-specific information',
|
||||
).toBe(false);
|
||||
|
||||
assertModelHasOutput(result);
|
||||
},
|
||||
});
|
||||
|
||||
const ignoringMainEntryPoint = "Agent ignores workspace's main entry point";
|
||||
evalTest('ALWAYS_PASSES', {
|
||||
name: ignoringMainEntryPoint,
|
||||
params: {
|
||||
settings: {
|
||||
tools: {
|
||||
core: [
|
||||
'save_memory',
|
||||
'list_directory',
|
||||
'read_file',
|
||||
'run_shell_command',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
prompt: `The main entry point for this workspace is \`src/index.js\`.`,
|
||||
assert: async (rig, result) => {
|
||||
await rig.waitForTelemetryReady();
|
||||
const wasToolCalled = rig
|
||||
.readToolLogs()
|
||||
.some((log) => log.toolRequest.name === 'save_memory');
|
||||
expect(
|
||||
wasToolCalled,
|
||||
'save_memory should not be called for workspace-specific information',
|
||||
).toBe(false);
|
||||
|
||||
assertModelHasOutput(result);
|
||||
},
|
||||
});
|
||||
|
||||
const rememberingBirthday = "Agent remembers user's birthday";
|
||||
evalTest('ALWAYS_PASSES', {
|
||||
name: rememberingBirthday,
|
||||
params: {
|
||||
settings: { tools: { core: ['save_memory'] } },
|
||||
},
|
||||
prompt: `My birthday is on June 15th.`,
|
||||
assert: async (rig, result) => {
|
||||
const wasToolCalled = await rig.waitForToolCall('save_memory');
|
||||
expect(wasToolCalled, 'Expected save_memory tool to be called').toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
assertModelHasOutput(result);
|
||||
checkModelOutputContent(result, {
|
||||
expectedContent: [/June 15th|ok|remember|will do/i],
|
||||
testName: `${TEST_PREFIX}${rememberingBirthday}`,
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, expect } from 'vitest';
|
||||
import { evalTest } from './test-helper.js';
|
||||
|
||||
describe('Shell Efficiency', () => {
|
||||
const getCommand = (call: any): string | undefined => {
|
||||
let args = call.toolRequest.args;
|
||||
if (typeof args === 'string') {
|
||||
try {
|
||||
args = JSON.parse(args);
|
||||
} catch (e) {
|
||||
// Ignore parse errors
|
||||
}
|
||||
}
|
||||
return typeof args === 'string' ? args : (args as any)['command'];
|
||||
};
|
||||
|
||||
evalTest('USUALLY_PASSES', {
|
||||
name: 'should use --silent/--quiet flags when installing packages',
|
||||
prompt: 'Install the "lodash" package using npm.',
|
||||
assert: async (rig) => {
|
||||
const toolCalls = rig.readToolLogs();
|
||||
const shellCalls = toolCalls.filter(
|
||||
(call) => call.toolRequest.name === 'run_shell_command',
|
||||
);
|
||||
|
||||
const hasEfficiencyFlag = shellCalls.some((call) => {
|
||||
const cmd = getCommand(call);
|
||||
return (
|
||||
cmd &&
|
||||
cmd.includes('npm install') &&
|
||||
(cmd.includes('--silent') ||
|
||||
cmd.includes('--quiet') ||
|
||||
cmd.includes('-q'))
|
||||
);
|
||||
});
|
||||
|
||||
expect(
|
||||
hasEfficiencyFlag,
|
||||
`Expected agent to use efficiency flags for npm install. Commands used: ${shellCalls
|
||||
.map(getCommand)
|
||||
.join(', ')}`,
|
||||
).toBe(true);
|
||||
},
|
||||
});
|
||||
|
||||
evalTest('USUALLY_PASSES', {
|
||||
name: 'should use --no-pager with git commands',
|
||||
prompt: 'Show the git log.',
|
||||
assert: async (rig) => {
|
||||
const toolCalls = rig.readToolLogs();
|
||||
const shellCalls = toolCalls.filter(
|
||||
(call) => call.toolRequest.name === 'run_shell_command',
|
||||
);
|
||||
|
||||
const hasNoPager = shellCalls.some((call) => {
|
||||
const cmd = getCommand(call);
|
||||
return cmd && cmd.includes('git') && cmd.includes('--no-pager');
|
||||
});
|
||||
|
||||
expect(
|
||||
hasNoPager,
|
||||
`Expected agent to use --no-pager with git. Commands used: ${shellCalls
|
||||
.map(getCommand)
|
||||
.join(', ')}`,
|
||||
).toBe(true);
|
||||
},
|
||||
});
|
||||
|
||||
evalTest('USUALLY_PASSES', {
|
||||
name: 'should NOT use efficiency flags when enableShellOutputEfficiency is disabled',
|
||||
params: {
|
||||
settings: {
|
||||
tools: {
|
||||
shell: {
|
||||
enableShellOutputEfficiency: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
prompt: 'Install the "lodash" package using npm.',
|
||||
assert: async (rig) => {
|
||||
const toolCalls = rig.readToolLogs();
|
||||
const shellCalls = toolCalls.filter(
|
||||
(call) => call.toolRequest.name === 'run_shell_command',
|
||||
);
|
||||
|
||||
const hasEfficiencyFlag = shellCalls.some((call) => {
|
||||
const cmd = getCommand(call);
|
||||
return (
|
||||
cmd &&
|
||||
cmd.includes('npm install') &&
|
||||
(cmd.includes('--silent') ||
|
||||
cmd.includes('--quiet') ||
|
||||
cmd.includes('-q'))
|
||||
);
|
||||
});
|
||||
|
||||
expect(
|
||||
hasEfficiencyFlag,
|
||||
'Agent used efficiency flags even though enableShellOutputEfficiency was disabled',
|
||||
).toBe(false);
|
||||
},
|
||||
});
|
||||
});
|
||||
+89
-12
@@ -7,9 +7,13 @@
|
||||
import { it } from 'vitest';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import crypto from 'node:crypto';
|
||||
import { execSync } from 'node:child_process';
|
||||
import { TestRig } from '@google/gemini-cli-test-utils';
|
||||
import { createUnauthorizedToolError } from '@google/gemini-cli-core';
|
||||
import {
|
||||
createUnauthorizedToolError,
|
||||
parseAgentMarkdown,
|
||||
} from '@google/gemini-cli-core';
|
||||
|
||||
export * from '@google/gemini-cli-test-utils';
|
||||
|
||||
@@ -34,14 +38,71 @@ export type EvalPolicy = 'ALWAYS_PASSES' | 'USUALLY_PASSES';
|
||||
export function evalTest(policy: EvalPolicy, evalCase: EvalCase) {
|
||||
const fn = async () => {
|
||||
const rig = new TestRig();
|
||||
const { logDir, sanitizedName } = await prepareLogDir(evalCase.name);
|
||||
const activityLogFile = path.join(logDir, `${sanitizedName}.jsonl`);
|
||||
const logFile = path.join(logDir, `${sanitizedName}.log`);
|
||||
let isSuccess = false;
|
||||
try {
|
||||
rig.setup(evalCase.name, evalCase.params);
|
||||
|
||||
// Symlink node modules to reduce the amount of time needed to
|
||||
// bootstrap test projects.
|
||||
const rootNodeModules = path.join(process.cwd(), 'node_modules');
|
||||
const testNodeModules = path.join(rig.testDir || '', 'node_modules');
|
||||
if (fs.existsSync(rootNodeModules) && !fs.existsSync(testNodeModules)) {
|
||||
fs.symlinkSync(rootNodeModules, testNodeModules, 'dir');
|
||||
}
|
||||
|
||||
if (evalCase.files) {
|
||||
const acknowledgedAgents: Record<string, Record<string, string>> = {};
|
||||
const projectRoot = fs.realpathSync(rig.testDir!);
|
||||
|
||||
for (const [filePath, content] of Object.entries(evalCase.files)) {
|
||||
const fullPath = path.join(rig.testDir!, filePath);
|
||||
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
||||
fs.writeFileSync(fullPath, content);
|
||||
|
||||
// If it's an agent file, calculate hash for acknowledgement
|
||||
if (
|
||||
filePath.startsWith('.gemini/agents/') &&
|
||||
filePath.endsWith('.md')
|
||||
) {
|
||||
const hash = crypto
|
||||
.createHash('sha256')
|
||||
.update(content)
|
||||
.digest('hex');
|
||||
|
||||
try {
|
||||
const agentDefs = await parseAgentMarkdown(fullPath, content);
|
||||
if (agentDefs.length > 0) {
|
||||
const agentName = agentDefs[0].name;
|
||||
if (!acknowledgedAgents[projectRoot]) {
|
||||
acknowledgedAgents[projectRoot] = {};
|
||||
}
|
||||
acknowledgedAgents[projectRoot][agentName] = hash;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`Failed to parse agent for test acknowledgement: ${filePath}`,
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write acknowledged_agents.json to the home directory
|
||||
if (Object.keys(acknowledgedAgents).length > 0) {
|
||||
const ackPath = path.join(
|
||||
rig.homeDir!,
|
||||
'.gemini',
|
||||
'acknowledgments',
|
||||
'agents.json',
|
||||
);
|
||||
fs.mkdirSync(path.dirname(ackPath), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
ackPath,
|
||||
JSON.stringify(acknowledgedAgents, null, 2),
|
||||
);
|
||||
}
|
||||
|
||||
const execOptions = { cwd: rig.testDir!, stdio: 'inherit' as const };
|
||||
@@ -62,6 +123,10 @@ export function evalTest(policy: EvalPolicy, evalCase: EvalCase) {
|
||||
const result = await rig.run({
|
||||
args: evalCase.prompt,
|
||||
approvalMode: evalCase.approvalMode ?? 'yolo',
|
||||
timeout: evalCase.timeout,
|
||||
env: {
|
||||
GEMINI_CLI_ACTIVITY_LOG_TARGET: activityLogFile,
|
||||
},
|
||||
});
|
||||
|
||||
const unauthorizedErrorPrefix =
|
||||
@@ -73,9 +138,21 @@ export function evalTest(policy: EvalPolicy, evalCase: EvalCase) {
|
||||
}
|
||||
|
||||
await evalCase.assert(rig, result);
|
||||
isSuccess = true;
|
||||
} finally {
|
||||
await logToFile(
|
||||
evalCase.name,
|
||||
if (isSuccess) {
|
||||
await fs.promises.unlink(activityLogFile).catch((err) => {
|
||||
if (err.code !== 'ENOENT') throw err;
|
||||
});
|
||||
}
|
||||
|
||||
if (rig._lastRunStderr) {
|
||||
const stderrFile = path.join(logDir, `${sanitizedName}.stderr.log`);
|
||||
await fs.promises.writeFile(stderrFile, rig._lastRunStderr);
|
||||
}
|
||||
|
||||
await fs.promises.writeFile(
|
||||
logFile,
|
||||
JSON.stringify(rig.readToolLogs(), null, 2),
|
||||
);
|
||||
await rig.cleanup();
|
||||
@@ -85,23 +162,23 @@ export function evalTest(policy: EvalPolicy, evalCase: EvalCase) {
|
||||
if (policy === 'USUALLY_PASSES' && !process.env['RUN_EVALS']) {
|
||||
it.skip(evalCase.name, fn);
|
||||
} else {
|
||||
it(evalCase.name, fn);
|
||||
it(evalCase.name, fn, evalCase.timeout);
|
||||
}
|
||||
}
|
||||
|
||||
async function prepareLogDir(name: string) {
|
||||
const logDir = path.resolve(process.cwd(), 'evals/logs');
|
||||
await fs.promises.mkdir(logDir, { recursive: true });
|
||||
const sanitizedName = name.replace(/[^a-z0-9]/gi, '_').toLowerCase();
|
||||
return { logDir, sanitizedName };
|
||||
}
|
||||
|
||||
export interface EvalCase {
|
||||
name: string;
|
||||
params?: Record<string, any>;
|
||||
prompt: string;
|
||||
timeout?: number;
|
||||
files?: Record<string, string>;
|
||||
approvalMode?: 'default' | 'auto_edit' | 'yolo' | 'plan';
|
||||
assert: (rig: TestRig, result: string) => Promise<void>;
|
||||
}
|
||||
|
||||
async function logToFile(name: string, content: string) {
|
||||
const logDir = 'evals/logs';
|
||||
await fs.promises.mkdir(logDir, { recursive: true });
|
||||
const sanitizedName = name.replace(/[^a-z0-9]/gi, '_').toLowerCase();
|
||||
const logFile = `${logDir}/${sanitizedName}.log`;
|
||||
await fs.promises.writeFile(logFile, content);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, expect } from 'vitest';
|
||||
import { evalTest } from './test-helper.js';
|
||||
|
||||
describe('validation_fidelity', () => {
|
||||
evalTest('ALWAYS_PASSES', {
|
||||
name: 'should perform exhaustive validation autonomously when guided by system instructions',
|
||||
files: {
|
||||
'src/types.ts': `
|
||||
export interface LogEntry {
|
||||
level: 'info' | 'warn' | 'error';
|
||||
message: string;
|
||||
}
|
||||
`,
|
||||
'src/logger.ts': `
|
||||
import { LogEntry } from './types.js';
|
||||
|
||||
export function formatLog(entry: LogEntry): string {
|
||||
return \`[\${entry.level.toUpperCase()}] \${entry.message}\`;
|
||||
}
|
||||
`,
|
||||
'src/logger.test.ts': `
|
||||
import { expect, test } from 'vitest';
|
||||
import { formatLog } from './logger.js';
|
||||
import { LogEntry } from './types.js';
|
||||
|
||||
test('formats log correctly', () => {
|
||||
const entry: LogEntry = { level: 'info', message: 'test message' };
|
||||
expect(formatLog(entry)).toBe('[INFO] test message');
|
||||
});
|
||||
`,
|
||||
'package.json': JSON.stringify({
|
||||
name: 'test-project',
|
||||
type: 'module',
|
||||
scripts: {
|
||||
test: 'vitest run',
|
||||
build: 'tsc --noEmit',
|
||||
},
|
||||
}),
|
||||
'tsconfig.json': JSON.stringify({
|
||||
compilerOptions: {
|
||||
target: 'ESNext',
|
||||
module: 'ESNext',
|
||||
moduleResolution: 'node',
|
||||
strict: true,
|
||||
esModuleInterop: true,
|
||||
skipLibCheck: true,
|
||||
forceConsistentCasingInFileNames: true,
|
||||
},
|
||||
}),
|
||||
},
|
||||
prompt:
|
||||
"Refactor the 'LogEntry' interface in 'src/types.ts' to rename the 'message' field to 'payload'.",
|
||||
timeout: 600000,
|
||||
assert: async (rig) => {
|
||||
// The goal of this eval is to see if the agent realizes it needs to update usages
|
||||
// AND run 'npm run build' or 'tsc' autonomously to ensure project-wide structural integrity.
|
||||
|
||||
const toolLogs = rig.readToolLogs();
|
||||
const shellCalls = toolLogs.filter(
|
||||
(log) => log.toolRequest.name === 'run_shell_command',
|
||||
);
|
||||
|
||||
const hasBuildOrTsc = shellCalls.some((log) => {
|
||||
const cmd = JSON.parse(log.toolRequest.args).command.toLowerCase();
|
||||
return (
|
||||
cmd.includes('npm run build') ||
|
||||
cmd.includes('tsc') ||
|
||||
cmd.includes('typecheck') ||
|
||||
cmd.includes('npm run verify')
|
||||
);
|
||||
});
|
||||
|
||||
expect(
|
||||
hasBuildOrTsc,
|
||||
'Expected the agent to autonomously run a build or type-check command to verify the refactoring',
|
||||
).toBe(true);
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, expect } from 'vitest';
|
||||
import { evalTest } from './test-helper.js';
|
||||
|
||||
describe('validation_fidelity_pre_existing_errors', () => {
|
||||
evalTest('USUALLY_PASSES', {
|
||||
name: 'should handle pre-existing project errors gracefully during validation',
|
||||
files: {
|
||||
'src/math.ts': `
|
||||
export function add(a: number, b: number): number {
|
||||
return a + b;
|
||||
}
|
||||
`,
|
||||
'src/index.ts': `
|
||||
import { add } from './math.js';
|
||||
console.log(add(1, 2));
|
||||
`,
|
||||
'src/utils.ts': `
|
||||
export function multiply(a: number, b: number): number {
|
||||
return a * c; // 'c' is not defined - PRE-EXISTING ERROR
|
||||
}
|
||||
`,
|
||||
'package.json': JSON.stringify({
|
||||
name: 'test-project',
|
||||
type: 'module',
|
||||
scripts: {
|
||||
test: 'vitest run',
|
||||
build: 'tsc --noEmit',
|
||||
},
|
||||
}),
|
||||
'tsconfig.json': JSON.stringify({
|
||||
compilerOptions: {
|
||||
target: 'ESNext',
|
||||
module: 'ESNext',
|
||||
moduleResolution: 'node',
|
||||
strict: true,
|
||||
esModuleInterop: true,
|
||||
skipLibCheck: true,
|
||||
forceConsistentCasingInFileNames: true,
|
||||
},
|
||||
}),
|
||||
},
|
||||
prompt: "In src/math.ts, rename the 'add' function to 'sum'.",
|
||||
timeout: 600000,
|
||||
assert: async (rig) => {
|
||||
const toolLogs = rig.readToolLogs();
|
||||
const replaceCalls = toolLogs.filter(
|
||||
(log) => log.toolRequest.name === 'replace',
|
||||
);
|
||||
|
||||
// Verify it did the work in math.ts
|
||||
const mathRefactor = replaceCalls.some((log) => {
|
||||
const args = JSON.parse(log.toolRequest.args);
|
||||
return (
|
||||
args.file_path.endsWith('src/math.ts') &&
|
||||
args.new_string.includes('sum')
|
||||
);
|
||||
});
|
||||
expect(mathRefactor, 'Agent should have refactored math.ts').toBe(true);
|
||||
|
||||
const shellCalls = toolLogs.filter(
|
||||
(log) => log.toolRequest.name === 'run_shell_command',
|
||||
);
|
||||
const ranValidation = shellCalls.some((log) => {
|
||||
const cmd = JSON.parse(log.toolRequest.args).command.toLowerCase();
|
||||
return cmd.includes('build') || cmd.includes('tsc');
|
||||
});
|
||||
|
||||
expect(ranValidation, 'Agent should have attempted validation').toBe(
|
||||
true,
|
||||
);
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { TestRig } from './test-helper.js';
|
||||
import { spawn, ChildProcess } from 'node:child_process';
|
||||
import { join, resolve } from 'node:path';
|
||||
import { writeFileSync, mkdirSync } from 'node:fs';
|
||||
import { Writable, Readable } from 'node:stream';
|
||||
import { env } from 'node:process';
|
||||
import * as acp from '@agentclientprotocol/sdk';
|
||||
|
||||
const sandboxEnv = env['GEMINI_SANDBOX'];
|
||||
const itMaybe = sandboxEnv && sandboxEnv !== 'false' ? it.skip : it;
|
||||
|
||||
class MockClient implements acp.Client {
|
||||
updates: acp.SessionNotification[] = [];
|
||||
sessionUpdate = async (params: acp.SessionNotification) => {
|
||||
this.updates.push(params);
|
||||
};
|
||||
requestPermission = async (): Promise<acp.RequestPermissionResponse> => {
|
||||
throw new Error('unexpected');
|
||||
};
|
||||
}
|
||||
|
||||
describe('ACP Environment and Auth', () => {
|
||||
let rig: TestRig;
|
||||
let child: ChildProcess | undefined;
|
||||
|
||||
beforeEach(() => {
|
||||
rig = new TestRig();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
child?.kill();
|
||||
child = undefined;
|
||||
await rig.cleanup();
|
||||
});
|
||||
|
||||
itMaybe(
|
||||
'should load .env from project directory and use the provided API key',
|
||||
async () => {
|
||||
rig.setup('acp-env-loading');
|
||||
|
||||
// Create a project directory with a .env file containing a recognizable invalid key
|
||||
const projectDir = resolve(join(rig.testDir!, 'project'));
|
||||
mkdirSync(projectDir, { recursive: true });
|
||||
writeFileSync(
|
||||
join(projectDir, '.env'),
|
||||
'GEMINI_API_KEY=test-key-from-env\n',
|
||||
);
|
||||
|
||||
const bundlePath = join(import.meta.dirname, '..', 'bundle/gemini.js');
|
||||
|
||||
child = spawn('node', [bundlePath, '--experimental-acp'], {
|
||||
cwd: rig.homeDir!,
|
||||
stdio: ['pipe', 'pipe', 'inherit'],
|
||||
env: {
|
||||
...process.env,
|
||||
GEMINI_CLI_HOME: rig.homeDir!,
|
||||
GEMINI_API_KEY: undefined,
|
||||
VERBOSE: 'true',
|
||||
},
|
||||
});
|
||||
|
||||
const input = Writable.toWeb(child.stdin!);
|
||||
const output = Readable.toWeb(
|
||||
child.stdout!,
|
||||
) as ReadableStream<Uint8Array>;
|
||||
const testClient = new MockClient();
|
||||
const stream = acp.ndJsonStream(input, output);
|
||||
const connection = new acp.ClientSideConnection(() => testClient, stream);
|
||||
|
||||
await connection.initialize({
|
||||
protocolVersion: acp.PROTOCOL_VERSION,
|
||||
clientCapabilities: {
|
||||
fs: { readTextFile: false, writeTextFile: false },
|
||||
},
|
||||
});
|
||||
|
||||
// 1. newSession should succeed because it finds the key in .env
|
||||
const { sessionId } = await connection.newSession({
|
||||
cwd: projectDir,
|
||||
mcpServers: [],
|
||||
});
|
||||
|
||||
expect(sessionId).toBeDefined();
|
||||
|
||||
// 2. prompt should fail because the key is invalid,
|
||||
// but the error should come from the API, not the internal auth check.
|
||||
await expect(
|
||||
connection.prompt({
|
||||
sessionId,
|
||||
prompt: [{ type: 'text', text: 'hello' }],
|
||||
}),
|
||||
).rejects.toSatisfy((error: unknown) => {
|
||||
const acpError = error as acp.RequestError;
|
||||
const errorData = acpError.data as
|
||||
| { error?: { message?: string } }
|
||||
| undefined;
|
||||
const message = String(errorData?.error?.message || acpError.message);
|
||||
// It should NOT be our internal "Authentication required" message
|
||||
expect(message).not.toContain('Authentication required');
|
||||
// It SHOULD be an API error mentioning the invalid key
|
||||
expect(message).toContain('API key not valid');
|
||||
return true;
|
||||
});
|
||||
|
||||
child.stdin!.end();
|
||||
},
|
||||
);
|
||||
|
||||
itMaybe(
|
||||
'should fail with authRequired when no API key is found',
|
||||
async () => {
|
||||
rig.setup('acp-auth-failure');
|
||||
|
||||
const bundlePath = join(import.meta.dirname, '..', 'bundle/gemini.js');
|
||||
|
||||
child = spawn('node', [bundlePath, '--experimental-acp'], {
|
||||
cwd: rig.homeDir!,
|
||||
stdio: ['pipe', 'pipe', 'inherit'],
|
||||
env: {
|
||||
...process.env,
|
||||
GEMINI_CLI_HOME: rig.homeDir!,
|
||||
GEMINI_API_KEY: undefined,
|
||||
VERBOSE: 'true',
|
||||
},
|
||||
});
|
||||
|
||||
const input = Writable.toWeb(child.stdin!);
|
||||
const output = Readable.toWeb(
|
||||
child.stdout!,
|
||||
) as ReadableStream<Uint8Array>;
|
||||
const testClient = new MockClient();
|
||||
const stream = acp.ndJsonStream(input, output);
|
||||
const connection = new acp.ClientSideConnection(() => testClient, stream);
|
||||
|
||||
await connection.initialize({
|
||||
protocolVersion: acp.PROTOCOL_VERSION,
|
||||
clientCapabilities: {
|
||||
fs: { readTextFile: false, writeTextFile: false },
|
||||
},
|
||||
});
|
||||
|
||||
await expect(
|
||||
connection.newSession({
|
||||
cwd: resolve(rig.testDir!),
|
||||
mcpServers: [],
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
message: expect.stringContaining(
|
||||
'Gemini API key is missing or not configured.',
|
||||
),
|
||||
});
|
||||
|
||||
child.stdin!.end();
|
||||
},
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,155 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import * as fs from 'node:fs/promises';
|
||||
import * as path from 'node:path';
|
||||
import * as os from 'node:os';
|
||||
import { GitService, Storage } from '@google/gemini-cli-core';
|
||||
|
||||
describe('Checkpointing Integration', () => {
|
||||
let tmpDir: string;
|
||||
let projectRoot: string;
|
||||
let fakeHome: string;
|
||||
let originalEnv: NodeJS.ProcessEnv;
|
||||
|
||||
beforeEach(async () => {
|
||||
tmpDir = await fs.mkdtemp(
|
||||
path.join(os.tmpdir(), 'gemini-checkpoint-test-'),
|
||||
);
|
||||
projectRoot = path.join(tmpDir, 'project');
|
||||
fakeHome = path.join(tmpDir, 'home');
|
||||
|
||||
await fs.mkdir(projectRoot, { recursive: true });
|
||||
await fs.mkdir(fakeHome, { recursive: true });
|
||||
|
||||
// Save original env
|
||||
originalEnv = { ...process.env };
|
||||
|
||||
// Simulate environment with NO global gitconfig
|
||||
process.env['HOME'] = fakeHome;
|
||||
delete process.env['GIT_CONFIG_GLOBAL'];
|
||||
delete process.env['GIT_CONFIG_SYSTEM'];
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
// Restore env
|
||||
process.env = originalEnv;
|
||||
|
||||
// Cleanup
|
||||
try {
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
} catch (e) {
|
||||
console.error('Failed to cleanup temp dir', e);
|
||||
}
|
||||
});
|
||||
|
||||
it('should successfully create and restore snapshots without global git config', async () => {
|
||||
const storage = new Storage(projectRoot);
|
||||
const gitService = new GitService(projectRoot, storage);
|
||||
|
||||
// 1. Initialize
|
||||
await gitService.initialize();
|
||||
|
||||
// Verify system config empty file creation
|
||||
// We need to access getHistoryDir logic or replicate it.
|
||||
// Since we don't have access to private getHistoryDir, we can infer it or just trust the functional test.
|
||||
|
||||
// 2. Create initial state
|
||||
await fs.writeFile(path.join(projectRoot, 'file1.txt'), 'version 1');
|
||||
await fs.writeFile(path.join(projectRoot, 'file2.txt'), 'permanent file');
|
||||
|
||||
// 3. Create Snapshot
|
||||
const snapshotHash = await gitService.createFileSnapshot('Checkpoint 1');
|
||||
expect(snapshotHash).toBeDefined();
|
||||
|
||||
// 4. Modify files
|
||||
await fs.writeFile(
|
||||
path.join(projectRoot, 'file1.txt'),
|
||||
'version 2 (BAD CHANGE)',
|
||||
);
|
||||
await fs.writeFile(
|
||||
path.join(projectRoot, 'file3.txt'),
|
||||
'new file (SHOULD BE GONE)',
|
||||
);
|
||||
await fs.rm(path.join(projectRoot, 'file2.txt'));
|
||||
|
||||
// 5. Restore
|
||||
await gitService.restoreProjectFromSnapshot(snapshotHash);
|
||||
|
||||
// 6. Verify state
|
||||
const file1Content = await fs.readFile(
|
||||
path.join(projectRoot, 'file1.txt'),
|
||||
'utf-8',
|
||||
);
|
||||
expect(file1Content).toBe('version 1');
|
||||
|
||||
const file2Exists = await fs
|
||||
.stat(path.join(projectRoot, 'file2.txt'))
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
expect(file2Exists).toBe(true);
|
||||
const file2Content = await fs.readFile(
|
||||
path.join(projectRoot, 'file2.txt'),
|
||||
'utf-8',
|
||||
);
|
||||
expect(file2Content).toBe('permanent file');
|
||||
|
||||
const file3Exists = await fs
|
||||
.stat(path.join(projectRoot, 'file3.txt'))
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
expect(file3Exists).toBe(false);
|
||||
});
|
||||
|
||||
it('should ignore user global git config and use isolated identity', async () => {
|
||||
// 1. Create a fake global gitconfig with a specific user
|
||||
const globalConfigPath = path.join(fakeHome, '.gitconfig');
|
||||
const globalConfigContent = `[user]
|
||||
name = Global User
|
||||
email = global@example.com
|
||||
`;
|
||||
await fs.writeFile(globalConfigPath, globalConfigContent);
|
||||
|
||||
// Point HOME to fakeHome so git picks up this global config (if we didn't isolate it)
|
||||
process.env['HOME'] = fakeHome;
|
||||
// Ensure GIT_CONFIG_GLOBAL is NOT set for the process initially,
|
||||
// so it would default to HOME/.gitconfig if GitService didn't override it.
|
||||
delete process.env['GIT_CONFIG_GLOBAL'];
|
||||
|
||||
const storage = new Storage(projectRoot);
|
||||
const gitService = new GitService(projectRoot, storage);
|
||||
|
||||
await gitService.initialize();
|
||||
|
||||
// 2. Create a file and snapshot
|
||||
await fs.writeFile(path.join(projectRoot, 'test.txt'), 'content');
|
||||
await gitService.createFileSnapshot('Snapshot with global config present');
|
||||
|
||||
// 3. Verify the commit author in the shadow repo
|
||||
const historyDir = storage.getHistoryDir();
|
||||
|
||||
const { execFileSync } = await import('node:child_process');
|
||||
|
||||
const logOutput = execFileSync(
|
||||
'git',
|
||||
['log', '-1', '--pretty=format:%an <%ae>'],
|
||||
{
|
||||
cwd: historyDir,
|
||||
env: {
|
||||
...process.env,
|
||||
GIT_DIR: path.join(historyDir, '.git'),
|
||||
GIT_CONFIG_GLOBAL: path.join(historyDir, '.gitconfig'),
|
||||
GIT_CONFIG_SYSTEM: path.join(historyDir, '.gitconfig_system_empty'),
|
||||
},
|
||||
encoding: 'utf-8',
|
||||
},
|
||||
);
|
||||
|
||||
expect(logOutput).toBe('Gemini CLI <gemini-cli@google.com>');
|
||||
expect(logOutput).not.toContain('Global User');
|
||||
});
|
||||
});
|
||||
@@ -7,7 +7,12 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { existsSync } from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
|
||||
import {
|
||||
TestRig,
|
||||
printDebugInfo,
|
||||
assertModelHasOutput,
|
||||
checkModelOutputContent,
|
||||
} from './test-helper.js';
|
||||
|
||||
describe('file-system', () => {
|
||||
let rig: TestRig;
|
||||
@@ -43,8 +48,11 @@ describe('file-system', () => {
|
||||
'Expected to find a read_file tool call',
|
||||
).toBeTruthy();
|
||||
|
||||
// Validate model output - will throw if no output, warn if missing expected content
|
||||
validateModelOutput(result, 'hello world', 'File read test');
|
||||
assertModelHasOutput(result);
|
||||
checkModelOutputContent(result, {
|
||||
expectedContent: 'hello world',
|
||||
testName: 'File read test',
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to write a file', async () => {
|
||||
@@ -74,8 +82,8 @@ describe('file-system', () => {
|
||||
'Expected to find a write_file, edit, or replace tool call',
|
||||
).toBeTruthy();
|
||||
|
||||
// Validate model output - will throw if no output
|
||||
validateModelOutput(result, null, 'File write test');
|
||||
assertModelHasOutput(result);
|
||||
checkModelOutputContent(result, { testName: 'File write test' });
|
||||
|
||||
const fileContent = rig.readFile('test.txt');
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import { mkdir, readdir, rm } from 'node:fs/promises';
|
||||
import { join, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { canUseRipgrep } from '../packages/core/src/tools/ripGrep.js';
|
||||
import { disableMouseTracking } from '@google/gemini-cli-core';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const rootDir = join(__dirname, '..');
|
||||
@@ -72,6 +73,11 @@ export async function setup() {
|
||||
}
|
||||
|
||||
export async function teardown() {
|
||||
// Disable mouse tracking
|
||||
if (process.stdout.isTTY) {
|
||||
disableMouseTracking();
|
||||
}
|
||||
|
||||
// Cleanup the test run directory unless KEEP_OUTPUT is set
|
||||
if (process.env['KEEP_OUTPUT'] !== 'true' && runDir) {
|
||||
try {
|
||||
|
||||
@@ -6,7 +6,12 @@
|
||||
|
||||
import { WEB_SEARCH_TOOL_NAME } from '../packages/core/src/tools/tool-names.js';
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
|
||||
import {
|
||||
TestRig,
|
||||
printDebugInfo,
|
||||
assertModelHasOutput,
|
||||
checkModelOutputContent,
|
||||
} from './test-helper.js';
|
||||
|
||||
describe('web search tool', () => {
|
||||
let rig: TestRig;
|
||||
@@ -68,12 +73,11 @@ describe('web search tool', () => {
|
||||
`Expected to find a call to ${WEB_SEARCH_TOOL_NAME}`,
|
||||
).toBeTruthy();
|
||||
|
||||
// Validate model output - will throw if no output, warn if missing expected content
|
||||
const hasExpectedContent = validateModelOutput(
|
||||
result,
|
||||
['weather', 'london'],
|
||||
'Google web search test',
|
||||
);
|
||||
assertModelHasOutput(result);
|
||||
const hasExpectedContent = checkModelOutputContent(result, {
|
||||
expectedContent: ['weather', 'london'],
|
||||
testName: 'Google web search test',
|
||||
});
|
||||
|
||||
// If content was missing, log the search queries used
|
||||
if (!hasExpectedContent) {
|
||||
|
||||
@@ -484,9 +484,19 @@ console.log(JSON.stringify({
|
||||
'hooks-system.before-tool-selection.responses',
|
||||
),
|
||||
});
|
||||
// Create inline hook command (works on both Unix and Windows)
|
||||
const hookCommand =
|
||||
"node -e \"console.log(JSON.stringify({hookSpecificOutput: {hookEventName: 'BeforeToolSelection', toolConfig: {mode: 'ANY', allowedFunctionNames: ['read_file', 'run_shell_command']}}}))\"";
|
||||
|
||||
// Write hook script to file (inline node -e has quoting issues on Windows)
|
||||
const hookScript = `console.log(JSON.stringify({
|
||||
hookSpecificOutput: {
|
||||
hookEventName: 'BeforeToolSelection',
|
||||
toolConfig: {
|
||||
mode: 'ANY',
|
||||
allowedFunctionNames: ['read_file', 'run_shell_command']
|
||||
}
|
||||
}
|
||||
}));`;
|
||||
const scriptPath = join(rig.testDir!, 'before_tool_selection_hook.cjs');
|
||||
writeFileSync(scriptPath, hookScript);
|
||||
|
||||
rig.setup('should modify tool selection with BeforeToolSelection hooks', {
|
||||
settings: {
|
||||
@@ -500,7 +510,7 @@ console.log(JSON.stringify({
|
||||
hooks: [
|
||||
{
|
||||
type: 'command',
|
||||
command: hookCommand,
|
||||
command: `node "${scriptPath.replace(/\\/g, '/')}"`,
|
||||
timeout: 5000,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -9,7 +9,8 @@ import {
|
||||
TestRig,
|
||||
poll,
|
||||
printDebugInfo,
|
||||
validateModelOutput,
|
||||
assertModelHasOutput,
|
||||
checkModelOutputContent,
|
||||
} from './test-helper.js';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
@@ -68,7 +69,10 @@ describe('list_directory', () => {
|
||||
throw e;
|
||||
}
|
||||
|
||||
// Validate model output - will throw if no output, warn if missing expected content
|
||||
validateModelOutput(result, ['file1.txt', 'subdir'], 'List directory test');
|
||||
assertModelHasOutput(result);
|
||||
checkModelOutputContent(result, {
|
||||
expectedContent: ['file1.txt', 'subdir'],
|
||||
testName: 'List directory test',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,7 +5,12 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
|
||||
import {
|
||||
TestRig,
|
||||
printDebugInfo,
|
||||
assertModelHasOutput,
|
||||
checkModelOutputContent,
|
||||
} from './test-helper.js';
|
||||
|
||||
describe('read_many_files', () => {
|
||||
let rig: TestRig;
|
||||
@@ -50,7 +55,7 @@ describe('read_many_files', () => {
|
||||
'Expected to find either read_many_files or multiple read_file tool calls',
|
||||
).toBeTruthy();
|
||||
|
||||
// Validate model output - will throw if no output
|
||||
validateModelOutput(result, null, 'Read many files test');
|
||||
assertModelHasOutput(result);
|
||||
checkModelOutputContent(result, { testName: 'Read many files test' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Session started."}],"role":"model"},"finishReason":"STOP","index":0}]}]}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { TestRig } from './test-helper.js';
|
||||
import * as path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
describe('resume-repro', () => {
|
||||
let rig: TestRig;
|
||||
|
||||
beforeEach(() => {
|
||||
rig = new TestRig();
|
||||
});
|
||||
|
||||
afterEach(async () => await rig.cleanup());
|
||||
|
||||
it('should be able to resume a session without "Storage must be initialized before use"', async () => {
|
||||
const responsesPath = path.join(__dirname, 'resume_repro.responses');
|
||||
await rig.setup('should be able to resume a session', {
|
||||
fakeResponsesPath: responsesPath,
|
||||
});
|
||||
|
||||
// 1. First run to create a session
|
||||
await rig.run({
|
||||
args: 'hello',
|
||||
});
|
||||
|
||||
// 2. Second run with --resume latest
|
||||
// This should NOT fail with "Storage must be initialized before use"
|
||||
const result = await rig.run({
|
||||
args: ['--resume', 'latest', 'continue'],
|
||||
});
|
||||
|
||||
expect(result).toContain('Session started');
|
||||
});
|
||||
});
|
||||
@@ -11,6 +11,7 @@ import * as os from 'node:os';
|
||||
import { RipGrepTool } from '../packages/core/src/tools/ripGrep.js';
|
||||
import { Config } from '../packages/core/src/config/config.js';
|
||||
import { WorkspaceContext } from '../packages/core/src/utils/workspaceContext.js';
|
||||
import { createMockMessageBus } from '../packages/core/src/test-utils/mock-message-bus.js';
|
||||
|
||||
// Mock Config to provide necessary context
|
||||
class MockConfig {
|
||||
@@ -28,9 +29,25 @@ class MockConfig {
|
||||
return true;
|
||||
}
|
||||
|
||||
getFileFilteringRespectGitIgnore() {
|
||||
return true;
|
||||
}
|
||||
|
||||
getFileFilteringRespectGeminiIgnore() {
|
||||
return true;
|
||||
}
|
||||
|
||||
getFileFilteringOptions() {
|
||||
return {
|
||||
respectGitIgnore: true,
|
||||
respectGeminiIgnore: true,
|
||||
customIgnoreFilePaths: [],
|
||||
};
|
||||
}
|
||||
|
||||
validatePathAccess() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
describe('ripgrep-real-direct', () => {
|
||||
@@ -50,7 +67,7 @@ describe('ripgrep-real-direct', () => {
|
||||
await fs.writeFile(path.join(tempDir, 'file3.txt'), 'goodbye moon\n');
|
||||
|
||||
const config = new MockConfig(tempDir) as unknown as Config;
|
||||
tool = new RipGrepTool(config);
|
||||
tool = new RipGrepTool(config, createMockMessageBus());
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
@@ -92,4 +109,24 @@ describe('ripgrep-real-direct', () => {
|
||||
expect(result.llmContent).toContain('script.js');
|
||||
expect(result.llmContent).not.toContain('file1.txt');
|
||||
});
|
||||
|
||||
it('should support context parameters', async () => {
|
||||
// Create a file with multiple lines
|
||||
await fs.writeFile(
|
||||
path.join(tempDir, 'context.txt'),
|
||||
'line1\nline2\nline3 match\nline4\nline5\n',
|
||||
);
|
||||
|
||||
const invocation = tool.build({
|
||||
pattern: 'match',
|
||||
context: 1,
|
||||
});
|
||||
const result = await invocation.execute(new AbortController().signal);
|
||||
|
||||
expect(result.llmContent).toContain('Found 1 match');
|
||||
expect(result.llmContent).toContain('context.txt');
|
||||
expect(result.llmContent).toContain('L2- line2');
|
||||
expect(result.llmContent).toContain('L3: line3 match');
|
||||
expect(result.llmContent).toContain('L4- line4');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,7 +5,12 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
|
||||
import {
|
||||
TestRig,
|
||||
printDebugInfo,
|
||||
assertModelHasOutput,
|
||||
checkModelOutputContent,
|
||||
} from './test-helper.js';
|
||||
import { getShellConfiguration } from '../packages/core/src/utils/shell-utils.js';
|
||||
|
||||
const { shell } = getShellConfiguration();
|
||||
@@ -115,13 +120,11 @@ describe('run_shell_command', () => {
|
||||
'Expected to find a run_shell_command tool call',
|
||||
).toBeTruthy();
|
||||
|
||||
// Validate model output - will throw if no output, warn if missing expected content
|
||||
// Model often reports exit code instead of showing output
|
||||
validateModelOutput(
|
||||
result,
|
||||
['hello-world', 'exit code 0'],
|
||||
'Shell command test',
|
||||
);
|
||||
assertModelHasOutput(result);
|
||||
checkModelOutputContent(result, {
|
||||
expectedContent: ['hello-world', 'exit code 0'],
|
||||
testName: 'Shell command test',
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to run a shell command via stdin', async () => {
|
||||
@@ -149,8 +152,11 @@ describe('run_shell_command', () => {
|
||||
'Expected to find a run_shell_command tool call',
|
||||
).toBeTruthy();
|
||||
|
||||
// Validate model output - will throw if no output, warn if missing expected content
|
||||
validateModelOutput(result, 'test-stdin', 'Shell command stdin test');
|
||||
assertModelHasOutput(result);
|
||||
checkModelOutputContent(result, {
|
||||
expectedContent: 'test-stdin',
|
||||
testName: 'Shell command stdin test',
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('should run allowed sub-command in non-interactive mode', async () => {
|
||||
@@ -494,12 +500,11 @@ describe('run_shell_command', () => {
|
||||
)[0];
|
||||
expect(toolCall.toolRequest.success).toBe(true);
|
||||
|
||||
// Validate model output - will throw if no output, warn if missing expected content
|
||||
validateModelOutput(
|
||||
result,
|
||||
'test-allow-all',
|
||||
'Shell command stdin allow all',
|
||||
);
|
||||
assertModelHasOutput(result);
|
||||
checkModelOutputContent(result, {
|
||||
expectedContent: 'test-allow-all',
|
||||
testName: 'Shell command stdin allow all',
|
||||
});
|
||||
});
|
||||
|
||||
it('should propagate environment variables to the child process', async () => {
|
||||
@@ -528,7 +533,11 @@ describe('run_shell_command', () => {
|
||||
foundToolCall,
|
||||
'Expected to find a run_shell_command tool call',
|
||||
).toBeTruthy();
|
||||
validateModelOutput(result, varValue, 'Env var propagation test');
|
||||
assertModelHasOutput(result);
|
||||
checkModelOutputContent(result, {
|
||||
expectedContent: varValue,
|
||||
testName: 'Env var propagation test',
|
||||
});
|
||||
expect(result).toContain(varValue);
|
||||
} finally {
|
||||
delete process.env[varName];
|
||||
@@ -558,17 +567,27 @@ describe('run_shell_command', () => {
|
||||
'Expected to find a run_shell_command tool call',
|
||||
).toBeTruthy();
|
||||
|
||||
validateModelOutput(result, fileName, 'Platform-specific listing test');
|
||||
assertModelHasOutput(result);
|
||||
checkModelOutputContent(result, {
|
||||
expectedContent: fileName,
|
||||
testName: 'Platform-specific listing test',
|
||||
});
|
||||
expect(result).toContain(fileName);
|
||||
});
|
||||
|
||||
it('rejects invalid shell expressions', async () => {
|
||||
await rig.setup('rejects invalid shell expressions', {
|
||||
settings: { tools: { core: ['run_shell_command'] } },
|
||||
settings: {
|
||||
tools: {
|
||||
core: ['run_shell_command'],
|
||||
allowed: ['run_shell_command(echo)'], // Specifically allow echo
|
||||
},
|
||||
},
|
||||
});
|
||||
const invalidCommand = getInvalidCommand();
|
||||
const result = await rig.run({
|
||||
args: `I am testing the error handling of the run_shell_command tool. Please attempt to run the following command, which I know has invalid syntax: \`${invalidCommand}\`. If the command fails as expected, please return the word FAIL, otherwise return the word SUCCESS.`,
|
||||
approvalMode: 'default', // Use default mode so safety fallback triggers confirmation
|
||||
});
|
||||
expect(result).toContain('FAIL');
|
||||
|
||||
|
||||
@@ -11,7 +11,12 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { TestRig, poll, validateModelOutput } from './test-helper.js';
|
||||
import {
|
||||
TestRig,
|
||||
poll,
|
||||
assertModelHasOutput,
|
||||
checkModelOutputContent,
|
||||
} from './test-helper.js';
|
||||
import { join } from 'node:path';
|
||||
import { writeFileSync } from 'node:fs';
|
||||
|
||||
@@ -226,8 +231,11 @@ describe.skip('simple-mcp-server', () => {
|
||||
|
||||
expect(foundToolCall, 'Expected to find an add tool call').toBeTruthy();
|
||||
|
||||
// Validate model output - will throw if no output, fail if missing expected content
|
||||
validateModelOutput(output, '15', 'MCP server test');
|
||||
assertModelHasOutput(output);
|
||||
checkModelOutputContent(output, {
|
||||
expectedContent: '15',
|
||||
testName: 'MCP server test',
|
||||
});
|
||||
expect(
|
||||
output.includes('15'),
|
||||
'Expected output to contain the sum (15)',
|
||||
|
||||
@@ -5,7 +5,12 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
|
||||
import {
|
||||
TestRig,
|
||||
printDebugInfo,
|
||||
assertModelHasOutput,
|
||||
checkModelOutputContent,
|
||||
} from './test-helper.js';
|
||||
|
||||
describe.skip('stdin context', () => {
|
||||
let rig: TestRig;
|
||||
@@ -67,7 +72,11 @@ describe.skip('stdin context', () => {
|
||||
}
|
||||
|
||||
// Validate model output
|
||||
validateModelOutput(result, randomString, 'STDIN context test');
|
||||
assertModelHasOutput(result);
|
||||
checkModelOutputContent(result, {
|
||||
expectedContent: randomString,
|
||||
testName: 'STDIN context test',
|
||||
});
|
||||
|
||||
expect(
|
||||
result.toLowerCase().includes(randomString),
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"I could not find the file `nonexistent-file-that-does-not-exist.txt` in the current directory or its subdirectories. Please verify the file path or name."}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":10,"candidatesTokenCount":25,"totalTokenCount":35,"promptTokensDetails":[{"modality":"TEXT","tokenCount":10}]}}]}
|
||||
@@ -0,0 +1 @@
|
||||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Hello! How can I help you today?"}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":5,"candidatesTokenCount":9,"totalTokenCount":14,"promptTokensDetails":[{"modality":"TEXT","tokenCount":5}]}}]}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { join } from 'node:path';
|
||||
import { TestRig } from './test-helper.js';
|
||||
|
||||
describe('stdout-stderr-output', () => {
|
||||
let rig: TestRig;
|
||||
|
||||
beforeEach(() => {
|
||||
rig = new TestRig();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await rig.cleanup();
|
||||
});
|
||||
|
||||
it('should send model response to stdout and app messages to stderr', async ({
|
||||
signal,
|
||||
}) => {
|
||||
await rig.setup('prompt-output-test', {
|
||||
fakeResponsesPath: join(
|
||||
import.meta.dirname,
|
||||
'stdout-stderr-output.responses',
|
||||
),
|
||||
});
|
||||
|
||||
const { stdout, exitCode } = await rig.runWithStreams(['-p', 'Say hello'], {
|
||||
signal,
|
||||
});
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
expect(stdout.toLowerCase()).toContain('hello');
|
||||
expect(stdout).not.toMatch(/^\[ERROR\]/m);
|
||||
expect(stdout).not.toMatch(/^\[INFO\]/m);
|
||||
});
|
||||
|
||||
it('should handle missing file with message to stdout and error to stderr', async ({
|
||||
signal,
|
||||
}) => {
|
||||
await rig.setup('error-output-test', {
|
||||
fakeResponsesPath: join(
|
||||
import.meta.dirname,
|
||||
'stdout-stderr-output-error.responses',
|
||||
),
|
||||
});
|
||||
|
||||
const { stdout, exitCode } = await rig.runWithStreams(
|
||||
['-p', '@nonexistent-file-that-does-not-exist.txt explain this'],
|
||||
{ signal },
|
||||
);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
expect(stdout.toLowerCase()).toMatch(
|
||||
/could not find|not exist|does not exist/,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -20,5 +20,8 @@ export default defineConfig({
|
||||
maxThreads: 16,
|
||||
},
|
||||
},
|
||||
env: {
|
||||
GEMINI_TEST_TYPE: 'integration',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -9,7 +9,8 @@ import {
|
||||
TestRig,
|
||||
createToolCallErrorMessage,
|
||||
printDebugInfo,
|
||||
validateModelOutput,
|
||||
assertModelHasOutput,
|
||||
checkModelOutputContent,
|
||||
} from './test-helper.js';
|
||||
|
||||
describe('write_file', () => {
|
||||
@@ -46,8 +47,11 @@ describe('write_file', () => {
|
||||
),
|
||||
).toBeTruthy();
|
||||
|
||||
// Validate model output - will throw if no output, warn if missing expected content
|
||||
validateModelOutput(result, 'dad.txt', 'Write file test');
|
||||
assertModelHasOutput(result);
|
||||
checkModelOutputContent(result, {
|
||||
expectedContent: 'dad.txt',
|
||||
testName: 'Write file test',
|
||||
});
|
||||
|
||||
const newFilePath = 'dad.txt';
|
||||
|
||||
|
||||
Generated
+98
-888
File diff suppressed because it is too large
Load Diff
+7
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.27.0-nightly.20260121.97aac696f",
|
||||
"version": "0.29.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.27.0-nightly.20260121.97aac696f"
|
||||
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.29.0-preview.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "cross-env NODE_ENV=development node scripts/start.js",
|
||||
@@ -32,7 +32,7 @@
|
||||
"docs:settings": "tsx ./scripts/generate-settings-doc.ts",
|
||||
"docs:keybindings": "tsx ./scripts/generate-keybindings-doc.ts",
|
||||
"build": "node scripts/build.js",
|
||||
"build-and-start": "npm run build && npm run start",
|
||||
"build-and-start": "npm run build && npm run start --",
|
||||
"build:vscode": "node scripts/build_vscode_companion.js",
|
||||
"build:all": "npm run build && npm run build:sandbox && npm run build:vscode",
|
||||
"build:packages": "npm run build --workspaces",
|
||||
@@ -86,9 +86,11 @@
|
||||
"@types/minimatch": "^5.1.2",
|
||||
"@types/mock-fs": "^4.13.4",
|
||||
"@types/prompts": "^2.4.9",
|
||||
"@types/proper-lockfile": "^4.1.4",
|
||||
"@types/react": "^19.2.0",
|
||||
"@types/react-dom": "^19.2.0",
|
||||
"@types/shell-quote": "^1.7.5",
|
||||
"@types/ws": "^8.18.1",
|
||||
"@vitest/coverage-v8": "^3.1.1",
|
||||
"@vitest/eslint-plugin": "^1.3.4",
|
||||
"cross-env": "^7.0.3",
|
||||
@@ -126,6 +128,7 @@
|
||||
"dependencies": {
|
||||
"ink": "npm:@jrichman/ink@6.4.8",
|
||||
"latest-version": "^9.0.0",
|
||||
"proper-lockfile": "^4.1.2",
|
||||
"simple-git": "^3.28.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
@@ -135,6 +138,7 @@
|
||||
"@lydell/node-pty-linux-x64": "1.1.0",
|
||||
"@lydell/node-pty-win32-arm64": "1.1.0",
|
||||
"@lydell/node-pty-win32-x64": "1.1.0",
|
||||
"gemini-cli-devtools": "^0.2.1",
|
||||
"keytar": "^7.9.0",
|
||||
"node-pty": "^1.0.0"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli-a2a-server",
|
||||
"version": "0.27.0-nightly.20260121.97aac696f",
|
||||
"version": "0.29.0-preview.1",
|
||||
"description": "Gemini CLI A2A Server",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -117,6 +117,7 @@ export class CoderAgentExecutor implements AgentExecutor {
|
||||
const agentSettings = persistedState._agentSettings;
|
||||
const config = await this.getConfig(agentSettings, sdkTask.id);
|
||||
const contextId: string =
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
(metadata['_contextId'] as string) || sdkTask.contextId;
|
||||
const runtimeTask = await Task.create(
|
||||
sdkTask.id,
|
||||
@@ -140,6 +141,7 @@ export class CoderAgentExecutor implements AgentExecutor {
|
||||
agentSettingsInput?: AgentSettings,
|
||||
eventBus?: ExecutionEventBus,
|
||||
): Promise<TaskWrapper> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const agentSettings = agentSettingsInput || ({} as AgentSettings);
|
||||
const config = await this.getConfig(agentSettings, taskId);
|
||||
const runtimeTask = await Task.create(
|
||||
@@ -290,6 +292,7 @@ export class CoderAgentExecutor implements AgentExecutor {
|
||||
const contextId: string =
|
||||
userMessage.contextId ||
|
||||
sdkTask?.contextId ||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
(sdkTask?.metadata?.['_contextId'] as string) ||
|
||||
uuidv4();
|
||||
|
||||
@@ -385,6 +388,7 @@ export class CoderAgentExecutor implements AgentExecutor {
|
||||
}
|
||||
} else {
|
||||
logger.info(`[CoderAgentExecutor] Creating new task ${taskId}.`);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const agentSettings = userMessage.metadata?.[
|
||||
'coderAgent'
|
||||
] as AgentSettings;
|
||||
|
||||
@@ -378,6 +378,7 @@ export class Task {
|
||||
if (tc.status === 'awaiting_approval' && tc.confirmationDetails) {
|
||||
this.pendingToolConfirmationDetails.set(
|
||||
tc.request.callId,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
tc.confirmationDetails as ToolCallConfirmationDetails,
|
||||
);
|
||||
}
|
||||
@@ -411,7 +412,7 @@ export class Task {
|
||||
);
|
||||
toolCalls.forEach((tc: ToolCall) => {
|
||||
if (tc.status === 'awaiting_approval' && tc.confirmationDetails) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises, @typescript-eslint/no-unsafe-type-assertion
|
||||
(tc.confirmationDetails as ToolCallConfirmationDetails).onConfirm(
|
||||
ToolConfirmationOutcome.ProceedOnce,
|
||||
);
|
||||
@@ -465,12 +466,14 @@ export class Task {
|
||||
T extends ToolCall | AnyDeclarativeTool,
|
||||
K extends UnionKeys<T>,
|
||||
>(from: T, ...fields: K[]): Partial<T> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const ret = {} as Pick<T, K>;
|
||||
for (const field of fields) {
|
||||
if (field in from) {
|
||||
ret[field] = from[field];
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
return ret as Partial<T>;
|
||||
}
|
||||
|
||||
@@ -493,6 +496,7 @@ export class Task {
|
||||
);
|
||||
|
||||
if (tc.tool) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
serializableToolCall.tool = this._pickFields(
|
||||
tc.tool,
|
||||
'name',
|
||||
@@ -622,8 +626,11 @@ export class Task {
|
||||
request.args['new_string']
|
||||
) {
|
||||
const newContent = await this.getProposedContent(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
request.args['file_path'] as string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
request.args['old_string'] as string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
request.args['new_string'] as string,
|
||||
);
|
||||
return { ...request, args: { ...request.args, newContent } };
|
||||
@@ -719,6 +726,7 @@ export class Task {
|
||||
case GeminiEventType.Error:
|
||||
default: {
|
||||
// Block scope for lexical declaration
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const errorEvent = event as ServerGeminiErrorEvent; // Type assertion
|
||||
const errorMessage =
|
||||
errorEvent.value?.error.message ?? 'Unknown error from LLM stream';
|
||||
@@ -807,6 +815,7 @@ export class Task {
|
||||
if (confirmationDetails.type === 'edit') {
|
||||
const payload = part.data['newContent']
|
||||
? ({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
newContent: part.data['newContent'] as string,
|
||||
} as ToolConfirmationPayload)
|
||||
: undefined;
|
||||
|
||||
@@ -85,6 +85,7 @@ export class InitCommand implements Command {
|
||||
if (!context.agentExecutor) {
|
||||
throw new Error('Agent executor not found in context.');
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const agentExecutor = context.agentExecutor as CoderAgentExecutor;
|
||||
|
||||
const agentSettings: AgentSettings = {
|
||||
|
||||
@@ -5,106 +5,266 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import * as path from 'node:path';
|
||||
import { loadConfig } from './config.js';
|
||||
import type { ExtensionLoader } from '@google/gemini-cli-core';
|
||||
import type { Settings } from './settings.js';
|
||||
import {
|
||||
type ExtensionLoader,
|
||||
FileDiscoveryService,
|
||||
getCodeAssistServer,
|
||||
Config,
|
||||
ExperimentFlags,
|
||||
fetchAdminControlsOnce,
|
||||
type FetchAdminControlsResponse,
|
||||
} from '@google/gemini-cli-core';
|
||||
|
||||
const {
|
||||
mockLoadServerHierarchicalMemory,
|
||||
mockConfigConstructor,
|
||||
mockVerifyGitAvailability,
|
||||
} = vi.hoisted(() => ({
|
||||
mockLoadServerHierarchicalMemory: vi.fn().mockResolvedValue({
|
||||
memoryContent: '',
|
||||
fileCount: 0,
|
||||
filePaths: [],
|
||||
}),
|
||||
mockConfigConstructor: vi.fn(),
|
||||
mockVerifyGitAvailability: vi.fn(),
|
||||
}));
|
||||
// Mock dependencies
|
||||
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import('@google/gemini-cli-core')>();
|
||||
return {
|
||||
...actual,
|
||||
Config: vi.fn().mockImplementation((params) => {
|
||||
const mockConfig = {
|
||||
...params,
|
||||
initialize: vi.fn(),
|
||||
refreshAuth: vi.fn(),
|
||||
getExperiments: vi.fn().mockReturnValue({
|
||||
flags: {
|
||||
[actual.ExperimentFlags.ENABLE_ADMIN_CONTROLS]: {
|
||||
boolValue: false,
|
||||
},
|
||||
},
|
||||
}),
|
||||
getRemoteAdminSettings: vi.fn(),
|
||||
setRemoteAdminSettings: vi.fn(),
|
||||
};
|
||||
return mockConfig;
|
||||
}),
|
||||
loadServerHierarchicalMemory: vi.fn().mockResolvedValue({
|
||||
memoryContent: { global: '', extension: '', project: '' },
|
||||
fileCount: 0,
|
||||
filePaths: [],
|
||||
}),
|
||||
startupProfiler: {
|
||||
flush: vi.fn(),
|
||||
},
|
||||
FileDiscoveryService: vi.fn(),
|
||||
getCodeAssistServer: vi.fn(),
|
||||
fetchAdminControlsOnce: vi.fn(),
|
||||
coreEvents: {
|
||||
emitAdminSettingsChanged: vi.fn(),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('@google/gemini-cli-core', async () => ({
|
||||
Config: class MockConfig {
|
||||
constructor(params: unknown) {
|
||||
mockConfigConstructor(params);
|
||||
}
|
||||
initialize = vi.fn();
|
||||
refreshAuth = vi.fn();
|
||||
},
|
||||
loadServerHierarchicalMemory: mockLoadServerHierarchicalMemory,
|
||||
startupProfiler: {
|
||||
flush: vi.fn(),
|
||||
},
|
||||
FileDiscoveryService: vi.fn(),
|
||||
ApprovalMode: { DEFAULT: 'default', YOLO: 'yolo' },
|
||||
AuthType: {
|
||||
LOGIN_WITH_GOOGLE: 'login_with_google',
|
||||
USE_GEMINI: 'use_gemini',
|
||||
},
|
||||
GEMINI_DIR: '.gemini',
|
||||
DEFAULT_GEMINI_EMBEDDING_MODEL: 'models/embedding-001',
|
||||
DEFAULT_GEMINI_MODEL: 'models/gemini-1.5-flash',
|
||||
PREVIEW_GEMINI_MODEL: 'models/gemini-1.5-pro-latest',
|
||||
homedir: () => '/tmp',
|
||||
GitService: {
|
||||
verifyGitAvailability: mockVerifyGitAvailability,
|
||||
vi.mock('../utils/logger.js', () => ({
|
||||
logger: {
|
||||
info: vi.fn(),
|
||||
error: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('loadConfig', () => {
|
||||
const mockSettings = {
|
||||
checkpointing: { enabled: true },
|
||||
};
|
||||
const mockExtensionLoader = {
|
||||
start: vi.fn(),
|
||||
getExtensions: vi.fn().mockReturnValue([]),
|
||||
} as unknown as ExtensionLoader;
|
||||
const mockSettings = {} as Settings;
|
||||
const mockExtensionLoader = {} as ExtensionLoader;
|
||||
const taskId = 'test-task-id';
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
vi.clearAllMocks();
|
||||
process.env['GEMINI_API_KEY'] = 'test-key';
|
||||
// Reset the mock return value just in case
|
||||
mockLoadServerHierarchicalMemory.mockResolvedValue({
|
||||
memoryContent: '',
|
||||
fileCount: 0,
|
||||
filePaths: [],
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
delete process.env['CUSTOM_IGNORE_FILE_PATHS'];
|
||||
delete process.env['GEMINI_API_KEY'];
|
||||
delete process.env['CHECKPOINTING'];
|
||||
});
|
||||
|
||||
it('should disable checkpointing if git is not installed', async () => {
|
||||
mockVerifyGitAvailability.mockResolvedValue(false);
|
||||
describe('admin settings overrides', () => {
|
||||
it('should not fetch admin controls if experiment is disabled', async () => {
|
||||
await loadConfig(mockSettings, mockExtensionLoader, taskId);
|
||||
expect(fetchAdminControlsOnce).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
await loadConfig(
|
||||
mockSettings as unknown as Settings,
|
||||
mockExtensionLoader,
|
||||
'test-task',
|
||||
);
|
||||
describe('when admin controls experiment is enabled', () => {
|
||||
beforeEach(() => {
|
||||
// We need to cast to any here to modify the mock implementation
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(Config as any).mockImplementation((params: unknown) => {
|
||||
const mockConfig = {
|
||||
...(params as object),
|
||||
initialize: vi.fn(),
|
||||
refreshAuth: vi.fn(),
|
||||
getExperiments: vi.fn().mockReturnValue({
|
||||
flags: {
|
||||
[ExperimentFlags.ENABLE_ADMIN_CONTROLS]: {
|
||||
boolValue: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
getRemoteAdminSettings: vi.fn().mockReturnValue({}),
|
||||
setRemoteAdminSettings: vi.fn(),
|
||||
};
|
||||
return mockConfig;
|
||||
});
|
||||
});
|
||||
|
||||
expect(mockConfigConstructor).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
checkpointing: false,
|
||||
}),
|
||||
);
|
||||
it('should fetch admin controls and apply them', async () => {
|
||||
const mockAdminSettings: FetchAdminControlsResponse = {
|
||||
mcpSetting: {
|
||||
mcpEnabled: false,
|
||||
},
|
||||
cliFeatureSetting: {
|
||||
extensionsSetting: {
|
||||
extensionsEnabled: false,
|
||||
},
|
||||
},
|
||||
strictModeDisabled: false,
|
||||
};
|
||||
vi.mocked(fetchAdminControlsOnce).mockResolvedValue(mockAdminSettings);
|
||||
|
||||
await loadConfig(mockSettings, mockExtensionLoader, taskId);
|
||||
|
||||
expect(Config).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({
|
||||
disableYoloMode: !mockAdminSettings.strictModeDisabled,
|
||||
mcpEnabled: mockAdminSettings.mcpSetting?.mcpEnabled,
|
||||
extensionsEnabled:
|
||||
mockAdminSettings.cliFeatureSetting?.extensionsSetting
|
||||
?.extensionsEnabled,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should treat unset admin settings as false when admin settings are passed', async () => {
|
||||
const mockAdminSettings: FetchAdminControlsResponse = {
|
||||
mcpSetting: {
|
||||
mcpEnabled: true,
|
||||
},
|
||||
};
|
||||
vi.mocked(fetchAdminControlsOnce).mockResolvedValue(mockAdminSettings);
|
||||
|
||||
await loadConfig(mockSettings, mockExtensionLoader, taskId);
|
||||
|
||||
expect(Config).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({
|
||||
disableYoloMode: !false,
|
||||
mcpEnabled: mockAdminSettings.mcpSetting?.mcpEnabled,
|
||||
extensionsEnabled: undefined,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should not pass default unset admin settings when no admin settings are present', async () => {
|
||||
const mockAdminSettings: FetchAdminControlsResponse = {};
|
||||
vi.mocked(fetchAdminControlsOnce).mockResolvedValue(mockAdminSettings);
|
||||
|
||||
await loadConfig(mockSettings, mockExtensionLoader, taskId);
|
||||
|
||||
expect(Config).toHaveBeenLastCalledWith(expect.objectContaining({}));
|
||||
});
|
||||
|
||||
it('should fetch admin controls using the code assist server when available', async () => {
|
||||
const mockAdminSettings: FetchAdminControlsResponse = {
|
||||
mcpSetting: {
|
||||
mcpEnabled: true,
|
||||
},
|
||||
strictModeDisabled: true,
|
||||
};
|
||||
const mockCodeAssistServer = { projectId: 'test-project' };
|
||||
vi.mocked(getCodeAssistServer).mockReturnValue(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
mockCodeAssistServer as any,
|
||||
);
|
||||
vi.mocked(fetchAdminControlsOnce).mockResolvedValue(mockAdminSettings);
|
||||
|
||||
await loadConfig(mockSettings, mockExtensionLoader, taskId);
|
||||
|
||||
expect(fetchAdminControlsOnce).toHaveBeenCalledWith(
|
||||
mockCodeAssistServer,
|
||||
true,
|
||||
);
|
||||
expect(Config).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({
|
||||
disableYoloMode: !mockAdminSettings.strictModeDisabled,
|
||||
mcpEnabled: mockAdminSettings.mcpSetting?.mcpEnabled,
|
||||
extensionsEnabled: undefined,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should enable checkpointing if git is installed', async () => {
|
||||
mockVerifyGitAvailability.mockResolvedValue(true);
|
||||
it('should set customIgnoreFilePaths when CUSTOM_IGNORE_FILE_PATHS env var is present', async () => {
|
||||
const testPath = '/tmp/ignore';
|
||||
process.env['CUSTOM_IGNORE_FILE_PATHS'] = testPath;
|
||||
const config = await loadConfig(mockSettings, mockExtensionLoader, taskId);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
expect((config as any).fileFiltering.customIgnoreFilePaths).toEqual([
|
||||
testPath,
|
||||
]);
|
||||
});
|
||||
|
||||
await loadConfig(
|
||||
mockSettings as unknown as Settings,
|
||||
mockExtensionLoader,
|
||||
'test-task',
|
||||
);
|
||||
it('should set customIgnoreFilePaths when settings.fileFiltering.customIgnoreFilePaths is present', async () => {
|
||||
const testPath = '/settings/ignore';
|
||||
const settings: Settings = {
|
||||
fileFiltering: {
|
||||
customIgnoreFilePaths: [testPath],
|
||||
},
|
||||
};
|
||||
const config = await loadConfig(settings, mockExtensionLoader, taskId);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
expect((config as any).fileFiltering.customIgnoreFilePaths).toEqual([
|
||||
testPath,
|
||||
]);
|
||||
});
|
||||
|
||||
expect(mockConfigConstructor).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
checkpointing: true,
|
||||
}),
|
||||
);
|
||||
it('should merge customIgnoreFilePaths from settings and env var', async () => {
|
||||
const envPath = '/env/ignore';
|
||||
const settingsPath = '/settings/ignore';
|
||||
process.env['CUSTOM_IGNORE_FILE_PATHS'] = envPath;
|
||||
const settings: Settings = {
|
||||
fileFiltering: {
|
||||
customIgnoreFilePaths: [settingsPath],
|
||||
},
|
||||
};
|
||||
const config = await loadConfig(settings, mockExtensionLoader, taskId);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
expect((config as any).fileFiltering.customIgnoreFilePaths).toEqual([
|
||||
settingsPath,
|
||||
envPath,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should split CUSTOM_IGNORE_FILE_PATHS using system delimiter', async () => {
|
||||
const paths = ['/path/one', '/path/two'];
|
||||
process.env['CUSTOM_IGNORE_FILE_PATHS'] = paths.join(path.delimiter);
|
||||
const config = await loadConfig(mockSettings, mockExtensionLoader, taskId);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
expect((config as any).fileFiltering.customIgnoreFilePaths).toEqual(paths);
|
||||
});
|
||||
|
||||
it('should have empty customIgnoreFilePaths when both are missing', async () => {
|
||||
const config = await loadConfig(mockSettings, mockExtensionLoader, taskId);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
expect((config as any).fileFiltering.customIgnoreFilePaths).toEqual([]);
|
||||
});
|
||||
|
||||
it('should initialize FileDiscoveryService with correct options', async () => {
|
||||
const testPath = '/tmp/ignore';
|
||||
process.env['CUSTOM_IGNORE_FILE_PATHS'] = testPath;
|
||||
const settings: Settings = {
|
||||
fileFiltering: {
|
||||
respectGitIgnore: false,
|
||||
},
|
||||
};
|
||||
|
||||
await loadConfig(settings, mockExtensionLoader, taskId);
|
||||
|
||||
expect(FileDiscoveryService).toHaveBeenCalledWith(expect.any(String), {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: undefined,
|
||||
customIgnoreFilePaths: [testPath],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,12 +18,14 @@ import {
|
||||
loadServerHierarchicalMemory,
|
||||
GEMINI_DIR,
|
||||
DEFAULT_GEMINI_EMBEDDING_MODEL,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
type ExtensionLoader,
|
||||
startupProfiler,
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
homedir,
|
||||
GitService,
|
||||
fetchAdminControlsOnce,
|
||||
getCodeAssistServer,
|
||||
ExperimentFlags,
|
||||
} from '@google/gemini-cli-core';
|
||||
|
||||
import { logger } from '../utils/logger.js';
|
||||
@@ -57,9 +59,7 @@ export async function loadConfig(
|
||||
|
||||
const configParams: ConfigParameters = {
|
||||
sessionId: taskId,
|
||||
model: settings.general?.previewFeatures
|
||||
? PREVIEW_GEMINI_MODEL
|
||||
: DEFAULT_GEMINI_MODEL,
|
||||
model: PREVIEW_GEMINI_MODEL,
|
||||
embeddingModel: DEFAULT_GEMINI_EMBEDDING_MODEL,
|
||||
sandbox: undefined, // Sandbox might not be relevant for a server-side agent
|
||||
targetDir: workspaceDir, // Or a specific directory the agent operates on
|
||||
@@ -77,6 +77,7 @@ export async function loadConfig(
|
||||
cwd: workspaceDir,
|
||||
telemetry: {
|
||||
enabled: settings.telemetry?.enabled,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
target: settings.telemetry?.target as TelemetryTarget,
|
||||
otlpEndpoint:
|
||||
process.env['OTEL_EXPORTER_OTLP_ENDPOINT'] ??
|
||||
@@ -86,21 +87,31 @@ export async function loadConfig(
|
||||
// Git-aware file filtering settings
|
||||
fileFiltering: {
|
||||
respectGitIgnore: settings.fileFiltering?.respectGitIgnore,
|
||||
respectGeminiIgnore: settings.fileFiltering?.respectGeminiIgnore,
|
||||
enableRecursiveFileSearch:
|
||||
settings.fileFiltering?.enableRecursiveFileSearch,
|
||||
customIgnoreFilePaths: [
|
||||
...(settings.fileFiltering?.customIgnoreFilePaths || []),
|
||||
...(process.env['CUSTOM_IGNORE_FILE_PATHS']
|
||||
? process.env['CUSTOM_IGNORE_FILE_PATHS'].split(path.delimiter)
|
||||
: []),
|
||||
],
|
||||
},
|
||||
ideMode: false,
|
||||
folderTrust,
|
||||
trustedFolder: true,
|
||||
extensionLoader,
|
||||
checkpointing,
|
||||
previewFeatures: settings.general?.previewFeatures,
|
||||
interactive: true,
|
||||
enableInteractiveShell: true,
|
||||
ptyInfo: 'auto',
|
||||
};
|
||||
|
||||
const fileService = new FileDiscoveryService(workspaceDir);
|
||||
const fileService = new FileDiscoveryService(workspaceDir, {
|
||||
respectGitIgnore: configParams?.fileFiltering?.respectGitIgnore,
|
||||
respectGeminiIgnore: configParams?.fileFiltering?.respectGeminiIgnore,
|
||||
customIgnoreFilePaths: configParams?.fileFiltering?.customIgnoreFilePaths,
|
||||
});
|
||||
const { memoryContent, fileCount, filePaths } =
|
||||
await loadServerHierarchicalMemory(
|
||||
workspaceDir,
|
||||
@@ -113,37 +124,50 @@ export async function loadConfig(
|
||||
configParams.userMemory = memoryContent;
|
||||
configParams.geminiMdFileCount = fileCount;
|
||||
configParams.geminiMdFilePaths = filePaths;
|
||||
const config = new Config({
|
||||
|
||||
// Set an initial config to use to get a code assist server.
|
||||
// This is needed to fetch admin controls.
|
||||
const initialConfig = new Config({
|
||||
...configParams,
|
||||
});
|
||||
|
||||
const codeAssistServer = getCodeAssistServer(initialConfig);
|
||||
|
||||
const adminControlsEnabled =
|
||||
initialConfig.getExperiments()?.flags[ExperimentFlags.ENABLE_ADMIN_CONTROLS]
|
||||
?.boolValue ?? false;
|
||||
|
||||
// Initialize final config parameters to the previous parameters.
|
||||
// If no admin controls are needed, these will be used as-is for the final
|
||||
// config.
|
||||
const finalConfigParams = { ...configParams };
|
||||
if (adminControlsEnabled) {
|
||||
const adminSettings = await fetchAdminControlsOnce(
|
||||
codeAssistServer,
|
||||
adminControlsEnabled,
|
||||
);
|
||||
|
||||
// Admin settings are able to be undefined if unset, but if any are present,
|
||||
// we should initialize them all.
|
||||
// If any are present, undefined settings should be treated as if they were
|
||||
// set to false.
|
||||
// If NONE are present, disregard admin settings entirely, and pass the
|
||||
// final config as is.
|
||||
if (Object.keys(adminSettings).length !== 0) {
|
||||
finalConfigParams.disableYoloMode = !adminSettings.strictModeDisabled;
|
||||
finalConfigParams.mcpEnabled = adminSettings.mcpSetting?.mcpEnabled;
|
||||
finalConfigParams.extensionsEnabled =
|
||||
adminSettings.cliFeatureSetting?.extensionsSetting?.extensionsEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
const config = new Config(finalConfigParams);
|
||||
|
||||
// Needed to initialize ToolRegistry, and git checkpointing if enabled
|
||||
await config.initialize();
|
||||
startupProfiler.flush(config);
|
||||
|
||||
if (process.env['USE_CCPA']) {
|
||||
logger.info('[Config] Using CCPA Auth:');
|
||||
try {
|
||||
if (adcFilePath) {
|
||||
path.resolve(adcFilePath);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
`[Config] USE_CCPA env var is true but unable to resolve GOOGLE_APPLICATION_CREDENTIALS file path ${adcFilePath}. Error ${e}`,
|
||||
);
|
||||
}
|
||||
await config.refreshAuth(AuthType.LOGIN_WITH_GOOGLE);
|
||||
logger.info(
|
||||
`[Config] GOOGLE_CLOUD_PROJECT: ${process.env['GOOGLE_CLOUD_PROJECT']}`,
|
||||
);
|
||||
} else if (process.env['GEMINI_API_KEY']) {
|
||||
logger.info('[Config] Using Gemini API Key');
|
||||
await config.refreshAuth(AuthType.USE_GEMINI);
|
||||
} else {
|
||||
const errorMessage =
|
||||
'[Config] Unable to set GeneratorConfig. Please provide a GEMINI_API_KEY or set USE_CCPA.';
|
||||
logger.error(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
await refreshAuthentication(config, adcFilePath, 'Config');
|
||||
|
||||
return config;
|
||||
}
|
||||
@@ -211,3 +235,33 @@ function findEnvFile(startDir: string): string | null {
|
||||
currentDir = parentDir;
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshAuthentication(
|
||||
config: Config,
|
||||
adcFilePath: string | undefined,
|
||||
logPrefix: string,
|
||||
): Promise<void> {
|
||||
if (process.env['USE_CCPA']) {
|
||||
logger.info(`[${logPrefix}] Using CCPA Auth:`);
|
||||
try {
|
||||
if (adcFilePath) {
|
||||
path.resolve(adcFilePath);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
`[${logPrefix}] USE_CCPA env var is true but unable to resolve GOOGLE_APPLICATION_CREDENTIALS file path ${adcFilePath}. Error ${e}`,
|
||||
);
|
||||
}
|
||||
await config.refreshAuth(AuthType.LOGIN_WITH_GOOGLE);
|
||||
logger.info(
|
||||
`[${logPrefix}] GOOGLE_CLOUD_PROJECT: ${process.env['GOOGLE_CLOUD_PROJECT']}`,
|
||||
);
|
||||
} else if (process.env['GEMINI_API_KEY']) {
|
||||
logger.info(`[${logPrefix}] Using Gemini API Key`);
|
||||
await config.refreshAuth(AuthType.USE_GEMINI);
|
||||
} else {
|
||||
const errorMessage = `[${logPrefix}] Unable to set GeneratorConfig. Please provide a GEMINI_API_KEY or set USE_CCPA.`;
|
||||
logger.error(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +93,7 @@ function loadExtension(extensionDir: string): GeminiCLIExtension | null {
|
||||
|
||||
try {
|
||||
const configContent = fs.readFileSync(configFilePath, 'utf-8');
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const config = JSON.parse(configContent) as ExtensionConfig;
|
||||
if (!config.name || !config.version) {
|
||||
logger.error(
|
||||
@@ -107,6 +108,7 @@ function loadExtension(extensionDir: string): GeminiCLIExtension | null {
|
||||
.map((contextFileName) => path.join(extensionDir, contextFileName))
|
||||
.filter((contextFilePath) => fs.existsSync(contextFilePath));
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
return {
|
||||
name: config.name,
|
||||
version: config.version,
|
||||
@@ -140,6 +142,7 @@ export function loadInstallMetadata(
|
||||
const metadataFilePath = path.join(extensionDir, INSTALL_METADATA_FILENAME);
|
||||
try {
|
||||
const configContent = fs.readFileSync(metadataFilePath, 'utf-8');
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const metadata = JSON.parse(configContent) as ExtensionInstallMetadata;
|
||||
return metadata;
|
||||
} catch (e) {
|
||||
|
||||
@@ -89,67 +89,6 @@ describe('loadSettings', () => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('should load nested previewFeatures from user settings', () => {
|
||||
const settings = {
|
||||
general: {
|
||||
previewFeatures: true,
|
||||
},
|
||||
};
|
||||
fs.writeFileSync(USER_SETTINGS_PATH, JSON.stringify(settings));
|
||||
|
||||
const result = loadSettings(mockWorkspaceDir);
|
||||
expect(result.general?.previewFeatures).toBe(true);
|
||||
});
|
||||
|
||||
it('should load nested previewFeatures from workspace settings', () => {
|
||||
const settings = {
|
||||
general: {
|
||||
previewFeatures: true,
|
||||
},
|
||||
};
|
||||
const workspaceSettingsPath = path.join(
|
||||
mockGeminiWorkspaceDir,
|
||||
'settings.json',
|
||||
);
|
||||
fs.writeFileSync(workspaceSettingsPath, JSON.stringify(settings));
|
||||
|
||||
const result = loadSettings(mockWorkspaceDir);
|
||||
expect(result.general?.previewFeatures).toBe(true);
|
||||
});
|
||||
|
||||
it('should prioritize workspace settings over user settings', () => {
|
||||
const userSettings = {
|
||||
general: {
|
||||
previewFeatures: false,
|
||||
},
|
||||
};
|
||||
fs.writeFileSync(USER_SETTINGS_PATH, JSON.stringify(userSettings));
|
||||
|
||||
const workspaceSettings = {
|
||||
general: {
|
||||
previewFeatures: true,
|
||||
},
|
||||
};
|
||||
const workspaceSettingsPath = path.join(
|
||||
mockGeminiWorkspaceDir,
|
||||
'settings.json',
|
||||
);
|
||||
fs.writeFileSync(workspaceSettingsPath, JSON.stringify(workspaceSettings));
|
||||
|
||||
const result = loadSettings(mockWorkspaceDir);
|
||||
expect(result.general?.previewFeatures).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle missing previewFeatures', () => {
|
||||
const settings = {
|
||||
general: {},
|
||||
};
|
||||
fs.writeFileSync(USER_SETTINGS_PATH, JSON.stringify(settings));
|
||||
|
||||
const result = loadSettings(mockWorkspaceDir);
|
||||
expect(result.general?.previewFeatures).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should load other top-level settings correctly', () => {
|
||||
const settings = {
|
||||
showMemoryUsage: true,
|
||||
|
||||
@@ -31,14 +31,13 @@ export interface Settings {
|
||||
showMemoryUsage?: boolean;
|
||||
checkpointing?: CheckpointingSettings;
|
||||
folderTrust?: boolean;
|
||||
general?: {
|
||||
previewFeatures?: boolean;
|
||||
};
|
||||
|
||||
// Git-aware file filtering settings
|
||||
fileFiltering?: {
|
||||
respectGitIgnore?: boolean;
|
||||
respectGeminiIgnore?: boolean;
|
||||
enableRecursiveFileSearch?: boolean;
|
||||
customIgnoreFilePaths?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -68,6 +67,7 @@ export function loadSettings(workspaceDir: string): Settings {
|
||||
try {
|
||||
if (fs.existsSync(USER_SETTINGS_PATH)) {
|
||||
const userContent = fs.readFileSync(USER_SETTINGS_PATH, 'utf-8');
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const parsedUserSettings = JSON.parse(
|
||||
stripJsonComments(userContent),
|
||||
) as Settings;
|
||||
@@ -90,6 +90,7 @@ export function loadSettings(workspaceDir: string): Settings {
|
||||
try {
|
||||
if (fs.existsSync(workspaceSettingsPath)) {
|
||||
const projectContent = fs.readFileSync(workspaceSettingsPath, 'utf-8');
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const parsedWorkspaceSettings = JSON.parse(
|
||||
stripJsonComments(projectContent),
|
||||
) as Settings;
|
||||
@@ -140,10 +141,12 @@ function resolveEnvVarsInObject<T>(obj: T): T {
|
||||
}
|
||||
|
||||
if (typeof obj === 'string') {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
return resolveEnvVarsInString(obj) as unknown as T;
|
||||
}
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
return obj.map((item) => resolveEnvVarsInObject(item)) as unknown as T;
|
||||
}
|
||||
|
||||
|
||||
@@ -118,6 +118,7 @@ async function handleExecuteCommand(
|
||||
const eventHandler = (event: AgentExecutionEvent) => {
|
||||
const jsonRpcResponse = {
|
||||
jsonrpc: '2.0',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
id: 'taskId' in event ? event.taskId : (event as Message).messageId,
|
||||
result: event,
|
||||
};
|
||||
@@ -206,6 +207,7 @@ export async function createApp() {
|
||||
expressApp.post('/tasks', async (req, res) => {
|
||||
try {
|
||||
const taskId = uuidv4();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const agentSettings = req.body.agentSettings as
|
||||
| AgentSettings
|
||||
| undefined;
|
||||
|
||||
@@ -95,6 +95,7 @@ export class GCSTaskStore implements TaskStore {
|
||||
await this.ensureBucketInitialized();
|
||||
const taskId = task.id;
|
||||
const persistedState = getPersistedState(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
task.metadata as PersistedTaskMetadata,
|
||||
);
|
||||
|
||||
|
||||
@@ -125,6 +125,7 @@ export const METADATA_KEY = '__persistedState';
|
||||
export function getPersistedState(
|
||||
metadata: PersistedTaskMetadata,
|
||||
): PersistedStateMetadata | undefined {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
return metadata?.[METADATA_KEY] as PersistedStateMetadata | undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import * as path from 'node:path';
|
||||
import type {
|
||||
Task as SDKTask,
|
||||
TaskStatusUpdateEvent,
|
||||
@@ -12,11 +13,11 @@ import type {
|
||||
import {
|
||||
ApprovalMode,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES,
|
||||
DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,
|
||||
GeminiClient,
|
||||
HookSystem,
|
||||
PolicyDecision,
|
||||
tmpdir,
|
||||
} from '@google/gemini-cli-core';
|
||||
import { createMockMessageBus } from '@google/gemini-cli-core/src/test-utils/mock-message-bus.js';
|
||||
import type { Config, Storage } from '@google/gemini-cli-core';
|
||||
@@ -25,6 +26,8 @@ import { expect, vi } from 'vitest';
|
||||
export function createMockConfig(
|
||||
overrides: Partial<Config> = {},
|
||||
): Partial<Config> {
|
||||
const tmpDir = tmpdir();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const mockConfig = {
|
||||
getToolRegistry: vi.fn().mockReturnValue({
|
||||
getTool: vi.fn(),
|
||||
@@ -39,15 +42,15 @@ export function createMockConfig(
|
||||
getWorkspaceContext: vi.fn().mockReturnValue({
|
||||
isPathWithinWorkspace: () => true,
|
||||
}),
|
||||
getTargetDir: () => '/test',
|
||||
getTargetDir: () => tmpDir,
|
||||
getCheckpointingEnabled: vi.fn().mockReturnValue(false),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
storage: {
|
||||
getProjectTempDir: () => '/tmp',
|
||||
getProjectTempCheckpointsDir: () => '/tmp/checkpoints',
|
||||
getProjectTempDir: () => tmpDir,
|
||||
getProjectTempCheckpointsDir: () => path.join(tmpDir, 'checkpoints'),
|
||||
} as Storage,
|
||||
getTruncateToolOutputThreshold: () =>
|
||||
DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,
|
||||
getTruncateToolOutputLines: () => DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES,
|
||||
getActiveModel: vi.fn().mockReturnValue(DEFAULT_GEMINI_MODEL),
|
||||
getDebugMode: vi.fn().mockReturnValue(false),
|
||||
getContentGeneratorConfig: vi.fn().mockReturnValue({ model: 'gemini-pro' }),
|
||||
@@ -147,6 +150,7 @@ export function assertUniqueFinalEventIsLast(
|
||||
events: SendStreamingMessageSuccessResponse[],
|
||||
) {
|
||||
// Final event is input-required & final
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const finalEvent = events[events.length - 1].result as TaskStatusUpdateEvent;
|
||||
expect(finalEvent.metadata?.['coderAgent']).toMatchObject({
|
||||
kind: 'state-change',
|
||||
@@ -156,9 +160,11 @@ export function assertUniqueFinalEventIsLast(
|
||||
|
||||
// There is only one event with final and its the last
|
||||
expect(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
events.filter((e) => (e.result as TaskStatusUpdateEvent).final).length,
|
||||
).toBe(1);
|
||||
expect(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
events.findIndex((e) => (e.result as TaskStatusUpdateEvent).final),
|
||||
).toBe(events.length - 1);
|
||||
}
|
||||
@@ -167,11 +173,13 @@ export function assertTaskCreationAndWorkingStatus(
|
||||
events: SendStreamingMessageSuccessResponse[],
|
||||
) {
|
||||
// Initial task creation event
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const taskEvent = events[0].result as SDKTask;
|
||||
expect(taskEvent.kind).toBe('task');
|
||||
expect(taskEvent.status.state).toBe('submitted');
|
||||
|
||||
// Status update: working
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const workingEvent = events[1].result as TaskStatusUpdateEvent;
|
||||
expect(workingEvent.kind).toBe('status-update');
|
||||
expect(workingEvent.status.state).toBe('working');
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
## React & Ink (CLI UI)
|
||||
|
||||
- **Side Effects**: Use reducers for complex state transitions; avoid `setState`
|
||||
triggers in callbacks.
|
||||
- Always fix react-hooks/exhaustive-deps lint errors by adding the missing
|
||||
dependencies.
|
||||
- **Shortcuts**: only define keyboard shortcuts in
|
||||
`packages/cli/src/config/keyBindings.ts
|
||||
|
||||
## Testing
|
||||
|
||||
- **Utilities**: Use `renderWithProviders` and `waitFor` from
|
||||
`packages/cli/src/test-utils/`.
|
||||
- **Snapshots**: Use `toMatchSnapshot()` to verify Ink output.
|
||||
- **Mocks**: Use mocks as sparingly as possilble.
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.27.0-nightly.20260121.97aac696f",
|
||||
"version": "0.29.0-preview.1",
|
||||
"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.27.0-nightly.20260121.97aac696f"
|
||||
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.29.0-preview.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "^0.12.0",
|
||||
@@ -34,10 +34,11 @@
|
||||
"@google/genai": "1.30.0",
|
||||
"@iarna/toml": "^2.2.5",
|
||||
"@modelcontextprotocol/sdk": "^1.23.0",
|
||||
"@types/update-notifier": "^6.0.8",
|
||||
"ansi-escapes": "^7.3.0",
|
||||
"ansi-regex": "^6.2.2",
|
||||
"chalk": "^4.1.2",
|
||||
"cli-spinners": "^2.9.2",
|
||||
"clipboardy": "^5.0.0",
|
||||
"color-convert": "^2.0.1",
|
||||
"command-exists": "^1.2.9",
|
||||
"comment-json": "^4.2.5",
|
||||
"diff": "^8.0.3",
|
||||
@@ -54,8 +55,8 @@
|
||||
"mnemonist": "^0.40.3",
|
||||
"open": "^10.1.2",
|
||||
"prompts": "^2.4.2",
|
||||
"proper-lockfile": "^4.1.2",
|
||||
"react": "^19.2.0",
|
||||
"read-package-up": "^11.0.0",
|
||||
"shell-quote": "^1.8.3",
|
||||
"simple-git": "^3.28.0",
|
||||
"string-width": "^8.1.0",
|
||||
@@ -64,27 +65,21 @@
|
||||
"tar": "^7.5.2",
|
||||
"tinygradient": "^1.1.5",
|
||||
"undici": "^7.10.0",
|
||||
"wrap-ansi": "9.0.2",
|
||||
"ws": "^8.16.0",
|
||||
"yargs": "^17.7.2",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/runtime": "^7.27.6",
|
||||
"@google/gemini-cli-test-utils": "file:../test-utils",
|
||||
"@types/archiver": "^6.0.3",
|
||||
"@types/command-exists": "^1.2.3",
|
||||
"@types/dotenv": "^6.1.1",
|
||||
"@types/hast": "^3.0.4",
|
||||
"@types/node": "^20.11.24",
|
||||
"@types/react": "^19.2.0",
|
||||
"@types/react-dom": "^19.2.0",
|
||||
"@types/semver": "^7.7.0",
|
||||
"@types/shell-quote": "^1.7.5",
|
||||
"@types/tar": "^6.1.13",
|
||||
"@types/ws": "^8.5.10",
|
||||
"@types/yargs": "^17.0.32",
|
||||
"archiver": "^7.0.1",
|
||||
"ink-testing-library": "^4.0.0",
|
||||
"pretty-format": "^30.0.2",
|
||||
"react-dom": "^19.2.0",
|
||||
"typescript": "^5.3.3",
|
||||
"vitest": "^3.1.1"
|
||||
},
|
||||
|
||||
@@ -17,31 +17,26 @@ import yargs from 'yargs';
|
||||
import { debugLogger } from '@google/gemini-cli-core';
|
||||
import {
|
||||
updateSetting,
|
||||
promptForSetting,
|
||||
getScopedEnvContents,
|
||||
type ExtensionSetting,
|
||||
} from '../../config/extensions/extensionSettings.js';
|
||||
import prompts from 'prompts';
|
||||
import * as fs from 'node:fs';
|
||||
|
||||
const {
|
||||
mockExtensionManager,
|
||||
mockGetExtensionAndManager,
|
||||
mockGetExtensionManager,
|
||||
mockLoadSettings,
|
||||
} = vi.hoisted(() => {
|
||||
const extensionManager = {
|
||||
loadExtensionConfig: vi.fn(),
|
||||
getExtensions: vi.fn(),
|
||||
loadExtensions: vi.fn(),
|
||||
getSettings: vi.fn(),
|
||||
};
|
||||
return {
|
||||
mockExtensionManager: extensionManager,
|
||||
mockGetExtensionAndManager: vi.fn(),
|
||||
mockGetExtensionManager: vi.fn(),
|
||||
mockLoadSettings: vi.fn().mockReturnValue({ merged: {} }),
|
||||
};
|
||||
});
|
||||
const { mockExtensionManager, mockGetExtensionManager, mockLoadSettings } =
|
||||
vi.hoisted(() => {
|
||||
const extensionManager = {
|
||||
loadExtensionConfig: vi.fn(),
|
||||
getExtensions: vi.fn(),
|
||||
loadExtensions: vi.fn(),
|
||||
getSettings: vi.fn(),
|
||||
};
|
||||
return {
|
||||
mockExtensionManager: extensionManager,
|
||||
mockGetExtensionManager: vi.fn(),
|
||||
mockLoadSettings: vi.fn().mockReturnValue({ merged: {} }),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('../../config/extension-manager.js', () => ({
|
||||
ExtensionManager: vi.fn().mockImplementation(() => mockExtensionManager),
|
||||
@@ -61,10 +56,13 @@ vi.mock('../utils.js', () => ({
|
||||
exitCli: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('./utils.js', () => ({
|
||||
getExtensionAndManager: mockGetExtensionAndManager,
|
||||
getExtensionManager: mockGetExtensionManager,
|
||||
}));
|
||||
vi.mock('./utils.js', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('./utils.js')>();
|
||||
return {
|
||||
...actual,
|
||||
getExtensionManager: mockGetExtensionManager,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('prompts');
|
||||
|
||||
@@ -79,17 +77,17 @@ vi.mock('../../config/settings.js', () => ({
|
||||
}));
|
||||
|
||||
describe('extensions configure command', () => {
|
||||
let tempWorkspaceDir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.spyOn(debugLogger, 'log');
|
||||
vi.spyOn(debugLogger, 'error');
|
||||
vi.clearAllMocks();
|
||||
|
||||
tempWorkspaceDir = fs.mkdtempSync('gemini-cli-test-workspace');
|
||||
vi.spyOn(process, 'cwd').mockReturnValue(tempWorkspaceDir);
|
||||
// Default behaviors
|
||||
mockLoadSettings.mockReturnValue({ merged: {} });
|
||||
mockGetExtensionAndManager.mockResolvedValue({
|
||||
extension: null,
|
||||
extensionManager: null,
|
||||
});
|
||||
mockGetExtensionManager.mockResolvedValue(mockExtensionManager);
|
||||
(ExtensionManager as unknown as Mock).mockImplementation(
|
||||
() => mockExtensionManager,
|
||||
@@ -112,11 +110,6 @@ describe('extensions configure command', () => {
|
||||
path = '/test/path',
|
||||
) => {
|
||||
const extension = { name, path, id };
|
||||
mockGetExtensionAndManager.mockImplementation(async (n) => {
|
||||
if (n === name)
|
||||
return { extension, extensionManager: mockExtensionManager };
|
||||
return { extension: null, extensionManager: null };
|
||||
});
|
||||
|
||||
mockExtensionManager.getExtensions.mockReturnValue([extension]);
|
||||
mockExtensionManager.loadExtensionConfig.mockResolvedValue({
|
||||
@@ -139,16 +132,14 @@ describe('extensions configure command', () => {
|
||||
expect.objectContaining({ name: 'test-ext' }),
|
||||
'test-id',
|
||||
'TEST_VAR',
|
||||
promptForSetting,
|
||||
expect.any(Function),
|
||||
'user',
|
||||
tempWorkspaceDir,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle missing extension', async () => {
|
||||
mockGetExtensionAndManager.mockResolvedValue({
|
||||
extension: null,
|
||||
extensionManager: null,
|
||||
});
|
||||
mockExtensionManager.getExtensions.mockReturnValue([]);
|
||||
|
||||
await runCommand('config missing-ext TEST_VAR');
|
||||
|
||||
@@ -184,8 +175,9 @@ describe('extensions configure command', () => {
|
||||
expect.objectContaining({ name: 'test-ext' }),
|
||||
'test-id',
|
||||
'VAR_1',
|
||||
promptForSetting,
|
||||
expect.any(Function),
|
||||
'user',
|
||||
tempWorkspaceDir,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -198,7 +190,7 @@ describe('extensions configure command', () => {
|
||||
return {};
|
||||
},
|
||||
);
|
||||
(prompts as unknown as Mock).mockResolvedValue({ overwrite: true });
|
||||
(prompts as unknown as Mock).mockResolvedValue({ confirm: true });
|
||||
(updateSetting as Mock).mockResolvedValue(undefined);
|
||||
|
||||
await runCommand('config test-ext');
|
||||
@@ -234,7 +226,7 @@ describe('extensions configure command', () => {
|
||||
const settings = [{ name: 'Setting 1', envVar: 'VAR_1' }];
|
||||
setupExtension('test-ext', settings);
|
||||
(getScopedEnvContents as Mock).mockResolvedValue({ VAR_1: 'existing' });
|
||||
(prompts as unknown as Mock).mockResolvedValue({ overwrite: false });
|
||||
(prompts as unknown as Mock).mockResolvedValue({ confirm: false });
|
||||
|
||||
await runCommand('config test-ext');
|
||||
|
||||
|
||||
@@ -5,18 +5,17 @@
|
||||
*/
|
||||
|
||||
import type { CommandModule } from 'yargs';
|
||||
import type { ExtensionSettingScope } from '../../config/extensions/extensionSettings.js';
|
||||
import {
|
||||
updateSetting,
|
||||
promptForSetting,
|
||||
ExtensionSettingScope,
|
||||
getScopedEnvContents,
|
||||
} from '../../config/extensions/extensionSettings.js';
|
||||
import { getExtensionAndManager, getExtensionManager } from './utils.js';
|
||||
configureAllExtensions,
|
||||
configureExtension,
|
||||
configureSpecificSetting,
|
||||
getExtensionManager,
|
||||
} from './utils.js';
|
||||
import { loadSettings } from '../../config/settings.js';
|
||||
import { debugLogger, coreEvents } from '@google/gemini-cli-core';
|
||||
import { coreEvents, debugLogger } from '@google/gemini-cli-core';
|
||||
import { exitCli } from '../utils.js';
|
||||
import prompts from 'prompts';
|
||||
import type { ExtensionConfig } from '../../config/extension.js';
|
||||
|
||||
interface ConfigureArgs {
|
||||
name?: string;
|
||||
setting?: string;
|
||||
@@ -64,160 +63,36 @@ export const configureCommand: CommandModule<object, ConfigureArgs> = {
|
||||
}
|
||||
}
|
||||
|
||||
const extensionManager = await getExtensionManager();
|
||||
|
||||
// Case 1: Configure specific setting for an extension
|
||||
if (name && setting) {
|
||||
await configureSpecificSetting(
|
||||
extensionManager,
|
||||
name,
|
||||
setting,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
scope as ExtensionSettingScope,
|
||||
);
|
||||
}
|
||||
// Case 2: Configure all settings for an extension
|
||||
else if (name) {
|
||||
await configureExtension(name, scope as ExtensionSettingScope);
|
||||
await configureExtension(
|
||||
extensionManager,
|
||||
name,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
scope as ExtensionSettingScope,
|
||||
);
|
||||
}
|
||||
// Case 3: Configure all extensions
|
||||
else {
|
||||
await configureAllExtensions(scope as ExtensionSettingScope);
|
||||
await configureAllExtensions(
|
||||
extensionManager,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
scope as ExtensionSettingScope,
|
||||
);
|
||||
}
|
||||
|
||||
await exitCli();
|
||||
},
|
||||
};
|
||||
|
||||
async function configureSpecificSetting(
|
||||
extensionName: string,
|
||||
settingKey: string,
|
||||
scope: ExtensionSettingScope,
|
||||
) {
|
||||
const { extension, extensionManager } =
|
||||
await getExtensionAndManager(extensionName);
|
||||
if (!extension || !extensionManager) {
|
||||
return;
|
||||
}
|
||||
const extensionConfig = await extensionManager.loadExtensionConfig(
|
||||
extension.path,
|
||||
);
|
||||
if (!extensionConfig) {
|
||||
debugLogger.error(
|
||||
`Could not find configuration for extension "${extensionName}".`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await updateSetting(
|
||||
extensionConfig,
|
||||
extension.id,
|
||||
settingKey,
|
||||
promptForSetting,
|
||||
scope,
|
||||
);
|
||||
}
|
||||
|
||||
async function configureExtension(
|
||||
extensionName: string,
|
||||
scope: ExtensionSettingScope,
|
||||
) {
|
||||
const { extension, extensionManager } =
|
||||
await getExtensionAndManager(extensionName);
|
||||
if (!extension || !extensionManager) {
|
||||
return;
|
||||
}
|
||||
const extensionConfig = await extensionManager.loadExtensionConfig(
|
||||
extension.path,
|
||||
);
|
||||
if (
|
||||
!extensionConfig ||
|
||||
!extensionConfig.settings ||
|
||||
extensionConfig.settings.length === 0
|
||||
) {
|
||||
debugLogger.log(
|
||||
`Extension "${extensionName}" has no settings to configure.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
debugLogger.log(`Configuring settings for "${extensionName}"...`);
|
||||
await configureExtensionSettings(extensionConfig, extension.id, scope);
|
||||
}
|
||||
|
||||
async function configureAllExtensions(scope: ExtensionSettingScope) {
|
||||
const extensionManager = await getExtensionManager();
|
||||
const extensions = extensionManager.getExtensions();
|
||||
|
||||
if (extensions.length === 0) {
|
||||
debugLogger.log('No extensions installed.');
|
||||
return;
|
||||
}
|
||||
|
||||
for (const extension of extensions) {
|
||||
const extensionConfig = await extensionManager.loadExtensionConfig(
|
||||
extension.path,
|
||||
);
|
||||
if (
|
||||
extensionConfig &&
|
||||
extensionConfig.settings &&
|
||||
extensionConfig.settings.length > 0
|
||||
) {
|
||||
debugLogger.log(`\nConfiguring settings for "${extension.name}"...`);
|
||||
await configureExtensionSettings(extensionConfig, extension.id, scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function configureExtensionSettings(
|
||||
extensionConfig: ExtensionConfig,
|
||||
extensionId: string,
|
||||
scope: ExtensionSettingScope,
|
||||
) {
|
||||
const currentScopedSettings = await getScopedEnvContents(
|
||||
extensionConfig,
|
||||
extensionId,
|
||||
scope,
|
||||
process.cwd(),
|
||||
);
|
||||
|
||||
let workspaceSettings: Record<string, string> = {};
|
||||
if (scope === ExtensionSettingScope.USER) {
|
||||
workspaceSettings = await getScopedEnvContents(
|
||||
extensionConfig,
|
||||
extensionId,
|
||||
ExtensionSettingScope.WORKSPACE,
|
||||
process.cwd(),
|
||||
);
|
||||
}
|
||||
|
||||
if (!extensionConfig.settings) return;
|
||||
|
||||
for (const setting of extensionConfig.settings) {
|
||||
const currentValue = currentScopedSettings[setting.envVar];
|
||||
const workspaceValue = workspaceSettings[setting.envVar];
|
||||
|
||||
if (workspaceValue !== undefined) {
|
||||
debugLogger.log(
|
||||
`Note: Setting "${setting.name}" is already configured in the workspace scope.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (currentValue !== undefined) {
|
||||
const response = await prompts({
|
||||
type: 'confirm',
|
||||
name: 'overwrite',
|
||||
message: `Setting "${setting.name}" (${setting.envVar}) is already set. Overwrite?`,
|
||||
initial: false,
|
||||
});
|
||||
|
||||
if (!response.overwrite) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
await updateSetting(
|
||||
extensionConfig,
|
||||
extensionId,
|
||||
setting.envVar,
|
||||
promptForSetting,
|
||||
scope,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,9 @@ export const disableCommand: CommandModule = {
|
||||
}),
|
||||
handler: async (argv) => {
|
||||
await handleDisable({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
name: argv['name'] as string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
scope: argv['scope'] as string,
|
||||
});
|
||||
await exitCli();
|
||||
|
||||
@@ -105,7 +105,9 @@ export const enableCommand: CommandModule = {
|
||||
}),
|
||||
handler: async (argv) => {
|
||||
await handleEnable({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
name: argv['name'] as string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
scope: argv['scope'] as string,
|
||||
});
|
||||
await exitCli();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user