google-gemini/gemini-cli
An agent editing this repo should treat packages/core and packages/cli as separate layers (core must not import cli), keep the Config god-object edits minimal, route all tool gating through the PolicyEngine/MessageBus rather than ad-hoc checks, and always run the colocated vitest file plus typecheck before considering a change done. Build is `npm run bundle` (esbuild → bundle/gemini.js); quick dev loop is `npm run start` / `npm start -- -p '...'` after build. Conventions: ESM with .js import specifiers for .ts files, Apache license header enforced by eslint, debugLogger instead of console.*, proper-lockfile/proper JSONL append for session files. Traps: two-process relaunch means env changes need GEMINI_CLI_NO_RELAUNCH=1 to debug the real process; posttest triggers a full build; Ink is a forked package via npm overrides.
- Read-first files for any agentic-loop change: packages/core/src/core/client.ts (GeminiClient), turn.ts (event types Turn/GeminiEventType), geminiChat.ts (stream+retry), scheduler/scheduler.ts (tool execution), config/config.ts (wiring). CLI-side entry: packages/cli/index.ts then src/gemini.tsx main().
- Build/dev commands: `npm run bundle` produces bundle/gemini.js (esbuild, includes generated git commit info and devtools bundle); `npm run start` runs scripts/start.js with NODE_ENV=development; `npm run build` builds packages only.
- Test commands: `npm run test:ci --workspace @google/gemini-cli-core` for core unit tests; `npm run test:integration:sandbox:none` for e2e without sandbox; `npm run typecheck` across workspaces; `npm run preflight` = clean+ci+format+build+lint+typecheck+test:ci (slow, full gate).
- Imports must use ESM `.js` specifiers referencing the compiled names (e.g. './utils/cleanup.js'), the repo is "type":"module" everywhere, and every file needs the SPDX Apache header enforced by eslint-plugin-headers.
- Logging convention: never use console.log/console.error in library paths — ConsolePatcher intercepts them; use debugLogger (debug/error/warn) for diagnostics and coreEvents.emitFeedback for user-facing messages so headless JSON output stays clean.
- Tool-approval changes must go through the PolicyEngine + MessageBus confirmation flow (checkPolicy → resolveConfirmation → MessageBusType.ToolConfirmationRequest); bypassing it (e.g. auto-executing in a new tool) defeats --approval-mode/--yolo/plan-mode semantics and enterprise admin policies.
- Safe edit boundaries: new tools go in packages/core/src/tools/ + registration in config.ts createToolRegistry; new slash commands in packages/cli/src/ui/commands/ + BuiltinCommandLoader; new settings keys in settingsSchema.ts (docs regenerated via npm run docs:settings, which CI verifies with --check).
- Debugging trap: the CLI double-forks — the visible process is a thin parent; to debug or test the real logic run with GEMINI_CLI_NO_RELAUNCH=1 (or `npm run debug` which uses --inspect-brk), otherwise breakpoints attach to the wrong process and stdin/stdio patching differs.
- Trap: `npm test` at the root runs a build afterwards (posttest → npm run build) and the prepare script runs `husky && npm run bundle`, so npm install/test are not side-effect-free; the package-lock is guarded by check:lockfile in CI.
- Layering rule: packages/cli depends on @google/gemini-cli-core (file:../core) and everything cross-layer is exported through packages/core/src/index.ts; a2a-server and sdk also consume core only through that barrel — new core APIs must be re-exported there to be usable.
- Ink is pinned to a fork (@jrichman/ink@6.6.9) via npm overrides with wrap-ansi/cliui pinned too — do not upgrade ink casually; TUI tests rely on the fork's behavior (react-devtools-core in devDeps).
- Session transcript writes must remain append-only JSONL with proper record shapes (MessageRecord/$set/$rewindTo/$partial metadata guards in chatRecordingService); --resume and rewind depend on parsing these exact line types.
- Enterprise/admin settings arrive via parent→child IPC and CCPA fetch in refreshAuth; features must consult admin flags (e.g. getAdminBlockedMcpServersMessage, adminSkillsEnabled, applyAdminAllowlist) rather than only user settings, or enterprise controls are bypassed.