Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
ff84679
docs: add codebase understanding guide
Samee24 Feb 26, 2026
760054e
docs: expand codebase understanding guide with technical depth
Samee24 Feb 26, 2026
f5b60c7
docs: finalize codebase understanding guide with advanced technical d…
Samee24 Feb 26, 2026
0f59d48
docs: add codebase understanding from antigravity
Samee24 Feb 26, 2026
bc6c644
Merge branch 'main' of https://github.com/google-gemini/gemini-cli
Samee24 Feb 27, 2026
99f2499
Merge branch 'main' of https://github.com/google-gemini/gemini-cli
Samee24 Mar 23, 2026
e930084
Merge branch 'main' of https://github.com/google-gemini/gemini-cli
Samee24 Apr 2, 2026
3dc0bb2
Merge branch 'main' of https://github.com/google-gemini/gemini-cli
Samee24 Apr 9, 2026
be2db23
Merge branch 'main' of https://github.com/google-gemini/gemini-cli
Samee24 Apr 10, 2026
7aec00d
Merge branch 'main' of https://github.com/google-gemini/gemini-cli
Samee24 Apr 10, 2026
dc30bd1
Merge branch 'main' of https://github.com/google-gemini/gemini-cli
Samee24 Apr 10, 2026
f7d4307
feat(cli): add `gemini gemma` command for streamlined local model setup
Samee24 Apr 10, 2026
8f023b5
feat(cli): add `gemini gemma logs` command to view LiteRT server logs
Samee24 Apr 10, 2026
82e87c4
docs: add gemma setup quick-start guide
Samee24 Apr 10, 2026
e0f043a
fix(core): set apiVersion to empty string for LiteRT-LM client
Samee24 Apr 13, 2026
ab8dc2d
Revert "fix(core): set apiVersion to empty string for LiteRT-LM client"
Samee24 Apr 13, 2026
d0dd169
feat(cli): show gemma router settings in /settings dialog
Samee24 Apr 13, 2026
8d3ac52
fix(gemma): resolve 404 errors and improve port resolution (#25340)
Abhijit-2592 Apr 13, 2026
c0117b4
chore(docs): regenerate settings schema and docs
Apr 15, 2026
509060e
rm docs
Apr 15, 2026
3b2243d
fix(cli): correctness and cross-platform fixes for gemma commands
Samee24 Apr 17, 2026
5223295
Merge branch 'main' into sameez/gemma-auto-setup
Samee24 Apr 17, 2026
85a5a97
chore: remove gemma setup guide doc for now
Samee24 Apr 17, 2026
eb5a3b9
chore: remove unnecessary comments across gemma commands
Samee24 Apr 17, 2026
c83376c
fix(cli): harden gemma router setup and server handling
Samee24 Apr 17, 2026
637af65
fix(cli): keep gemma logs attached to tail
Samee24 Apr 17, 2026
21bd3bd
Update packages/cli/src/commands/gemma/platform.ts
Samee24 Apr 17, 2026
6fed15f
Update packages/cli/src/gemini.tsx
Samee24 Apr 17, 2026
403a4c0
fix(cli): harden gemma setup and stop safety
Samee24 Apr 17, 2026
078aeb3
fix(cli): repair gemma platform config parsing
Samee24 Apr 17, 2026
dec7329
fix(cli): prettier
Samee24 Apr 17, 2026
c79ade9
Merge branch 'main' into sameez/gemma-auto-setup
Samee24 Apr 17, 2026
8f2a331
fix(cli): default autostart to false
Samee24 Apr 20, 2026
364eb1c
fix(cli): make server check more robust
Samee24 Apr 20, 2026
dac00da
fix(cli): scope gemma settings for security and project isolation
Samee24 Apr 20, 2026
e17478b
fix(cli): handle missing tail command and log stale PID cleanup
Samee24 Apr 20, 2026
6bad211
fix(cli): use generateContent endpoint for server health check
Samee24 Apr 20, 2026
e1a2899
Merge branch 'main' into sameez/gemma-auto-setup
Samee24 Apr 20, 2026
d3576a2
fix(cli): default autoStartServer to false in schema
Samee24 Apr 20, 2026
2d3e3ab
fix(cli): fix unsafe type assertion in logs error handler
Samee24 Apr 20, 2026
14e60b3
format
Samee24 Apr 20, 2026
b894f16
fix(chore): update docs
Apr 20, 2026
8ff6330
fix(cli): update tests for autoStartServer default and cross-platform…
Samee24 Apr 20, 2026
c065641
Merge branch 'main' into sameez/gemma-auto-setup
Samee24 Apr 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions docs/cli/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,17 +161,19 @@ they appear in the UI.

### Experimental

| UI Label | Setting | Description | Default |
| ---------------------------------------------------- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| Enable Git Worktrees | `experimental.worktrees` | Enable automated Git worktree management for parallel work. | `false` |
| Use OSC 52 Paste | `experimental.useOSC52Paste` | Use OSC 52 for pasting. This may be more robust than the default system when using remote terminal sessions (if your terminal is configured to allow it). | `false` |
| Use OSC 52 Copy | `experimental.useOSC52Copy` | Use OSC 52 for copying. This may be more robust than the default system when using remote terminal sessions (if your terminal is configured to allow it). | `false` |
| Model Steering | `experimental.modelSteering` | Enable model steering (user hints) to guide the model during tool execution. | `false` |
| Direct Web Fetch | `experimental.directWebFetch` | Enable web fetch behavior that bypasses LLM summarization. | `false` |
| Memory Manager Agent | `experimental.memoryManager` | Replace the built-in save_memory tool with a memory manager subagent that supports adding, removing, de-duplicating, and organizing memories. | `false` |
| Auto Memory | `experimental.autoMemory` | Automatically extract reusable skills from past sessions in the background. Review results with /memory inbox. | `false` |
| Use the generalist profile to manage agent contexts. | `experimental.generalistProfile` | Suitable for general coding and software development tasks. | `false` |
| Enable Context Management | `experimental.contextManagement` | Enable logic for context management. | `false` |
| UI Label | Setting | Description | Default |
| ---------------------------------------------------- | ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| Enable Git Worktrees | `experimental.worktrees` | Enable automated Git worktree management for parallel work. | `false` |
| Use OSC 52 Paste | `experimental.useOSC52Paste` | Use OSC 52 for pasting. This may be more robust than the default system when using remote terminal sessions (if your terminal is configured to allow it). | `false` |
| Use OSC 52 Copy | `experimental.useOSC52Copy` | Use OSC 52 for copying. This may be more robust than the default system when using remote terminal sessions (if your terminal is configured to allow it). | `false` |
| Model Steering | `experimental.modelSteering` | Enable model steering (user hints) to guide the model during tool execution. | `false` |
| Direct Web Fetch | `experimental.directWebFetch` | Enable web fetch behavior that bypasses LLM summarization. | `false` |
| Enable Gemma Model Router | `experimental.gemmaModelRouter.enabled` | Enable the Gemma Model Router (experimental). Requires a local endpoint serving Gemma via the Gemini API using LiteRT-LM shim. | `false` |
| Auto-start LiteRT Server | `experimental.gemmaModelRouter.autoStartServer` | Automatically start the LiteRT-LM server when Gemini CLI starts and the Gemma router is enabled. | `false` |
| Memory Manager Agent | `experimental.memoryManager` | Replace the built-in save_memory tool with a memory manager subagent that supports adding, removing, de-duplicating, and organizing memories. | `false` |
| Auto Memory | `experimental.autoMemory` | Automatically extract reusable skills from past sessions in the background. Review results with /memory inbox. | `false` |
| Use the generalist profile to manage agent contexts. | `experimental.generalistProfile` | Suitable for general coding and software development tasks. | `false` |
| Enable Context Management | `experimental.contextManagement` | Enable logic for context management. | `false` |

### Skills

Expand Down
12 changes: 12 additions & 0 deletions docs/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -1711,6 +1711,18 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `false`
- **Requires restart:** Yes

- **`experimental.gemmaModelRouter.autoStartServer`** (boolean):
- **Description:** Automatically start the LiteRT-LM server when Gemini CLI
starts and the Gemma router is enabled.
- **Default:** `false`
- **Requires restart:** Yes

- **`experimental.gemmaModelRouter.binaryPath`** (string):
- **Description:** Custom path to the LiteRT-LM binary. Leave empty to use the
default location (~/.gemini/bin/litert/).
- **Default:** `""`
- **Requires restart:** Yes

- **`experimental.gemmaModelRouter.classifier.host`** (string):
- **Description:** The host of the classifier.
- **Default:** `"http://localhost:9379"`
Expand Down
33 changes: 33 additions & 0 deletions packages/cli/src/commands/gemma.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

import type { CommandModule, Argv } from 'yargs';
import { initializeOutputListenersAndFlush } from '../gemini.js';
import { defer } from '../deferred.js';
import { setupCommand } from './gemma/setup.js';
import { startCommand } from './gemma/start.js';
import { stopCommand } from './gemma/stop.js';
import { statusCommand } from './gemma/status.js';
import { logsCommand } from './gemma/logs.js';

export const gemmaCommand: CommandModule = {
command: 'gemma',
describe: 'Manage local Gemma model routing',
builder: (yargs: Argv) =>
yargs
.middleware((argv) => {
initializeOutputListenersAndFlush();
argv['isCommand'] = true;
})
.command(defer(setupCommand, 'gemma'))
.command(defer(startCommand, 'gemma'))
.command(defer(stopCommand, 'gemma'))
.command(defer(statusCommand, 'gemma'))
.command(defer(logsCommand, 'gemma'))
.demandCommand(1, 'You need at least one command before continuing.')
.version(false),
handler: () => {},
};
45 changes: 45 additions & 0 deletions packages/cli/src/commands/gemma/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

import path from 'node:path';
import { Storage } from '@google/gemini-cli-core';

export const LITERT_RELEASE_VERSION = 'v0.9.0-alpha03';
export const LITERT_RELEASE_BASE_URL =
'https://github.com/google-ai-edge/LiteRT-LM/releases/download';
export const GEMMA_MODEL_NAME = 'gemma3-1b-gpu-custom';
export const DEFAULT_PORT = 9379;
export const HEALTH_CHECK_TIMEOUT_MS = 5000;
export const LITERT_API_VERSION = 'v1beta';
export const SERVER_START_WAIT_MS = 3000;

export const PLATFORM_BINARY_MAP: Record<string, string> = {
'darwin-arm64': 'lit.macos_arm64',
'linux-x64': 'lit.linux_x86_64',
'win32-x64': 'lit.windows_x86_64.exe',
};

// SHA-256 hashes for the official LiteRT-LM v0.9.0-alpha03 release binaries.
export const PLATFORM_BINARY_SHA256: Record<string, string> = {
'lit.macos_arm64':
'9e826a2634f2e8b220ad0f1e1b5c139e0b47cb172326e3b7d46d31382f49478e',
'lit.linux_x86_64':
'66601df8a07f08244b188e9fcab0bf4a16562fe76d8d47e49f40273d57541ee8',
'lit.windows_x86_64.exe':
'de82d2829d2fb1cbdb318e2d8a78dc2f9659ff14cb11b2894d1f30e0bfde2bf6',
};

export function getLiteRtBinDir(): string {
return path.join(Storage.getGlobalGeminiDir(), 'bin', 'litert');
}

export function getPidFilePath(): string {
return path.join(Storage.getGlobalTempDir(), 'litert-server.pid');
}

export function getLogFilePath(): string {
return path.join(Storage.getGlobalTempDir(), 'litert-server.log');
}
186 changes: 186 additions & 0 deletions packages/cli/src/commands/gemma/logs.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

import fs from 'node:fs';
import type { ChildProcess } from 'node:child_process';
import { EventEmitter } from 'node:events';
import os from 'node:os';
import path from 'node:path';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { spawn } from 'node:child_process';
import { exitCli } from '../utils.js';
import { getLogFilePath } from './constants.js';
import { logsCommand, readLastLines } from './logs.js';

vi.mock('@google/gemini-cli-core', async (importOriginal) => {
const { mockCoreDebugLogger } = await import(
'../../test-utils/mockDebugLogger.js'
);
return mockCoreDebugLogger(
await importOriginal<typeof import('@google/gemini-cli-core')>(),
{
stripAnsi: false,
},
);
});

vi.mock('node:child_process', async (importOriginal) => {
const actual = await importOriginal<typeof import('node:child_process')>();
return {
...actual,
spawn: vi.fn(),
};
});

vi.mock('../utils.js', () => ({
exitCli: vi.fn(),
}));

vi.mock('./constants.js', () => ({
getLogFilePath: vi.fn(),
}));

function createMockChild(): ChildProcess {
return Object.assign(new EventEmitter(), {
kill: vi.fn(),
}) as unknown as ChildProcess;
}

async function flushMicrotasks() {
await Promise.resolve();
await Promise.resolve();
}

describe('readLastLines', () => {
const tempFiles: string[] = [];

afterEach(async () => {
await Promise.all(
tempFiles
.splice(0)
.map((filePath) => fs.promises.rm(filePath, { force: true })),
);
});

it('returns only the requested tail lines without reading the whole file eagerly', async () => {
const filePath = path.join(
os.tmpdir(),
`gemma-logs-${Date.now()}-${Math.random().toString(36).slice(2)}.log`,
);
tempFiles.push(filePath);

const content = Array.from({ length: 2000 }, (_, i) => `line-${i + 1}`)
.join('\n')
.concat('\n');
await fs.promises.writeFile(filePath, content, 'utf-8');

await expect(readLastLines(filePath, 3)).resolves.toBe(
'line-1998\nline-1999\nline-2000\n',
);
});

it('returns an empty string when zero lines are requested', async () => {
const filePath = path.join(
os.tmpdir(),
`gemma-logs-${Date.now()}-${Math.random().toString(36).slice(2)}.log`,
);
tempFiles.push(filePath);
await fs.promises.writeFile(filePath, 'line-1\nline-2\n', 'utf-8');

await expect(readLastLines(filePath, 0)).resolves.toBe('');
});
});

describe('logsCommand', () => {
const originalPlatform = process.platform;

beforeEach(() => {
vi.clearAllMocks();
Object.defineProperty(process, 'platform', {
value: 'linux',
configurable: true,
});
vi.mocked(getLogFilePath).mockReturnValue('/tmp/gemma.log');
vi.spyOn(fs.promises, 'access').mockResolvedValue(undefined);
});

afterEach(() => {
Object.defineProperty(process, 'platform', {
value: originalPlatform,
configurable: true,
});
vi.restoreAllMocks();
});

it('waits for the tail process to close before exiting in follow mode', async () => {
const child = createMockChild();
vi.mocked(spawn).mockReturnValue(child);

let resolved = false;
const handlerPromise = (
logsCommand.handler as (argv: Record<string, unknown>) => Promise<void>
)({}).then(() => {
resolved = true;
});

await flushMicrotasks();

expect(spawn).toHaveBeenCalledWith(
'tail',
['-f', '-n', '20', '/tmp/gemma.log'],
{ stdio: 'inherit' },
);
expect(resolved).toBe(false);
expect(exitCli).not.toHaveBeenCalled();

child.emit('close', 0);
await handlerPromise;

expect(exitCli).toHaveBeenCalledWith(0);
});

it('uses one-shot tail output when follow is disabled', async () => {
const child = createMockChild();
vi.mocked(spawn).mockReturnValue(child);

const handlerPromise = (
logsCommand.handler as (argv: Record<string, unknown>) => Promise<void>
)({ follow: false });

await flushMicrotasks();

expect(spawn).toHaveBeenCalledWith('tail', ['-n', '20', '/tmp/gemma.log'], {
stdio: 'inherit',
});

child.emit('close', 0);
await handlerPromise;

expect(exitCli).toHaveBeenCalledWith(0);
});

it('follows from the requested line count when both --lines and --follow are set', async () => {
const child = createMockChild();
vi.mocked(spawn).mockReturnValue(child);

const handlerPromise = (
logsCommand.handler as (argv: Record<string, unknown>) => Promise<void>
)({ lines: 5, follow: true });

await flushMicrotasks();

expect(spawn).toHaveBeenCalledWith(
'tail',
['-f', '-n', '5', '/tmp/gemma.log'],
{ stdio: 'inherit' },
);

child.emit('close', 0);
await handlerPromise;

expect(exitCli).toHaveBeenCalledWith(0);
});
});
Loading
Loading