summaryrefslogtreecommitdiff
path: root/packages/cli/src/config
diff options
context:
space:
mode:
authorJacob Richman <[email protected]>2025-05-01 10:34:07 -0700
committerGitHub <[email protected]>2025-05-01 10:34:07 -0700
commit7e8f379dfbd4d70050ce301a42a38ba9c1f052f4 (patch)
tree7d712dd0b3b6b246bc7dd92048cc91c5317a3a47 /packages/cli/src/config
parenta18eea8c23dfb6759472d6b0bb80e13c2d6ef736 (diff)
Save settings to ~/.gemini/settings.json and optionally /your/workspace/.gemini/settings.json (#237)
Diffstat (limited to 'packages/cli/src/config')
-rw-r--r--packages/cli/src/config/settings.ts132
1 files changed, 132 insertions, 0 deletions
diff --git a/packages/cli/src/config/settings.ts b/packages/cli/src/config/settings.ts
new file mode 100644
index 00000000..0c877f8f
--- /dev/null
+++ b/packages/cli/src/config/settings.ts
@@ -0,0 +1,132 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as fs from 'fs';
+import * as path from 'path';
+import { homedir } from 'os';
+import { Config } from '@gemini-code/server';
+
+const SETTINGS_DIRECTORY_NAME = '.gemini';
+const USER_SETTINGS_DIR = path.join(homedir(), SETTINGS_DIRECTORY_NAME);
+const USER_SETTINGS_PATH = path.join(USER_SETTINGS_DIR, 'settings.json');
+
+export enum SettingScope {
+ User = 'User',
+ Workspace = 'Workspace',
+}
+
+export interface Settings {
+ theme?: string;
+ // Add other settings here.
+}
+
+export interface SettingsFile {
+ settings: Settings;
+ path: string;
+}
+export class LoadedSettings {
+ constructor(user: SettingsFile, workspace: SettingsFile) {
+ this.user = user;
+ this.workspace = workspace;
+ this.merged = this.computeMergedSettings();
+ }
+
+ readonly user: SettingsFile;
+ readonly workspace: SettingsFile;
+
+ private merged: Settings;
+
+ getMerged(): Settings {
+ return this.merged;
+ }
+
+ private computeMergedSettings(): Settings {
+ return {
+ ...this.user.settings,
+ ...this.workspace.settings,
+ };
+ }
+
+ forScope(scope: SettingScope): SettingsFile {
+ switch (scope) {
+ case SettingScope.User:
+ return this.user;
+ case SettingScope.Workspace:
+ return this.workspace;
+ default:
+ throw new Error(`Invalid scope: ${scope}`);
+ }
+ }
+
+ setValue(
+ scope: SettingScope,
+ key: keyof Settings,
+ value: string | undefined,
+ ): void {
+ const settingsFile = this.forScope(scope);
+ settingsFile.settings[key] = value;
+ this.merged = this.computeMergedSettings();
+ saveSettings(settingsFile);
+ }
+}
+
+/**
+ * Loads settings from user and project configuration files.
+ * Project settings override user settings.
+ */
+export function loadSettings(config: Config): LoadedSettings {
+ let userSettings: Settings = {};
+ let workspaceSettings = {};
+
+ // Load user settings
+ try {
+ if (fs.existsSync(USER_SETTINGS_PATH)) {
+ const userContent = fs.readFileSync(USER_SETTINGS_PATH, 'utf-8');
+ userSettings = JSON.parse(userContent);
+ }
+ } catch (error) {
+ console.error('Error reading user settings file:', error);
+ }
+
+ const workspaceSettingsPath = path.join(
+ config.getTargetDir(),
+ SETTINGS_DIRECTORY_NAME,
+ 'settings.json',
+ );
+
+ // Load workspace settings
+ try {
+ if (fs.existsSync(workspaceSettingsPath)) {
+ const projectContent = fs.readFileSync(workspaceSettingsPath, 'utf-8');
+ workspaceSettings = JSON.parse(projectContent);
+ }
+ } catch (error) {
+ console.error('Error reading workspace settings file:', error);
+ }
+
+ return new LoadedSettings(
+ { path: USER_SETTINGS_PATH, settings: userSettings },
+ { path: workspaceSettingsPath, settings: workspaceSettings },
+ );
+}
+
+export function saveSettings(settingsFile: SettingsFile): void {
+ try {
+ // Ensure the directory exists
+ const dirPath = path.dirname(settingsFile.path);
+ if (!fs.existsSync(dirPath)) {
+ fs.mkdirSync(dirPath, { recursive: true });
+ }
+
+ fs.writeFileSync(
+ settingsFile.path,
+ JSON.stringify(settingsFile.settings, null, 2),
+ 'utf-8',
+ );
+ } catch (error) {
+ console.error('Error saving user settings file:', error);
+ }
+}