mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-06-12 20:37:08 -07:00
refactor(config): stabilize synchronous experiment access and add real-time updates
- Revert getExperimentValue to a synchronous interface to maintain compatibility with existing callers. - Add Config.updateExperimentalSettings() to support immediate application of /experiment set/unset changes. - Implement CLI argument normalization to handle kebab-case, camelCase, and snake_case consistently. - Enhance getExperimentFlagIdFromName to be more resilient to different naming conventions. - Rename getNumericalRoutingEnabled to isNumericalRoutingEnabled for better idiomatic consistency. - Update documentation in experimentation skill to recommend strongly-typed wrappers in Config.ts. - Add regression tests for CLI overrides and update all relevant routing and command tests. - Fix lint errors by removing unnecessary await/Promise.all for synchronous config methods.
This commit is contained in:
@@ -889,36 +889,46 @@ export async function loadCliConfig(
|
||||
const [key, ...valueParts] = entry.split('=');
|
||||
const value = valueParts.join('=');
|
||||
if (key && value !== undefined) {
|
||||
// Normalize key to kebab-case (e.g., enableNumericalRouting -> enable-numerical-routing)
|
||||
const normalizedKey = key
|
||||
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
||||
.toLowerCase()
|
||||
.replace(/_/g, '-');
|
||||
|
||||
// Simple type inference for CLI args
|
||||
if (value === 'true') experimentalCliArgs[key] = true;
|
||||
else if (value === 'false') experimentalCliArgs[key] = false;
|
||||
if (value === 'true') experimentalCliArgs[normalizedKey] = true;
|
||||
else if (value === 'false') experimentalCliArgs[normalizedKey] = false;
|
||||
else if (!isNaN(Number(value)))
|
||||
experimentalCliArgs[key] = Number(value);
|
||||
else experimentalCliArgs[key] = value;
|
||||
experimentalCliArgs[normalizedKey] = Number(value);
|
||||
else experimentalCliArgs[normalizedKey] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let clientName: string | undefined = undefined;
|
||||
if (isAcpMode) {
|
||||
const ide = detectIdeFromEnv();
|
||||
if (
|
||||
ide &&
|
||||
(ide.name !== 'vscode' || process.env['TERM_PROGRAM'] === 'vscode')
|
||||
) {
|
||||
clientName = `acp-${ide.name}`;
|
||||
}
|
||||
let clientName: string | undefined = undefined;
|
||||
if (isAcpMode) {
|
||||
const ide = detectIdeFromEnv();
|
||||
if (
|
||||
ide &&
|
||||
(ide.name !== 'vscode' || process.env['TERM_PROGRAM'] === 'vscode')
|
||||
) {
|
||||
clientName = `acp-${ide.name}`;
|
||||
}
|
||||
}
|
||||
|
||||
const useGeneralistProfile =
|
||||
settings.experimental?.generalistProfile ?? false;
|
||||
const useContextManagement =
|
||||
settings.experimental?.contextManagement ?? false;
|
||||
const contextManagement = {
|
||||
...(useGeneralistProfile ? generalistProfile : {}),
|
||||
...(useContextManagement ? settings?.contextManagement : {}),
|
||||
enabled: useContextManagement || useGeneralistProfile,
|
||||
};
|
||||
const useGeneralistProfile =
|
||||
settings.experimental?.generalistProfile ?? false;
|
||||
const useContextManagement =
|
||||
settings.experimental?.contextManagement ?? false;
|
||||
const contextManagement = {
|
||||
...(useGeneralistProfile ? generalistProfile : {}),
|
||||
...(useContextManagement ? settings?.contextManagement : {}),
|
||||
enabled: useContextManagement || useGeneralistProfile,
|
||||
};
|
||||
|
||||
if (debugMode && Object.keys(experimentalCliArgs).length > 0) {
|
||||
debugLogger.debug('Experimental CLI args:', experimentalCliArgs);
|
||||
}
|
||||
return new Config({
|
||||
acpMode: isAcpMode,
|
||||
clientName,
|
||||
|
||||
@@ -120,7 +120,7 @@ export const createMockConfig = (overrides: Partial<Config> = {}): Config =>
|
||||
isTrustedFolder: vi.fn().mockReturnValue(true),
|
||||
getCompressionThreshold: vi.fn().mockResolvedValue(undefined),
|
||||
getUserCaching: vi.fn().mockResolvedValue(false),
|
||||
getNumericalRoutingEnabled: vi.fn().mockResolvedValue(false),
|
||||
isNumericalRoutingEnabled: vi.fn().mockReturnValue(false),
|
||||
getClassifierThreshold: vi.fn().mockResolvedValue(undefined),
|
||||
getBannerTextNoCapacityIssues: vi.fn().mockResolvedValue(''),
|
||||
getBannerTextCapacityIssues: vi.fn().mockResolvedValue(''),
|
||||
|
||||
@@ -18,6 +18,7 @@ describe('experimentCommand', () => {
|
||||
services: {
|
||||
config: {
|
||||
getExperimentValue: vi.fn(),
|
||||
updateExperimentalSettings: vi.fn(),
|
||||
},
|
||||
settings: {
|
||||
merged: {
|
||||
|
||||
@@ -107,13 +107,16 @@ const setExperimentCommand: SlashCommand = {
|
||||
value = rawValue;
|
||||
}
|
||||
|
||||
const { settings } = context.services;
|
||||
const { settings, config } = context.services;
|
||||
if (!config) return;
|
||||
|
||||
const currentExperimental = {
|
||||
...((settings.merged.experimental as Record<string, unknown>) || {}),
|
||||
};
|
||||
currentExperimental[name] = value;
|
||||
|
||||
settings.setValue(SettingScope.User, 'experimental', currentExperimental);
|
||||
config.updateExperimentalSettings(currentExperimental);
|
||||
|
||||
context.ui.addItem({
|
||||
type: MessageType.INFO,
|
||||
@@ -138,7 +141,9 @@ const unsetExperimentCommand: SlashCommand = {
|
||||
return;
|
||||
}
|
||||
|
||||
const { settings } = context.services;
|
||||
const { settings, config } = context.services;
|
||||
if (!config) return;
|
||||
|
||||
const currentExperimental = {
|
||||
...((settings.merged.experimental as Record<string, unknown>) || {}),
|
||||
};
|
||||
@@ -153,6 +158,7 @@ const unsetExperimentCommand: SlashCommand = {
|
||||
|
||||
delete currentExperimental[name];
|
||||
settings.setValue(SettingScope.User, 'experimental', currentExperimental);
|
||||
config.updateExperimentalSettings(currentExperimental);
|
||||
|
||||
context.ui.addItem({
|
||||
type: MessageType.INFO,
|
||||
|
||||
Reference in New Issue
Block a user