40 KiB
Client-Side Experimentation Framework Proposal for Gemini CLI
Status: Proposal Author: Generated Analysis Date: January 2026
Executive Summary
This document proposes a standardized framework for implementing client-side experimental features in Gemini CLI. The goal is to enable developers to ship features that users can opt into in a controlled, standardized way while protecting users who don't opt-in.
Table of Contents
- Current State Summary
- Proposed Design
- Implementation Details
- Workflow Integration
- User Experience
- Graduation Process
- Industry Comparison
- References
Current State Summary
What Gemini CLI Already Has
| Capability | Location | Description |
|---|---|---|
experimental.* settings |
settingsSchema.ts |
Boolean toggles for experimental features |
general.previewFeatures |
settingsSchema.ts |
Preview model access control |
| Settings merge hierarchy | settings.ts |
Schema defaults → System → User → Workspace → Admin |
| Agent experimental flags | registry.ts |
definition.experimental on agent definitions |
| Remote admin controls | settings.ts |
Server-side overrides for enterprise |
Current Experimental Features
| Feature | Setting | Default | Purpose |
|---|---|---|---|
| Agents | experimental.enableAgents |
false |
Enable subagent system |
| JIT Context | experimental.jitContext |
false |
Just-in-time context loading |
| Event-Driven Scheduler | experimental.enableEventDrivenScheduler |
true |
Event-based task orchestration |
| Plugin Hot-Reload | experimental.extensionReloading |
false |
Runtime plugin loading |
| Plugin Configuration | experimental.extensionConfig |
false |
Plugin settings management |
| Plugin Management | experimental.extensionManagement |
true |
Plugin lifecycle features |
| Plan Mode | experimental.plan |
false |
Read-only planning mode |
| OSC 52 Paste | experimental.useOSC52Paste |
false |
Remote session clipboard |
| Preview Models | general.previewFeatures |
false |
Access to preview models |
What's Missing
- ❌ Standardized lifecycle for experimental features
- ❌ Clear graduation criteria (experimental → stable)
- ❌ Discoverability of experimental features
- ❌ Telemetry integration for experiment usage tracking
- ❌ Documentation automation for experimental features
- ❌ CLI-level opt-in flags (like Cargo's
-Zflags) - ❌ Feature dependency management
- ❌ Deprecation tracking and warnings
Proposed Design
Multi-Tier Feature Gate System
Tier 1: Feature Lifecycle Stages
Adopt a Kubernetes-inspired maturity model with clear stages:
| Stage | Default | Stability | Can Remove? | Flag Required |
|---|---|---|---|---|
| Alpha | false |
May break, incomplete | Yes, any time | Explicit opt-in |
| Beta | false |
Mostly stable, collecting feedback | Yes, with deprecation | Explicit opt-in |
| GA | true |
Stable | No (breaking change) | None |
| Deprecated | true→false |
Stable but discouraged | After deprecation period | None |
Tier 2: Architecture Overview
┌─────────────────────────────────────────────────────────────────┐
│ Feature Gate Registry │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Feature Definition │ │
│ │ - name: string │ │
│ │ - stage: 'alpha' | 'beta' | 'ga' | 'deprecated' │ │
│ │ - description: string │ │
│ │ - owner: string (GitHub team/individual) │ │
│ │ - trackingIssue: string (GitHub issue URL) │ │
│ │ - addedIn: string (version) │ │
│ │ - targetGAVersion?: string │ │
│ │ - telemetryKey?: string │ │
│ │ - requiresRestart: boolean │ │
│ │ - dependencies?: string[] (other feature names) │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Activation Methods │
│ │
│ 1. Settings File (persistent) │
│ experimental.<featureName>: true │
│ │
│ 2. CLI Flag (session-only, like Cargo's -Z) │
│ gemini --feature=<name> --feature=<name2> │
│ gemini -X <name> │
│ │
│ 3. Environment Variable (CI/testing) │
│ GEMINI_FEATURES=<name1>,<name2> │
│ │
│ 4. Admin Override (enterprise) │
│ Remote admin controls can force-enable/disable │
└─────────────────────────────────────────────────────────────────┘
Tier 3: Activation Priority (highest to lowest)
- Admin Override - Enterprise admins can force-enable or force-disable
- CLI Flags - Session-specific activation via
--feature=X - Environment Variables -
GEMINI_FEATURES=X,Yfor CI/testing - Workspace Settings -
.gemini/settings.json(if workspace is trusted) - User Settings -
~/.gemini/settings.json - System Defaults - Built-in defaults from feature definition
Implementation Details
1. Feature Gate Registry
File: packages/core/src/features/featureGate.ts
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
export enum FeatureStage {
/** Experimental, may break, can be removed any time */
ALPHA = 'alpha',
/** Mostly stable, collecting feedback, requires deprecation to remove */
BETA = 'beta',
/** Stable, always enabled, feature gate no longer needed */
GA = 'ga',
/** Stable but discouraged, will be removed in future version */
DEPRECATED = 'deprecated',
}
export interface FeatureDefinition {
/** Unique identifier, kebab-case (e.g., "jit-context") */
name: string;
/** Current lifecycle stage */
stage: FeatureStage;
/** Human-readable description */
description: string;
/** GitHub team or individual responsible */
owner: string;
/** GitHub issue URL for tracking */
trackingIssue?: string;
/** Version when this feature was added */
addedInVersion: string;
/** Target version for GA (if alpha/beta) */
targetGAVersion?: string;
/** Version when deprecated (if deprecated) */
deprecatedInVersion?: string;
/** Version when feature will be removed (if deprecated) */
removalVersion?: string;
/** Whether enabling/disabling requires CLI restart */
requiresRestart: boolean;
/** Other features this depends on */
dependencies?: string[];
/** Key for telemetry tracking */
telemetryKey?: string;
/** Warning message shown when feature is enabled */
warningMessage?: string;
/** Features that are mutually exclusive with this one */
conflictsWith?: string[];
}
/**
* Central registry of all feature gates.
*
* To add a new feature:
* 1. Create a tracking issue
* 2. Add entry here with stage: ALPHA
* 3. Implement feature gated by FeatureGateService.isEnabled()
* 4. Graduate through stages based on feedback
*/
export const FEATURE_GATES: Record<string, FeatureDefinition> = {
'jit-context': {
name: 'jit-context',
stage: FeatureStage.ALPHA,
description: 'Just-in-time context loading for improved memory usage',
owner: '@anthropics/gemini-cli',
trackingIssue: 'https://github.com/google-gemini/gemini-cli/issues/XXX',
addedInVersion: '0.25.0',
targetGAVersion: '1.0.0',
requiresRestart: true,
telemetryKey: 'feature_jit_context',
warningMessage:
'JIT context is experimental and may affect response quality.',
},
agents: {
name: 'agents',
stage: FeatureStage.ALPHA,
description: 'Enable subagent system for complex multi-step tasks',
owner: '@anthropics/gemini-cli',
trackingIssue: 'https://github.com/google-gemini/gemini-cli/issues/XXX',
addedInVersion: '0.20.0',
requiresRestart: false,
telemetryKey: 'feature_agents',
warningMessage: 'Agents run in YOLO mode and will auto-approve tool calls.',
},
'plan-mode': {
name: 'plan-mode',
stage: FeatureStage.BETA,
description:
'Read-only planning mode for reviewing changes before execution',
owner: '@anthropics/gemini-cli',
trackingIssue: 'https://github.com/google-gemini/gemini-cli/issues/XXX',
addedInVersion: '0.22.0',
targetGAVersion: '0.30.0',
requiresRestart: false,
telemetryKey: 'feature_plan_mode',
},
'event-driven-scheduler': {
name: 'event-driven-scheduler',
stage: FeatureStage.BETA,
description: 'Event-based task orchestration system',
owner: '@anthropics/gemini-cli',
addedInVersion: '0.18.0',
requiresRestart: true,
telemetryKey: 'feature_event_scheduler',
},
// Example of a deprecated feature
'legacy-context-loading': {
name: 'legacy-context-loading',
stage: FeatureStage.DEPRECATED,
description: 'Legacy context loading (use jit-context instead)',
owner: '@anthropics/gemini-cli',
addedInVersion: '0.10.0',
deprecatedInVersion: '0.25.0',
removalVersion: '1.0.0',
requiresRestart: true,
warningMessage:
'This feature is deprecated and will be removed in v1.0.0. Migrate to jit-context.',
},
};
/**
* Get all features at a specific stage
*/
export function getFeaturesByStage(stage: FeatureStage): FeatureDefinition[] {
return Object.values(FEATURE_GATES).filter((f) => f.stage === stage);
}
/**
* Get a feature definition by name
*/
export function getFeature(name: string): FeatureDefinition | undefined {
return FEATURE_GATES[name];
}
/**
* Check if a feature name is valid
*/
export function isValidFeature(name: string): boolean {
return name in FEATURE_GATES;
}
2. Feature Gate Service
File: packages/core/src/features/featureGateService.ts
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {
FEATURE_GATES,
FeatureStage,
getFeature,
isValidFeature,
type FeatureDefinition,
} from './featureGate.js';
import { debugLogger } from '../utils/debugLogger.js';
export interface FeatureGateServiceConfig {
/** Features enabled via settings file */
settingsFeatures: Record<string, boolean>;
/** Features enabled via CLI flags */
cliFeatures: string[];
/** Features enabled via environment variable */
envFeatures: string[];
/** Admin overrides (highest priority) */
adminOverrides: Record<string, boolean>;
}
export class FeatureGateService {
private readonly enabledFeatures: Set<string> = new Set();
private readonly config: FeatureGateServiceConfig;
constructor(config: FeatureGateServiceConfig) {
this.config = config;
this.computeEnabledFeatures();
}
/**
* Check if a feature is enabled
*/
isEnabled(featureName: string): boolean {
const definition = getFeature(featureName);
if (!definition) {
debugLogger.warn(`Unknown feature gate: ${featureName}`);
return false;
}
// GA features are always enabled
if (definition.stage === FeatureStage.GA) {
return true;
}
// Admin override takes highest priority
if (this.config.adminOverrides[featureName] !== undefined) {
return this.config.adminOverrides[featureName];
}
return this.enabledFeatures.has(featureName);
}
/**
* Get all currently enabled features
*/
getEnabledFeatures(): FeatureDefinition[] {
return [...this.enabledFeatures]
.map((name) => getFeature(name))
.filter((f): f is FeatureDefinition => f !== undefined);
}
/**
* Get enabled features at a specific stage
*/
getEnabledFeaturesAtStage(stage: FeatureStage): FeatureDefinition[] {
return this.getEnabledFeatures().filter((f) => f.stage === stage);
}
/**
* Get warnings for enabled experimental features
*/
getStartupWarnings(): string[] {
const warnings: string[] = [];
for (const feature of this.getEnabledFeatures()) {
if (feature.stage === FeatureStage.ALPHA && feature.warningMessage) {
warnings.push(`⚠️ [ALPHA] ${feature.name}: ${feature.warningMessage}`);
} else if (
feature.stage === FeatureStage.DEPRECATED &&
feature.warningMessage
) {
warnings.push(
`⚠️ [DEPRECATED] ${feature.name}: ${feature.warningMessage}`,
);
}
}
return warnings;
}
/**
* Validate feature dependencies
*/
validateDependencies(): string[] {
const errors: string[] = [];
for (const featureName of this.enabledFeatures) {
const feature = getFeature(featureName);
if (!feature?.dependencies) continue;
for (const dep of feature.dependencies) {
if (!this.isEnabled(dep)) {
errors.push(
`Feature "${featureName}" requires "${dep}" to be enabled`,
);
}
}
// Check for conflicts
if (feature.conflictsWith) {
for (const conflict of feature.conflictsWith) {
if (this.isEnabled(conflict)) {
errors.push(
`Feature "${featureName}" conflicts with "${conflict}"`,
);
}
}
}
}
return errors;
}
private computeEnabledFeatures(): void {
this.enabledFeatures.clear();
// Process in order of priority (lowest to highest)
// 1. Settings file
for (const [name, enabled] of Object.entries(
this.config.settingsFeatures,
)) {
if (enabled && isValidFeature(name)) {
this.enabledFeatures.add(name);
}
}
// 2. Environment variables
for (const name of this.config.envFeatures) {
if (isValidFeature(name)) {
this.enabledFeatures.add(name);
} else {
debugLogger.warn(`Unknown feature in GEMINI_FEATURES: ${name}`);
}
}
// 3. CLI flags (can also disable with --no-feature=X)
for (const name of this.config.cliFeatures) {
if (name.startsWith('no-')) {
const featureName = name.slice(3);
this.enabledFeatures.delete(featureName);
} else if (isValidFeature(name)) {
this.enabledFeatures.add(name);
} else {
debugLogger.warn(`Unknown feature flag: ${name}`);
}
}
// Note: Admin overrides are checked at runtime in isEnabled()
}
}
3. CLI Flag Support
File: packages/cli/src/config/config.ts (additions)
// Add to yargs options
.option('feature', {
alias: 'X',
type: 'array',
string: true,
description: 'Enable experimental feature(s) for this session. Use --feature=<name> or -X <name>',
coerce: (features: string[]) =>
features.flatMap(f => f.split(',').map(x => x.trim())),
})
.option('no-feature', {
type: 'array',
string: true,
description: 'Disable a feature for this session',
coerce: (features: string[]) =>
features.flatMap(f => f.split(',').map(x => x.trim())),
})
.option('list-features', {
type: 'boolean',
description: 'List all available feature gates and exit',
})
4. Environment Variable Support
// In loadCliConfig or similar
function parseEnvFeatures(): string[] {
const envValue = process.env['GEMINI_FEATURES'];
if (!envValue) return [];
return envValue
.split(',')
.map((f) => f.trim())
.filter((f) => f.length > 0);
}
5. /features Slash Command
File: packages/cli/src/commands/features.ts
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {
FEATURE_GATES,
FeatureStage,
getFeaturesByStage,
} from '@google/gemini-cli-core';
import type { Config } from '@google/gemini-cli-core';
export async function featuresCommand(config: Config): Promise<void> {
const featureService = config.getFeatureGateService();
console.log('
📋 Gemini CLI Feature Gates
');
console.log(
'Feature gates allow you to opt-in to experimental functionality.
',
);
// Alpha features
const alphaFeatures = getFeaturesByStage(FeatureStage.ALPHA);
if (alphaFeatures.length > 0) {
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('ALPHA Features (experimental, may change without notice)');
console.log(
'━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
',
);
for (const feature of alphaFeatures) {
const enabled = featureService.isEnabled(feature.name);
const status = enabled ? '✓ ENABLED' : '○ disabled';
console.log(` ${feature.name}`);
console.log(` Status: ${status}`);
console.log(` ${feature.description}`);
if (feature.trackingIssue) {
console.log(` Tracking: ${feature.trackingIssue}`);
}
if (feature.targetGAVersion) {
console.log(` Target GA: v${feature.targetGAVersion}`);
}
console.log();
}
}
// Beta features
const betaFeatures = getFeaturesByStage(FeatureStage.BETA);
if (betaFeatures.length > 0) {
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('BETA Features (mostly stable, feedback welcome)');
console.log(
'━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
',
);
for (const feature of betaFeatures) {
const enabled = featureService.isEnabled(feature.name);
const status = enabled ? '✓ ENABLED' : '○ disabled';
console.log(` ${feature.name}`);
console.log(` Status: ${status}`);
console.log(` ${feature.description}`);
if (feature.trackingIssue) {
console.log(` Tracking: ${feature.trackingIssue}`);
}
console.log();
}
}
// Deprecated features
const deprecatedFeatures = getFeaturesByStage(FeatureStage.DEPRECATED);
if (deprecatedFeatures.length > 0) {
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('DEPRECATED Features (will be removed)');
console.log(
'━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
',
);
for (const feature of deprecatedFeatures) {
const enabled = featureService.isEnabled(feature.name);
const status = enabled ? '✓ ENABLED' : '○ disabled';
console.log(` ${feature.name}`);
console.log(` Status: ${status}`);
console.log(` ${feature.description}`);
if (feature.removalVersion) {
console.log(` ⚠️ Will be removed in: v${feature.removalVersion}`);
}
console.log();
}
}
// Usage instructions
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('How to Enable Features');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
');
console.log(' CLI flag (session only):');
console.log(' gemini --feature=<name>');
console.log(' gemini -X <name>');
console.log();
console.log(' Environment variable (CI/testing):');
console.log(' GEMINI_FEATURES=<name1>,<name2> gemini');
console.log();
console.log(' Settings file (persistent):');
console.log(' Add to ~/.gemini/settings.json:');
console.log(' { "experimental": { "<name>": true } }');
console.log();
}
6. Settings Schema Auto-Generation
File: packages/cli/src/config/settingsSchema.ts (additions)
import { FEATURE_GATES, FeatureStage } from '@google/gemini-cli-core';
/**
* Auto-generate experimental settings schema from feature definitions.
* This ensures the settings schema stays in sync with feature gates.
*/
function generateExperimentalSchema(): Record<string, SettingDefinition> {
const schema: Record<string, SettingDefinition> = {};
for (const [name, def] of Object.entries(FEATURE_GATES)) {
// GA features don't need settings (always enabled)
if (def.stage === FeatureStage.GA) continue;
// Convert kebab-case to camelCase for settings
const settingName = name.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
schema[settingName] = {
type: 'boolean',
default: false,
label: `[${def.stage.toUpperCase()}] ${def.name}`,
description:
def.description +
(def.warningMessage ? ` ⚠️ ${def.warningMessage}` : ''),
showInDialog: def.stage === FeatureStage.BETA, // Only show beta in settings UI
requiresRestart: def.requiresRestart,
ignoreInDocs: def.stage === FeatureStage.ALPHA, // Don't document alpha features
};
}
return schema;
}
// Use in schema definition
export const settingsSchema = {
// ... other settings ...
experimental: {
type: 'object',
label: 'Experimental Features',
description:
'Enable experimental features. Use /features to see all available.',
properties: generateExperimentalSchema(),
},
};
7. Telemetry Integration
File: packages/core/src/telemetry/featureTelemetry.ts
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { getFeature } from '../features/featureGate.js';
import type { Config } from '../config/config.js';
export interface FeatureUsageEvent {
featureName: string;
stage: string;
action: 'enabled' | 'used' | 'error';
clientVersion: string;
sessionId: string;
timestamp: string;
}
/**
* Log when a feature is enabled at startup
*/
export function logFeatureEnabled(config: Config, featureName: string): void {
const definition = getFeature(featureName);
if (!definition?.telemetryKey) return;
// Use existing telemetry infrastructure
config.getTelemetryService()?.recordEvent({
name: 'feature_enabled',
attributes: {
feature: featureName,
stage: definition.stage,
version: config.getClientVersion(),
},
});
}
/**
* Log when a feature is actively used
*/
export function logFeatureUsed(config: Config, featureName: string): void {
const definition = getFeature(featureName);
if (!definition?.telemetryKey) return;
config.getTelemetryService()?.recordEvent({
name: 'feature_used',
attributes: {
feature: featureName,
stage: definition.stage,
version: config.getClientVersion(),
},
});
}
Workflow Integration
1. Feature Tracking Issue Template
File: .github/ISSUE_TEMPLATE/feature-gate.yml
name: Feature Gate Proposal
description: Propose a new experimental feature gate
title: '[Feature Gate] '
labels: ['type/feature-gate', 'stage/alpha']
body:
- type: markdown
attributes:
value: |
## Feature Gate Proposal
Use this template to propose a new experimental feature for Gemini CLI.
All new features should start as Alpha and graduate through stages.
- type: input
id: feature-name
attributes:
label: Feature Name
description: Lowercase, kebab-case identifier (e.g., "jit-context")
placeholder: my-feature-name
validations:
required: true
- type: dropdown
id: initial-stage
attributes:
label: Initial Stage
description: Most features should start as Alpha
options:
- alpha
- beta
default: 0
validations:
required: true
- type: textarea
id: description
attributes:
label: Description
description: What does this feature do? (This will be shown to users)
placeholder: A brief description of the feature's functionality
validations:
required: true
- type: textarea
id: motivation
attributes:
label: Motivation
description: Why is this feature needed? What problem does it solve?
validations:
required: true
- type: input
id: target-ga
attributes:
label: Target GA Version
description: When do you expect this to be stable? (e.g., "1.0.0")
placeholder: '1.0.0'
- type: textarea
id: graduation-criteria
attributes:
label: Graduation Criteria
description: What needs to happen for this to move from Alpha → Beta → GA?
value: |
### Alpha → Beta
- [ ] Feature is functionally complete
- [ ] No critical bugs reported for 2 weeks
- [ ] Basic documentation exists
- [ ] Telemetry shows stable usage patterns
### Beta → GA
- [ ] Feature has been in Beta for 4+ weeks
- [ ] Documentation is complete
- [ ] No breaking changes planned
- [ ] Team consensus achieved
validations:
required: true
- type: textarea
id: warning-message
attributes:
label: Warning Message
description: Optional warning shown when users enable this feature
placeholder: 'This feature may affect performance in large codebases.'
- type: checkboxes
id: requirements
attributes:
label: Requirements
options:
- label: Requires CLI restart when toggled
- label: Has dependencies on other features
- label: Conflicts with other features
2. Automated Documentation Workflow
File: .github/workflows/docs-features.yml
name: Update Feature Documentation
on:
push:
branches: [main]
paths:
- 'packages/core/src/features/featureGate.ts'
workflow_dispatch:
jobs:
update-docs:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Generate feature documentation
run: npm run docs:generate-features
- name: Check for changes
id: changes
run: |
if [[ -n $(git status --porcelain docs/experimental-features.md) ]]; then
echo "changed=true" >> $GITHUB_OUTPUT
fi
- name: Create Pull Request
if: steps.changes.outputs.changed == 'true'
uses: peter-evans/create-pull-request@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: 'docs: Update experimental features documentation'
title: 'docs: Update experimental features documentation'
body: |
This PR was automatically generated to update the experimental features documentation.
The feature gate definitions in `packages/core/src/features/featureGate.ts` have changed.
branch: docs/update-features
labels: documentation,automated
3. Feature Gate Validation in CI
File: .github/workflows/ci.yml (additions)
validate-features:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Validate feature gates
run: npm run validate:features
# This script should:
# - Check all features have tracking issues
# - Check deprecated features have removal versions
# - Check feature names follow conventions
# - Check for orphaned feature flags in code
User Experience
Enabling via CLI Flag
# Single feature
gemini --feature=jit-context "Analyze this codebase"
# Short form
gemini -X jit-context "Analyze this codebase"
# Multiple features
gemini --feature=jit-context --feature=agents "Help me refactor"
# Comma-separated
gemini -X jit-context,agents
# Disable a feature for this session
gemini --no-feature=event-driven-scheduler
Enabling via Environment Variable
# For CI/CD or testing
GEMINI_FEATURES=jit-context,agents gemini -p "Run tests"
# In a shell profile for persistent enablement
export GEMINI_FEATURES=jit-context
Enabling via Settings File
// ~/.gemini/settings.json
{
"experimental": {
"jitContext": true,
"agents": true,
"planMode": true
}
}
Listing Features
$ gemini --list-features
📋 Gemini CLI Feature Gates
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ALPHA Features (experimental, may change without notice)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
jit-context
Status: ○ disabled
Just-in-time context loading for improved memory usage
Tracking: https://github.com/google-gemini/gemini-cli/issues/XXX
Target GA: v1.0.0
agents
Status: ○ disabled
Enable subagent system for complex multi-step tasks
Tracking: https://github.com/google-gemini/gemini-cli/issues/XXX
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
BETA Features (mostly stable, feedback welcome)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
plan-mode
Status: ○ disabled
Read-only planning mode for reviewing changes before execution
Tracking: https://github.com/google-gemini/gemini-cli/issues/XXX
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
How to Enable Features
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CLI flag (session only):
gemini --feature=<name>
gemini -X <name>
Environment variable (CI/testing):
GEMINI_FEATURES=<name1>,<name2> gemini
Settings file (persistent):
Add to ~/.gemini/settings.json:
{ "experimental": { "<name>": true } }
Startup Warnings
$ gemini --feature=agents
⚠️ Experimental features enabled:
• [ALPHA] agents: Agents run in YOLO mode and will auto-approve tool calls.
Type /features to learn more about experimental features.
>
Graduation Process
Stage Transitions
┌─────────────┐
│ ALPHA │
│ (2+ weeks) │
└──────┬──────┘
│
┌──────────────┼──────────────┐
│ │ │
▼ │ ▼
┌─────────────┐ │ ┌─────────────┐
│ BETA │ │ │ REMOVED │
│ (4+ weeks) │ │ │ (failed) │
└──────┬──────┘ │ └─────────────┘
│ │
│ │
▼ │
┌─────────────┐ │
│ GA │◄──────┘
│ (stable) │
└──────┬──────┘
│
▼
┌─────────────┐
│ DEPRECATED │
│ (optional) │
└──────┬──────┘
│
▼
┌─────────────┐
│ REMOVED │
└─────────────┘
Alpha → Beta Criteria
- Feature is functionally complete
- At least 2 weeks since Alpha release
- No critical bugs reported
- Basic documentation exists
- Telemetry shows no major issues
- Tracking issue updated with feedback summary
Beta → GA Criteria
- At least 4 weeks in Beta
- Telemetry shows stable usage patterns
- Documentation is complete
- No breaking changes planned
- Team consensus in tracking issue
- Feature gate can be removed (always enabled)
GA → Deprecated Criteria
- Replacement feature available (if applicable)
- Deprecation warning added to feature definition
- Migration guide published
- Removal version announced (typically N+2 major versions)
Deprecation Timeline
| Action | Timeline |
|---|---|
| Mark as deprecated | Version N |
| Log deprecation warnings | Version N |
| Disable by default | Version N+1 |
| Remove feature | Version N+2 |
Industry Comparison
| Aspect | Gemini CLI (Proposed) | Cargo (Rust) | Kubernetes |
|---|---|---|---|
| Stages | Alpha/Beta/GA/Deprecated | Unstable/Stable | Alpha/Beta/GA |
| CLI Flag | --feature=X / -X |
-Z flag |
--feature-gates=X=true |
| Config File | experimental.X: true |
[unstable] X = true |
Component flags |
| Default Off | Alpha, Beta | All unstable | Alpha only |
| Nightly Only | No (available in all) | Yes | No |
| Tracking | GitHub Issues | Tracking Issues | KEPs |
| Telemetry | Optional | None | Metrics endpoint |
| Admin Override | Yes | No | No |
References
Open Source Feature Flag Tools
- OpenFeature - Vendor-agnostic feature flagging specification (CNCF)
- GrowthBook - Open source feature flags and A/B testing
- Flagsmith - Open source feature flag service
- Unleash - Open source feature management
CLI Tool Implementations
- Cargo Unstable Features - Rust's package manager experimental features
- Kubernetes Feature Gates - K8s feature gate system
- Feature Flags Best Practices - LaunchDarkly
Internal References
- Current experimental settings:
packages/cli/src/config/settingsSchema.ts - Settings merge logic:
packages/cli/src/config/settings.ts - Agent experimental flags:
packages/core/src/agents/registry.ts - Release workflows:
.github/workflows/release-*.yml
Appendix: Migration Path
Migrating Existing Experimental Features
The following existing experimental settings should be migrated to the new feature gate system:
| Current Setting | New Feature Gate | Stage |
|---|---|---|
experimental.enableAgents |
agents |
Alpha |
experimental.jitContext |
jit-context |
Alpha |
experimental.plan |
plan-mode |
Beta |
experimental.enableEventDrivenScheduler |
event-driven-scheduler |
Beta |
experimental.extensionReloading |
extension-reloading |
Alpha |
experimental.extensionConfig |
extension-config |
Alpha |
experimental.useOSC52Paste |
osc52-paste |
Alpha |
general.previewFeatures |
preview-models |
Beta |
A migration script should:
- Read existing settings
- Map to new feature gate names
- Preserve user preferences
- Log migration actions