mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-10 22:21:22 -07:00
421 lines
21 KiB
Markdown
421 lines
21 KiB
Markdown
## Building and running
|
|
|
|
Before submitting any changes, it is crucial to validate them by running the
|
|
full preflight check. This command will build the repository, run all tests,
|
|
check for type errors, and lint the code.
|
|
|
|
To run the full suite of checks, execute the following command:
|
|
|
|
```bash
|
|
npm run preflight
|
|
```
|
|
|
|
This single command ensures that your changes meet all the quality gates of the
|
|
project. While you can run the individual steps (`build`, `test`, `typecheck`,
|
|
`lint`) separately, it is highly recommended to use `npm run preflight` to
|
|
ensure a comprehensive validation.
|
|
|
|
## Running Tests in Workspaces\*\*: To run a specific test file within a
|
|
|
|
workspace, use the command:
|
|
`npm test -w <workspace-name> -- <path/to/test-file.test.ts>`. **CRITICAL**: The
|
|
`<path/to/test-file.test.ts>` MUST be relative to the workspace directory root,
|
|
NOT the project root.
|
|
|
|
- _Example (Core package)_:
|
|
`npm test -w @google/gemini-cli-core -- src/routing/modelRouterService.test.ts`
|
|
- _Common workspaces_: `@google/gemini-cli`, `@google/gemini-cli-core`.
|
|
|
|
## Writing Tests
|
|
|
|
This project uses **Vitest** as its primary testing framework. When writing
|
|
tests, aim to follow existing patterns. Key conventions include:
|
|
|
|
### Test Structure and Framework
|
|
|
|
- **Framework**: All tests are written using Vitest (`describe`, `it`, `expect`,
|
|
`vi`).
|
|
- **File Location**: Test files (`*.test.ts` for logic, `*.test.tsx` for React
|
|
components) are co-located with the source files they test.
|
|
- **Configuration**: Test environments are defined in `vitest.config.ts` files.
|
|
- **Setup/Teardown**: Use `beforeEach` and `afterEach`. Commonly,
|
|
`vi.resetAllMocks()` is called in `beforeEach` and `vi.restoreAllMocks()` in
|
|
`afterEach`.
|
|
|
|
### Mocking (`vi` from Vitest)
|
|
|
|
- **ES Modules**: Mock with
|
|
`vi.mock('module-name', async (importOriginal) => { ... })`. Use
|
|
`importOriginal` for selective mocking.
|
|
- _Example_:
|
|
`vi.mock('os', async (importOriginal) => { const actual = await importOriginal(); return { ...actual, homedir: vi.fn() }; });`
|
|
- **Mocking Order**: For critical dependencies (e.g., `os`, `fs`) that affect
|
|
module-level constants, place `vi.mock` at the _very top_ of the test file,
|
|
before other imports.
|
|
- **Hoisting**: Use `const myMock = vi.hoisted(() => vi.fn());` if a mock
|
|
function needs to be defined before its use in a `vi.mock` factory.
|
|
- **Mock Functions**: Create with `vi.fn()`. Define behavior with
|
|
`mockImplementation()`, `mockResolvedValue()`, or `mockRejectedValue()`.
|
|
- **Spying**: Use `vi.spyOn(object, 'methodName')`. Restore spies with
|
|
`mockRestore()` in `afterEach`.
|
|
|
|
### Commonly Mocked Modules
|
|
|
|
- **Node.js built-ins**: `fs`, `fs/promises`, `os` (especially `os.homedir()`),
|
|
`path`, `child_process` (`execSync`, `spawn`).
|
|
- **External SDKs**: `@google/genai`, `@modelcontextprotocol/sdk`.
|
|
- **Internal Project Modules**: Dependencies from other project packages are
|
|
often mocked.
|
|
|
|
### React Component Testing (CLI UI - Ink)
|
|
|
|
- Use `render()` from `ink-testing-library`.
|
|
- Assert output with `lastFrame()`.
|
|
- Wrap components in necessary `Context.Provider`s.
|
|
- Mock custom React hooks and complex child components using `vi.mock()`.
|
|
|
|
### Asynchronous Testing
|
|
|
|
- Use `async/await`.
|
|
- For timers, use `vi.useFakeTimers()`, `vi.advanceTimersByTimeAsync()`,
|
|
`vi.runAllTimersAsync()`.
|
|
- Test promise rejections with `await expect(promise).rejects.toThrow(...)`.
|
|
|
|
### General Guidance
|
|
|
|
- When adding tests, first examine existing tests to understand and conform to
|
|
established conventions.
|
|
- Pay close attention to the mocks at the top of existing test files; they
|
|
reveal critical dependencies and how they are managed in a test environment.
|
|
|
|
## Git Repo
|
|
|
|
The main branch for this project is called "main"
|
|
|
|
## JavaScript/TypeScript
|
|
|
|
When contributing to this React, Node, and TypeScript codebase, please
|
|
prioritize the use of plain JavaScript objects with accompanying TypeScript
|
|
interface or type declarations over JavaScript class syntax. This approach
|
|
offers significant advantages, especially concerning interoperability with React
|
|
and overall code maintainability.
|
|
|
|
### Preferring Plain Objects over Classes
|
|
|
|
JavaScript classes, by their nature, are designed to encapsulate internal state
|
|
and behavior. While this can be useful in some object-oriented paradigms, it
|
|
often introduces unnecessary complexity and friction when working with React's
|
|
component-based architecture. Here's why plain objects are preferred:
|
|
|
|
- Seamless React Integration: React components thrive on explicit props and
|
|
state management. Classes' tendency to store internal state directly within
|
|
instances can make prop and state propagation harder to reason about and
|
|
maintain. Plain objects, on the other hand, are inherently immutable (when
|
|
used thoughtfully) and can be easily passed as props, simplifying data flow
|
|
and reducing unexpected side effects.
|
|
|
|
- Reduced Boilerplate and Increased Conciseness: Classes often promote the use
|
|
of constructors, this binding, getters, setters, and other boilerplate that
|
|
can unnecessarily bloat code. TypeScript interface and type declarations
|
|
provide powerful static type checking without the runtime overhead or
|
|
verbosity of class definitions. This allows for more succinct and readable
|
|
code, aligning with JavaScript's strengths in functional programming.
|
|
|
|
- Enhanced Readability and Predictability: Plain objects, especially when their
|
|
structure is clearly defined by TypeScript interfaces, are often easier to
|
|
read and understand. Their properties are directly accessible, and there's no
|
|
hidden internal state or complex inheritance chains to navigate. This
|
|
predictability leads to fewer bugs and a more maintainable codebase.
|
|
|
|
- Simplified Immutability: While not strictly enforced, plain objects encourage
|
|
an immutable approach to data. When you need to modify an object, you
|
|
typically create a new one with the desired changes, rather than mutating the
|
|
original. This pattern aligns perfectly with React's reconciliation process
|
|
and helps prevent subtle bugs related to shared mutable state.
|
|
|
|
- Better Serialization and Deserialization: Plain JavaScript objects are
|
|
naturally easy to serialize to JSON and deserialize back, which is a common
|
|
requirement in web development (e.g., for API communication or local storage).
|
|
Classes, with their methods and prototypes, can complicate this process.
|
|
|
|
### Embracing ES Module Syntax for Encapsulation
|
|
|
|
Rather than relying on Java-esque private or public class members, which can be
|
|
verbose and sometimes limit flexibility, we strongly prefer leveraging ES module
|
|
syntax (`import`/`export`) for encapsulating private and public APIs.
|
|
|
|
- Clearer Public API Definition: With ES modules, anything that is exported is
|
|
part of the public API of that module, while anything not exported is
|
|
inherently private to that module. This provides a very clear and explicit way
|
|
to define what parts of your code are meant to be consumed by other modules.
|
|
|
|
- Enhanced Testability (Without Exposing Internals): By default, unexported
|
|
functions or variables are not accessible from outside the module. This
|
|
encourages you to test the public API of your modules, rather than their
|
|
internal implementation details. If you find yourself needing to spy on or
|
|
stub an unexported function for testing purposes, it's often a "code smell"
|
|
indicating that the function might be a good candidate for extraction into its
|
|
own separate, testable module with a well-defined public API. This promotes a
|
|
more robust and maintainable testing strategy.
|
|
|
|
- Reduced Coupling: Explicitly defined module boundaries through import/export
|
|
help reduce coupling between different parts of your codebase. This makes it
|
|
easier to refactor, debug, and understand individual components in isolation.
|
|
|
|
### Avoiding `any` Types and Type Assertions; Preferring `unknown`
|
|
|
|
TypeScript's power lies in its ability to provide static type checking, catching
|
|
potential errors before your code runs. To fully leverage this, it's crucial to
|
|
avoid the `any` type and be judicious with type assertions.
|
|
|
|
- **The Dangers of `any`**: Using any effectively opts out of TypeScript's type
|
|
checking for that particular variable or expression. While it might seem
|
|
convenient in the short term, it introduces significant risks:
|
|
- **Loss of Type Safety**: You lose all the benefits of type checking, making
|
|
it easy to introduce runtime errors that TypeScript would otherwise have
|
|
caught.
|
|
- **Reduced Readability and Maintainability**: Code with `any` types is harder
|
|
to understand and maintain, as the expected type of data is no longer
|
|
explicitly defined.
|
|
- **Masking Underlying Issues**: Often, the need for any indicates a deeper
|
|
problem in the design of your code or the way you're interacting with
|
|
external libraries. It's a sign that you might need to refine your types or
|
|
refactor your code.
|
|
|
|
- **Preferring `unknown` over `any`**: When you absolutely cannot determine the
|
|
type of a value at compile time, and you're tempted to reach for any, consider
|
|
using unknown instead. unknown is a type-safe counterpart to any. While a
|
|
variable of type unknown can hold any value, you must perform type narrowing
|
|
(e.g., using typeof or instanceof checks, or a type assertion) before you can
|
|
perform any operations on it. This forces you to handle the unknown type
|
|
explicitly, preventing accidental runtime errors.
|
|
|
|
```ts
|
|
function processValue(value: unknown) {
|
|
if (typeof value === 'string') {
|
|
// value is now safely a string
|
|
console.log(value.toUpperCase());
|
|
} else if (typeof value === 'number') {
|
|
// value is now safely a number
|
|
console.log(value * 2);
|
|
}
|
|
// Without narrowing, you cannot access properties or methods on 'value'
|
|
// console.log(value.someProperty); // Error: Object is of type 'unknown'.
|
|
}
|
|
```
|
|
|
|
- **Type Assertions (`as Type`) - Use with Caution**: Type assertions tell the
|
|
TypeScript compiler, "Trust me, I know what I'm doing; this is definitely of
|
|
this type." While there are legitimate use cases (e.g., when dealing with
|
|
external libraries that don't have perfect type definitions, or when you have
|
|
more information than the compiler), they should be used sparingly and with
|
|
extreme caution.
|
|
- **Bypassing Type Checking**: Like `any`, type assertions bypass TypeScript's
|
|
safety checks. If your assertion is incorrect, you introduce a runtime error
|
|
that TypeScript would not have warned you about.
|
|
- **Code Smell in Testing**: A common scenario where `any` or type assertions
|
|
might be tempting is when trying to test "private" implementation details
|
|
(e.g., spying on or stubbing an unexported function within a module). This
|
|
is a strong indication of a "code smell" in your testing strategy and
|
|
potentially your code structure. Instead of trying to force access to
|
|
private internals, consider whether those internal details should be
|
|
refactored into a separate module with a well-defined public API. This makes
|
|
them inherently testable without compromising encapsulation.
|
|
|
|
### Type narrowing `switch` clauses
|
|
|
|
Use the `checkExhaustive` helper in the default clause of a switch statement.
|
|
This will ensure that all of the possible options within the value or
|
|
enumeration are used.
|
|
|
|
This helper method can be found in `packages/cli/src/utils/checks.ts`
|
|
|
|
### Embracing JavaScript's Array Operators
|
|
|
|
To further enhance code cleanliness and promote safe functional programming
|
|
practices, leverage JavaScript's rich set of array operators as much as
|
|
possible. Methods like `.map()`, `.filter()`, `.reduce()`, `.slice()`,
|
|
`.sort()`, and others are incredibly powerful for transforming and manipulating
|
|
data collections in an immutable and declarative way.
|
|
|
|
Using these operators:
|
|
|
|
- Promotes Immutability: Most array operators return new arrays, leaving the
|
|
original array untouched. This functional approach helps prevent unintended
|
|
side effects and makes your code more predictable.
|
|
- Improves Readability: Chaining array operators often lead to more concise and
|
|
expressive code than traditional for loops or imperative logic. The intent of
|
|
the operation is clear at a glance.
|
|
- Facilitates Functional Programming: These operators are cornerstones of
|
|
functional programming, encouraging the creation of pure functions that take
|
|
inputs and produce outputs without causing side effects. This paradigm is
|
|
highly beneficial for writing robust and testable code that pairs well with
|
|
React.
|
|
|
|
By consistently applying these principles, we can maintain a codebase that is
|
|
not only efficient and performant but also a joy to work with, both now and in
|
|
the future.
|
|
|
|
## React (mirrored and adjusted from [react-mcp-server](https://github.com/facebook/react/blob/4448b18760d867f9e009e810571e7a3b8930bb19/compiler/packages/react-mcp-server/src/index.ts#L376C1-L441C94))
|
|
|
|
### Role
|
|
|
|
You are a React assistant that helps users write more efficient and optimizable
|
|
React code. You specialize in identifying patterns that enable React Compiler to
|
|
automatically apply optimizations, reducing unnecessary re-renders and improving
|
|
application performance.
|
|
|
|
### Follow these guidelines in all code you produce and suggest
|
|
|
|
Use functional components with Hooks: Do not generate class components or use
|
|
old lifecycle methods. Manage state with useState or useReducer, and side
|
|
effects with useEffect (or related Hooks). Always prefer functions and Hooks for
|
|
any new component logic.
|
|
|
|
Keep components pure and side-effect-free during rendering: Do not produce code
|
|
that performs side effects (like subscriptions, network requests, or modifying
|
|
external variables) directly inside the component's function body. Such actions
|
|
should be wrapped in useEffect or performed in event handlers. Ensure your
|
|
render logic is a pure function of props and state.
|
|
|
|
Respect one-way data flow: Pass data down through props and avoid any global
|
|
mutations. If two components need to share data, lift that state up to a common
|
|
parent or use React Context, rather than trying to sync local state or use
|
|
external variables.
|
|
|
|
Never mutate state directly: Always generate code that updates state immutably.
|
|
For example, use spread syntax or other methods to create new objects/arrays
|
|
when updating state. Do not use assignments like state.someValue = ... or array
|
|
mutations like array.push() on state variables. Use the state setter (setState
|
|
from useState, etc.) to update state.
|
|
|
|
Accurately use useEffect and other effect Hooks: whenever you think you could
|
|
useEffect, think and reason harder to avoid it. useEffect is primarily only used
|
|
for synchronization, for example synchronizing React with some external state.
|
|
IMPORTANT - Don't setState (the 2nd value returned by useState) within a
|
|
useEffect as that will degrade performance. When writing effects, include all
|
|
necessary dependencies in the dependency array. Do not suppress ESLint rules or
|
|
omit dependencies that the effect's code uses. Structure the effect callbacks to
|
|
handle changing values properly (e.g., update subscriptions on prop changes,
|
|
clean up on unmount or dependency change). If a piece of logic should only run
|
|
in response to a user action (like a form submission or button click), put that
|
|
logic in an event handler, not in a useEffect. Where possible, useEffects should
|
|
return a cleanup function.
|
|
|
|
Follow the Rules of Hooks: Ensure that any Hooks (useState, useEffect,
|
|
useContext, custom Hooks, etc.) are called unconditionally at the top level of
|
|
React function components or other Hooks. Do not generate code that calls Hooks
|
|
inside loops, conditional statements, or nested helper functions. Do not call
|
|
Hooks in non-component functions or outside the React component rendering
|
|
context.
|
|
|
|
Use refs only when necessary: Avoid using useRef unless the task genuinely
|
|
requires it (such as focusing a control, managing an animation, or integrating
|
|
with a non-React library). Do not use refs to store application state that
|
|
should be reactive. If you do use refs, never write to or read from ref.current
|
|
during the rendering of a component (except for initial setup like lazy
|
|
initialization). Any ref usage should not affect the rendered output directly.
|
|
|
|
Prefer composition and small components: Break down UI into small, reusable
|
|
components rather than writing large monolithic components. The code you
|
|
generate should promote clarity and reusability by composing components
|
|
together. Similarly, abstract repetitive logic into custom Hooks when
|
|
appropriate to avoid duplicating code.
|
|
|
|
Optimize for concurrency: Assume React may render your components multiple times
|
|
for scheduling purposes (especially in development with Strict Mode). Write code
|
|
that remains correct even if the component function runs more than once. For
|
|
instance, avoid side effects in the component body and use functional state
|
|
updates (e.g., setCount(c => c + 1)) when updating state based on previous state
|
|
to prevent race conditions. Always include cleanup functions in effects that
|
|
subscribe to external resources. Don't write useEffects for "do this when this
|
|
changes" side effects. This ensures your generated code will work with React's
|
|
concurrent rendering features without issues.
|
|
|
|
Optimize to reduce network waterfalls - Use parallel data fetching wherever
|
|
possible (e.g., start multiple requests at once rather than one after another).
|
|
Leverage Suspense for data loading and keep requests co-located with the
|
|
component that needs the data. In a server-centric approach, fetch related data
|
|
together in a single request on the server side (using Server Components, for
|
|
example) to reduce round trips. Also, consider using caching layers or global
|
|
fetch management to avoid repeating identical requests.
|
|
|
|
Rely on React Compiler - useMemo, useCallback, and React.memo can be omitted if
|
|
React Compiler is enabled. Avoid premature optimization with manual memoization.
|
|
Instead, focus on writing clear, simple components with direct data flow and
|
|
side-effect-free render functions. Let the React Compiler handle tree-shaking,
|
|
inlining, and other performance enhancements to keep your code base simpler and
|
|
more maintainable.
|
|
|
|
Design for a good user experience - Provide clear, minimal, and non-blocking UI
|
|
states. When data is loading, show lightweight placeholders (e.g., skeleton
|
|
screens) rather than intrusive spinners everywhere. Handle errors gracefully
|
|
with a dedicated error boundary or a friendly inline message. Where possible,
|
|
render partial data as it becomes available rather than making the user wait for
|
|
everything. Suspense allows you to declare the loading states in your component
|
|
tree in a natural way, preventing “flash” states and improving perceived
|
|
performance.
|
|
|
|
### Process
|
|
|
|
1. Analyze the user's code for optimization opportunities:
|
|
- Check for React anti-patterns that prevent compiler optimization
|
|
- Look for component structure issues that limit compiler effectiveness
|
|
- Think about each suggestion you are making and consult React docs for best
|
|
practices
|
|
|
|
2. Provide actionable guidance:
|
|
- Explain specific code changes with clear reasoning
|
|
- Show before/after examples when suggesting changes
|
|
- Only suggest changes that meaningfully improve optimization potential
|
|
|
|
### Optimization Guidelines
|
|
|
|
- State updates should be structured to enable granular updates
|
|
- Side effects should be isolated and dependencies clearly defined
|
|
|
|
## Documentation guidelines
|
|
|
|
When working in the `/docs` directory, follow the guidelines in this section:
|
|
|
|
- **Role:** You are an expert technical writer and AI assistant for contributors
|
|
to Gemini CLI. Produce professional, accurate, and consistent documentation to
|
|
guide users of Gemini CLI.
|
|
- **Technical Accuracy:** Do not invent facts, commands, code, API names, or
|
|
output. All technical information specific to Gemini CLI must be based on code
|
|
found within this directory and its subdirectories.
|
|
- **Style Authority:** Your source for writing guidance and style is the
|
|
"Documentation contribution process" section in the root directory's
|
|
`CONTRIBUTING.md` file, as well as any guidelines provided this section.
|
|
- **Information Architecture Consideration:** Before proposing documentation
|
|
changes, consider the information architecture. If a change adds significant
|
|
new content to existing documents, evaluate if creating a new, more focused
|
|
page or changes to `sidebar.json` would provide a better user experience.
|
|
- **Proactive User Consideration:** The user experience should be a primary
|
|
concern when making changes to documentation. Aim to fill gaps in existing
|
|
knowledge whenever possible while keeping documentation concise and easy for
|
|
users to understand. If changes might hinder user understanding or
|
|
accessibility, proactively raise these concerns and propose alternatives.
|
|
|
|
## Comments policy
|
|
|
|
Only write high-value comments if at all. Avoid talking to the user through
|
|
comments.
|
|
|
|
## Logging and Error Handling
|
|
|
|
- **Avoid Console Statements:** Do not use `console.log`, `console.error`, or
|
|
similar methods directly.
|
|
- **Non-User-Facing Logs:** For developer-facing debug messages, use
|
|
`debugLogger` (from `@google/gemini-cli-core`).
|
|
- **User-Facing Feedback:** To surface errors or warnings to the user, use
|
|
`coreEvents.emitFeedback` (from `@google/gemini-cli-core`).
|
|
|
|
## General requirements
|
|
|
|
- If there is something you do not understand or is ambiguous, seek confirmation
|
|
or clarification from the user before making changes based on assumptions.
|
|
- Use hyphens instead of underscores in flag names (e.g. `my-flag` instead of
|
|
`my_flag`).
|
|
- Always refer to Gemini CLI as `Gemini CLI`, never `the Gemini CLI`.
|