.gemini/commands/strict-development-rules.md
These rules apply strictly to all code modifications and additions within the Gemini CLI project.
waitFor from
packages/cli/src/test-utils/async.ts instead of vi.waitFor for all
waitFor calls within packages/cli. NEVER use fixed waits (e.g.,
await delay(100)). Always use waitFor with a predicate to ensure tests are
stable and fast. Using the wrong waitFor can result in flaky tests and act
warnings.act to wrap all blocks in tests that change component
state. Use render or renderWithProviders from
packages/cli/src/test-utils/render.tsx instead of render from
ink-testing-library directly. This prevents spurious act warnings. If test
cases specify providers directly, consider whether the existing
renderWithProviders should be modified.toMatchSnapshot to verify that rendering works as
expected rather than matching against the raw content of the output. When
modifying snapshots, verify the changes are intentional and do not hide
underlying bugs.fs, os, child_process) ONLY at the top of
the file. Ideally, avoid mocking these dependencies altogether.vi.restoreAllMocks() in afterEach to prevent test pollution.vi.useFakeTimers() for tests involving time-based logic to avoid
flakiness.any in tests; prefer proper types or
unknown with narrowing.packages/cli)setState and Side Effects: NEVER trigger side effects from within the
body of a setState callback. Use a reducer or useRef if necessary. These
cases have historically introduced multiple bugs; typically, they should be
resolved using a reducer.ResizeObserver as needed.useKeyPress.ts from
the Gemini CLI package rather than the standard ink library. This library
supports reporting multiple keyboard events sequentially in the same React
frame (critical for slow terminals). Handling this correctly often requires
reducers to ensure multiple state updates are handled gracefully without
overriding values. Refer to text-buffer.ts for a canonical example.console.log, console.warn, or console.error in
the code.undefined
rather than true as a default if the state is truly unknown). Prefer a
reducer whenever practical. NEVER disable react-hooks/exhaustive-deps; fix
the code to correctly declare dependencies instead. Evaluate all the React
states in a component and ensure that the useState calls are necessary and
not cases where values could be derived on render. Ensure there are no stale
closures that are relying on a value from a previous render. React Components
that modify Settings should effectively use the useSettingsStore pattern.
Components that configure application Settings (e.g settings.json) are the
only reasonable case for unsaved changes to drive UX; in these cases, the
Settings store should only be written to on save. If the user experience does
not utilize unsaved changes because there is no option to exit without saving
or reverting the unsaved changes, then the component should directly read from
and write to the Settings store without holding pending changes in component
level UI state.useEffect should not be used to synchronize React states, it
should only be used for genuine side effects that occur outside of React.
Contributors should be able to strongly justify the need for an effect.
Consider whether the effect should instead be inside an event handler, or
whether it is better off being computed on render. Carefully manage
useEffect dependencies.if statements where switch statements
could be used. Keep AppContainer minimal; refactor complex logic into React
hooks. Evaluate whether business logic should be added to hookSystem.ts or
integrated into packages/core rather than packages/cli.packages/core)initialize() methods). Services should be stateless where possible,
or use the centralized Storage service for persistence.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.debugLogger from packages/core/src/utils/debugLogger.ts
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. Handle filesystem errors gracefully
using isNodeError from packages/core/src/utils/errors.ts.packages/core/src/tools/ and
register them in packages/core/src/tools/tool-registry.ts. Export all new
public services, utilities, and types from packages/core/src/index.ts.packages/core.
packages/cli should ONLY contain UI/Ink components, command-line argument
parsing, and user interaction logic.ConfirmationBus or Output abstractions for communicating with the user
from Core.coreEvents. If a service imports another just to notify it of a change, use
an event instead.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.debugLogger for rethrown errors to avoid duplicate logging.packages/cli/src/ui/key/keyBindings.ts and document them in
docs/cli/keyboard-shortcuts.md. Be careful of keybindings that require the
Meta key, as only certain meta key shortcuts are supported on Mac. Avoid
function keys and shortcuts commonly bound in VSCode.checkExhaustive in the default clause of switch statements to ensure
all cases are handled.!) unless absolutely necessary.any and unknown in both CLI and Core
packages. unknown is only allowed if it is immediately narrowed using type
guards or Zod validation.@typescript-eslint/no-floating-promises.KeypressContext.tsx and
terminalCapabilityManager.ts.ITERM_SESSION_ID may be present when users run
VSCode from within iTerm, even if the terminal is not iTerm.