@google/gemini-cli 0.5.0-nightly.20250908.4693137b → 0.5.0-preview

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/dist/package.json +2 -2
  2. package/dist/src/commands/extensions/install.js +5 -12
  3. package/dist/src/commands/extensions/install.js.map +1 -1
  4. package/dist/src/commands/extensions/install.test.js +1 -16
  5. package/dist/src/commands/extensions/install.test.js.map +1 -1
  6. package/dist/src/config/config.js +1 -0
  7. package/dist/src/config/config.js.map +1 -1
  8. package/dist/src/config/extension.d.ts +1 -0
  9. package/dist/src/config/extension.js +70 -46
  10. package/dist/src/config/extension.js.map +1 -1
  11. package/dist/src/config/settings.js +2 -2
  12. package/dist/src/config/settings.js.map +1 -1
  13. package/dist/src/config/settingsSchema.d.ts +31 -6
  14. package/dist/src/config/settingsSchema.js +24 -3
  15. package/dist/src/config/settingsSchema.js.map +1 -1
  16. package/dist/src/config/settingsSchema.test.js +62 -55
  17. package/dist/src/config/settingsSchema.test.js.map +1 -1
  18. package/dist/src/gemini.js +4 -37
  19. package/dist/src/gemini.js.map +1 -1
  20. package/dist/src/generated/git-commit.d.ts +2 -2
  21. package/dist/src/generated/git-commit.js +2 -2
  22. package/dist/src/generated/git-commit.js.map +1 -1
  23. package/dist/src/nonInteractiveCli.js +75 -73
  24. package/dist/src/nonInteractiveCli.js.map +1 -1
  25. package/dist/src/ui/AppContainer.js +40 -136
  26. package/dist/src/ui/AppContainer.js.map +1 -1
  27. package/dist/src/ui/AppContainer.test.js +199 -16
  28. package/dist/src/ui/AppContainer.test.js.map +1 -1
  29. package/dist/src/ui/components/AppHeader.js +5 -2
  30. package/dist/src/ui/components/AppHeader.js.map +1 -1
  31. package/dist/src/ui/components/Composer.js +2 -1
  32. package/dist/src/ui/components/Composer.js.map +1 -1
  33. package/dist/src/ui/components/ConfigInitDisplay.d.ts +6 -0
  34. package/dist/src/ui/components/ConfigInitDisplay.js +37 -0
  35. package/dist/src/ui/components/ConfigInitDisplay.js.map +1 -0
  36. package/dist/src/ui/components/DialogManager.js +2 -3
  37. package/dist/src/ui/components/DialogManager.js.map +1 -1
  38. package/dist/src/ui/components/GeminiRespondingSpinner.d.ts +5 -0
  39. package/dist/src/ui/components/GeminiRespondingSpinner.js +5 -1
  40. package/dist/src/ui/components/GeminiRespondingSpinner.js.map +1 -1
  41. package/dist/src/ui/components/ProQuotaDialog.d.ts +2 -2
  42. package/dist/src/ui/components/ProQuotaDialog.js +2 -2
  43. package/dist/src/ui/components/ProQuotaDialog.js.map +1 -1
  44. package/dist/src/ui/components/ProQuotaDialog.test.js +3 -3
  45. package/dist/src/ui/components/ProQuotaDialog.test.js.map +1 -1
  46. package/dist/src/ui/components/SettingsDialog.js +20 -5
  47. package/dist/src/ui/components/SettingsDialog.js.map +1 -1
  48. package/dist/src/ui/components/SettingsDialog.test.js +144 -79
  49. package/dist/src/ui/components/SettingsDialog.test.js.map +1 -1
  50. package/dist/src/ui/components/ThemeDialog.js +10 -27
  51. package/dist/src/ui/components/ThemeDialog.js.map +1 -1
  52. package/dist/src/ui/components/ThemeDialog.test.d.ts +6 -0
  53. package/dist/src/ui/components/ThemeDialog.test.js +65 -0
  54. package/dist/src/ui/components/ThemeDialog.test.js.map +1 -0
  55. package/dist/src/ui/components/shared/EnumSelector.d.ts +18 -0
  56. package/dist/src/ui/components/shared/EnumSelector.js +44 -0
  57. package/dist/src/ui/components/shared/EnumSelector.js.map +1 -0
  58. package/dist/src/ui/components/shared/EnumSelector.test.d.ts +6 -0
  59. package/dist/src/ui/components/shared/EnumSelector.test.js +70 -0
  60. package/dist/src/ui/components/shared/EnumSelector.test.js.map +1 -0
  61. package/dist/src/ui/components/shared/ScopeSelector.d.ts +19 -0
  62. package/dist/src/ui/components/shared/ScopeSelector.js +11 -0
  63. package/dist/src/ui/components/shared/ScopeSelector.js.map +1 -0
  64. package/dist/src/ui/contexts/UIStateContext.d.ts +8 -2
  65. package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
  66. package/dist/src/ui/hooks/useFolderTrust.d.ts +1 -2
  67. package/dist/src/ui/hooks/useFolderTrust.js +21 -8
  68. package/dist/src/ui/hooks/useFolderTrust.js.map +1 -1
  69. package/dist/src/ui/hooks/useGeminiStream.js +36 -34
  70. package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
  71. package/dist/src/ui/hooks/useMessageQueue.d.ts +2 -1
  72. package/dist/src/ui/hooks/useMessageQueue.js +5 -3
  73. package/dist/src/ui/hooks/useMessageQueue.js.map +1 -1
  74. package/dist/src/ui/hooks/useMessageQueue.test.js +9 -0
  75. package/dist/src/ui/hooks/useMessageQueue.test.js.map +1 -1
  76. package/dist/src/ui/hooks/useQuotaAndFallback.d.ts +21 -0
  77. package/dist/src/ui/hooks/useQuotaAndFallback.js +122 -0
  78. package/dist/src/ui/hooks/useQuotaAndFallback.js.map +1 -0
  79. package/dist/src/ui/hooks/useQuotaAndFallback.test.d.ts +6 -0
  80. package/dist/src/ui/hooks/useQuotaAndFallback.test.js +269 -0
  81. package/dist/src/ui/hooks/useQuotaAndFallback.test.js.map +1 -0
  82. package/dist/src/utils/settingsUtils.d.ts +16 -6
  83. package/dist/src/utils/settingsUtils.js +35 -25
  84. package/dist/src/utils/settingsUtils.js.map +1 -1
  85. package/dist/src/utils/settingsUtils.test.js +418 -156
  86. package/dist/src/utils/settingsUtils.test.js.map +1 -1
  87. package/dist/src/zed-integration/schema.d.ts +4 -4
  88. package/dist/tsconfig.tsbuildinfo +1 -1
  89. package/package.json +3 -3
  90. package/dist/google-gemini-cli-0.3.4.tgz +0 -0
@@ -8,14 +8,109 @@ import {
8
8
  // Schema utilities
9
9
  getSettingsByCategory, getSettingDefinition, requiresRestart, getDefaultValue, getRestartRequiredSettings, getEffectiveValue, getAllSettingKeys, getSettingsByType, getSettingsRequiringRestart, isValidSettingKey, getSettingCategory, shouldShowInDialog, getDialogSettingsByCategory, getDialogSettingsByType, getDialogSettingKeys,
10
10
  // Business logic utilities
11
- getSettingValue, isSettingModified, settingExistsInScope, setPendingSettingValue, hasRestartRequiredSettings, getRestartRequiredFromModified, getDisplayValue, isDefaultValue, isValueInherited, getEffectiveDisplayValue, } from './settingsUtils.js';
11
+ getSettingValue, isSettingModified, TEST_ONLY, settingExistsInScope, setPendingSettingValue, hasRestartRequiredSettings, getRestartRequiredFromModified, getDisplayValue, isDefaultValue, isValueInherited, getEffectiveDisplayValue, } from './settingsUtils.js';
12
+ import { getSettingsSchema, } from '../config/settingsSchema.js';
13
+ vi.mock('../config/settingsSchema.js', async (importOriginal) => {
14
+ const original = await importOriginal();
15
+ return {
16
+ ...original,
17
+ getSettingsSchema: vi.fn(),
18
+ };
19
+ });
20
+ function makeMockSettings(settings) {
21
+ return settings;
22
+ }
12
23
  describe('SettingsUtils', () => {
24
+ beforeEach(() => {
25
+ const SETTINGS_SCHEMA = {
26
+ mcpServers: {
27
+ type: 'object',
28
+ label: 'MCP Servers',
29
+ category: 'Advanced',
30
+ requiresRestart: true,
31
+ default: {},
32
+ description: 'Configuration for MCP servers.',
33
+ showInDialog: false,
34
+ },
35
+ test: {
36
+ type: 'string',
37
+ label: 'Test',
38
+ category: 'Basic',
39
+ requiresRestart: false,
40
+ default: 'hello',
41
+ description: 'A test field',
42
+ showInDialog: true,
43
+ },
44
+ advanced: {
45
+ type: 'object',
46
+ label: 'Advanced',
47
+ category: 'Advanced',
48
+ requiresRestart: true,
49
+ default: {},
50
+ description: 'Advanced settings for power users.',
51
+ showInDialog: false,
52
+ },
53
+ ui: {
54
+ type: 'object',
55
+ label: 'UI',
56
+ category: 'UI',
57
+ requiresRestart: false,
58
+ default: {},
59
+ description: 'User interface settings.',
60
+ showInDialog: false,
61
+ properties: {
62
+ theme: {
63
+ type: 'string',
64
+ label: 'Theme',
65
+ category: 'UI',
66
+ requiresRestart: false,
67
+ default: undefined,
68
+ description: 'The color theme for the UI.',
69
+ showInDialog: false,
70
+ },
71
+ requiresRestart: {
72
+ type: 'boolean',
73
+ label: 'Requires Restart',
74
+ category: 'UI',
75
+ default: false,
76
+ requiresRestart: true,
77
+ },
78
+ accessibility: {
79
+ type: 'object',
80
+ label: 'Accessibility',
81
+ category: 'UI',
82
+ requiresRestart: true,
83
+ default: {},
84
+ description: 'Accessibility settings.',
85
+ showInDialog: false,
86
+ properties: {
87
+ disableLoadingPhrases: {
88
+ type: 'boolean',
89
+ label: 'Disable Loading Phrases',
90
+ category: 'UI',
91
+ requiresRestart: true,
92
+ default: false,
93
+ description: 'Disable loading phrases for accessibility',
94
+ showInDialog: true,
95
+ },
96
+ },
97
+ },
98
+ },
99
+ },
100
+ };
101
+ vi.mocked(getSettingsSchema).mockReturnValue(SETTINGS_SCHEMA);
102
+ });
103
+ afterEach(() => {
104
+ TEST_ONLY.clearFlattenedSchema();
105
+ vi.clearAllMocks();
106
+ vi.resetAllMocks();
107
+ });
13
108
  describe('Schema Utilities', () => {
14
109
  describe('getSettingsByCategory', () => {
15
110
  it('should group settings by category', () => {
16
111
  const categories = getSettingsByCategory();
17
- expect(categories).toHaveProperty('General');
18
- expect(categories).toHaveProperty('UI');
112
+ expect(categories).toHaveProperty('Advanced');
113
+ expect(categories).toHaveProperty('Basic');
19
114
  });
20
115
  it('should include key property in grouped settings', () => {
21
116
  const categories = getSettingsByCategory();
@@ -28,9 +123,9 @@ describe('SettingsUtils', () => {
28
123
  });
29
124
  describe('getSettingDefinition', () => {
30
125
  it('should return definition for valid setting', () => {
31
- const definition = getSettingDefinition('ui.showMemoryUsage');
126
+ const definition = getSettingDefinition('ui.theme');
32
127
  expect(definition).toBeDefined();
33
- expect(definition?.label).toBe('Show Memory Usage');
128
+ expect(definition?.label).toBe('Theme');
34
129
  });
35
130
  it('should return undefined for invalid setting', () => {
36
131
  const definition = getSettingDefinition('invalidSetting');
@@ -39,12 +134,10 @@ describe('SettingsUtils', () => {
39
134
  });
40
135
  describe('requiresRestart', () => {
41
136
  it('should return true for settings that require restart', () => {
42
- expect(requiresRestart('advanced.autoConfigureMemory')).toBe(true);
43
- expect(requiresRestart('general.checkpointing.enabled')).toBe(true);
137
+ expect(requiresRestart('ui.requiresRestart')).toBe(true);
44
138
  });
45
139
  it('should return false for settings that do not require restart', () => {
46
- expect(requiresRestart('ui.showMemoryUsage')).toBe(false);
47
- expect(requiresRestart('ui.hideTips')).toBe(false);
140
+ expect(requiresRestart('ui.theme')).toBe(false);
48
141
  });
49
142
  it('should return false for invalid settings', () => {
50
143
  expect(requiresRestart('invalidSetting')).toBe(false);
@@ -52,8 +145,8 @@ describe('SettingsUtils', () => {
52
145
  });
53
146
  describe('getDefaultValue', () => {
54
147
  it('should return correct default values', () => {
55
- expect(getDefaultValue('ui.showMemoryUsage')).toBe(false);
56
- expect(getDefaultValue('context.fileFiltering.enableRecursiveFileSearch')).toBe(true);
148
+ expect(getDefaultValue('test')).toBe('hello');
149
+ expect(getDefaultValue('ui.requiresRestart')).toBe(false);
57
150
  });
58
151
  it('should return undefined for invalid settings', () => {
59
152
  expect(getDefaultValue('invalidSetting')).toBeUndefined();
@@ -62,43 +155,46 @@ describe('SettingsUtils', () => {
62
155
  describe('getRestartRequiredSettings', () => {
63
156
  it('should return all settings that require restart', () => {
64
157
  const restartSettings = getRestartRequiredSettings();
65
- expect(restartSettings).toContain('advanced.autoConfigureMemory');
66
- expect(restartSettings).toContain('general.checkpointing.enabled');
67
- expect(restartSettings).not.toContain('ui.showMemoryUsage');
158
+ expect(restartSettings).toContain('mcpServers');
159
+ expect(restartSettings).toContain('ui.requiresRestart');
68
160
  });
69
161
  });
70
162
  describe('getEffectiveValue', () => {
71
163
  it('should return value from settings when set', () => {
72
- const settings = { ui: { showMemoryUsage: true } };
73
- const mergedSettings = { ui: { showMemoryUsage: false } };
74
- const value = getEffectiveValue('ui.showMemoryUsage', settings, mergedSettings);
164
+ const settings = makeMockSettings({ ui: { requiresRestart: true } });
165
+ const mergedSettings = makeMockSettings({
166
+ ui: { requiresRestart: false },
167
+ });
168
+ const value = getEffectiveValue('ui.requiresRestart', settings, mergedSettings);
75
169
  expect(value).toBe(true);
76
170
  });
77
171
  it('should return value from merged settings when not set in current scope', () => {
78
- const settings = {};
79
- const mergedSettings = { ui: { showMemoryUsage: true } };
80
- const value = getEffectiveValue('ui.showMemoryUsage', settings, mergedSettings);
172
+ const settings = makeMockSettings({});
173
+ const mergedSettings = makeMockSettings({
174
+ ui: { requiresRestart: true },
175
+ });
176
+ const value = getEffectiveValue('ui.requiresRestart', settings, mergedSettings);
81
177
  expect(value).toBe(true);
82
178
  });
83
179
  it('should return default value when not set anywhere', () => {
84
- const settings = {};
85
- const mergedSettings = {};
86
- const value = getEffectiveValue('ui.showMemoryUsage', settings, mergedSettings);
180
+ const settings = makeMockSettings({});
181
+ const mergedSettings = makeMockSettings({});
182
+ const value = getEffectiveValue('ui.requiresRestart', settings, mergedSettings);
87
183
  expect(value).toBe(false); // default value
88
184
  });
89
185
  it('should handle nested settings correctly', () => {
90
- const settings = {
186
+ const settings = makeMockSettings({
91
187
  ui: { accessibility: { disableLoadingPhrases: true } },
92
- };
93
- const mergedSettings = {
188
+ });
189
+ const mergedSettings = makeMockSettings({
94
190
  ui: { accessibility: { disableLoadingPhrases: false } },
95
- };
191
+ });
96
192
  const value = getEffectiveValue('ui.accessibility.disableLoadingPhrases', settings, mergedSettings);
97
193
  expect(value).toBe(true);
98
194
  });
99
195
  it('should return undefined for invalid settings', () => {
100
- const settings = {};
101
- const mergedSettings = {};
196
+ const settings = makeMockSettings({});
197
+ const mergedSettings = makeMockSettings({});
102
198
  const value = getEffectiveValue('invalidSetting', settings, mergedSettings);
103
199
  expect(value).toBeUndefined();
104
200
  });
@@ -106,9 +202,8 @@ describe('SettingsUtils', () => {
106
202
  describe('getAllSettingKeys', () => {
107
203
  it('should return all setting keys', () => {
108
204
  const keys = getAllSettingKeys();
109
- expect(keys).toContain('ui.showMemoryUsage');
205
+ expect(keys).toContain('test');
110
206
  expect(keys).toContain('ui.accessibility.disableLoadingPhrases');
111
- expect(keys).toContain('general.checkpointing.enabled');
112
207
  });
113
208
  });
114
209
  describe('getSettingsByType', () => {
@@ -131,7 +226,7 @@ describe('SettingsUtils', () => {
131
226
  });
132
227
  describe('isValidSettingKey', () => {
133
228
  it('should return true for valid setting keys', () => {
134
- expect(isValidSettingKey('ui.showMemoryUsage')).toBe(true);
229
+ expect(isValidSettingKey('ui.requiresRestart')).toBe(true);
135
230
  expect(isValidSettingKey('ui.accessibility.disableLoadingPhrases')).toBe(true);
136
231
  });
137
232
  it('should return false for invalid setting keys', () => {
@@ -141,7 +236,7 @@ describe('SettingsUtils', () => {
141
236
  });
142
237
  describe('getSettingCategory', () => {
143
238
  it('should return correct category for valid settings', () => {
144
- expect(getSettingCategory('ui.showMemoryUsage')).toBe('UI');
239
+ expect(getSettingCategory('ui.requiresRestart')).toBe('UI');
145
240
  expect(getSettingCategory('ui.accessibility.disableLoadingPhrases')).toBe('UI');
146
241
  });
147
242
  it('should return undefined for invalid settings', () => {
@@ -150,17 +245,12 @@ describe('SettingsUtils', () => {
150
245
  });
151
246
  describe('shouldShowInDialog', () => {
152
247
  it('should return true for settings marked to show in dialog', () => {
153
- expect(shouldShowInDialog('ui.showMemoryUsage')).toBe(true);
248
+ expect(shouldShowInDialog('ui.requiresRestart')).toBe(true);
154
249
  expect(shouldShowInDialog('general.vimMode')).toBe(true);
155
250
  expect(shouldShowInDialog('ui.hideWindowTitle')).toBe(true);
156
- expect(shouldShowInDialog('privacy.usageStatisticsEnabled')).toBe(false);
157
251
  });
158
252
  it('should return false for settings marked to hide from dialog', () => {
159
- expect(shouldShowInDialog('security.auth.selectedType')).toBe(false);
160
- expect(shouldShowInDialog('tools.core')).toBe(false);
161
- expect(shouldShowInDialog('ui.customThemes')).toBe(false);
162
- expect(shouldShowInDialog('ui.theme')).toBe(false); // Changed to false
163
- expect(shouldShowInDialog('general.preferredEditor')).toBe(false); // Changed to false
253
+ expect(shouldShowInDialog('ui.theme')).toBe(false);
164
254
  });
165
255
  it('should return true for invalid settings (default behavior)', () => {
166
256
  expect(shouldShowInDialog('invalidSetting')).toBe(true);
@@ -173,9 +263,8 @@ describe('SettingsUtils', () => {
173
263
  expect(categories['UI']).toBeDefined();
174
264
  const uiSettings = categories['UI'];
175
265
  const uiKeys = uiSettings.map((s) => s.key);
176
- expect(uiKeys).toContain('ui.showMemoryUsage');
177
- expect(uiKeys).toContain('ui.hideWindowTitle');
178
- expect(uiKeys).not.toContain('ui.customThemes'); // This is marked false
266
+ expect(uiKeys).toContain('ui.requiresRestart');
267
+ expect(uiKeys).toContain('ui.accessibility.disableLoadingPhrases');
179
268
  expect(uiKeys).not.toContain('ui.theme'); // This is now marked false
180
269
  });
181
270
  it('should not include Advanced category settings', () => {
@@ -187,13 +276,8 @@ describe('SettingsUtils', () => {
187
276
  const categories = getDialogSettingsByCategory();
188
277
  const allSettings = Object.values(categories).flat();
189
278
  const allKeys = allSettings.map((s) => s.key);
190
- expect(allKeys).toContain('general.vimMode');
191
- expect(allKeys).toContain('ide.enabled');
192
- expect(allKeys).toContain('general.disableAutoUpdate');
193
- expect(allKeys).toContain('ui.showMemoryUsage');
194
- expect(allKeys).not.toContain('privacy.usageStatisticsEnabled');
195
- expect(allKeys).not.toContain('security.auth.selectedType');
196
- expect(allKeys).not.toContain('tools.core');
279
+ expect(allKeys).toContain('test');
280
+ expect(allKeys).toContain('ui.requiresRestart');
197
281
  expect(allKeys).not.toContain('ui.theme'); // Now hidden
198
282
  expect(allKeys).not.toContain('general.preferredEditor'); // Now hidden
199
283
  });
@@ -202,9 +286,8 @@ describe('SettingsUtils', () => {
202
286
  it('should return only boolean dialog settings', () => {
203
287
  const booleanSettings = getDialogSettingsByType('boolean');
204
288
  const keys = booleanSettings.map((s) => s.key);
205
- expect(keys).toContain('ui.showMemoryUsage');
206
- expect(keys).toContain('general.vimMode');
207
- expect(keys).toContain('ui.hideWindowTitle');
289
+ expect(keys).toContain('ui.requiresRestart');
290
+ expect(keys).toContain('ui.accessibility.disableLoadingPhrases');
208
291
  expect(keys).not.toContain('privacy.usageStatisticsEnabled');
209
292
  expect(keys).not.toContain('security.auth.selectedType'); // Advanced setting
210
293
  expect(keys).not.toContain('security.auth.useExternal'); // Advanced setting
@@ -224,24 +307,11 @@ describe('SettingsUtils', () => {
224
307
  it('should return only settings marked for dialog display', () => {
225
308
  const dialogKeys = getDialogSettingKeys();
226
309
  // Should include settings marked for dialog
227
- expect(dialogKeys).toContain('ui.showMemoryUsage');
228
- expect(dialogKeys).toContain('general.vimMode');
229
- expect(dialogKeys).toContain('ui.hideWindowTitle');
230
- expect(dialogKeys).not.toContain('privacy.usageStatisticsEnabled');
231
- expect(dialogKeys).toContain('ide.enabled');
232
- expect(dialogKeys).toContain('general.disableAutoUpdate');
310
+ expect(dialogKeys).toContain('ui.requiresRestart');
233
311
  // Should include nested settings marked for dialog
234
- expect(dialogKeys).toContain('context.fileFiltering.respectGitIgnore');
235
- expect(dialogKeys).toContain('context.fileFiltering.respectGeminiIgnore');
236
- expect(dialogKeys).toContain('context.fileFiltering.enableRecursiveFileSearch');
312
+ expect(dialogKeys).toContain('ui.accessibility.disableLoadingPhrases');
237
313
  // Should NOT include settings marked as hidden
238
314
  expect(dialogKeys).not.toContain('ui.theme'); // Hidden
239
- expect(dialogKeys).not.toContain('ui.customThemes'); // Hidden
240
- expect(dialogKeys).not.toContain('general.preferredEditor'); // Hidden
241
- expect(dialogKeys).not.toContain('security.auth.selectedType'); // Advanced
242
- expect(dialogKeys).not.toContain('tools.core'); // Advanced
243
- expect(dialogKeys).not.toContain('mcpServers'); // Advanced
244
- expect(dialogKeys).not.toContain('telemetry'); // Advanced
245
315
  });
246
316
  it('should return fewer keys than getAllSettingKeys', () => {
247
317
  const allKeys = getAllSettingKeys();
@@ -250,10 +320,43 @@ describe('SettingsUtils', () => {
250
320
  expect(dialogKeys.length).toBeGreaterThan(0);
251
321
  });
252
322
  it('should handle nested settings display correctly', () => {
323
+ vi.mocked(getSettingsSchema).mockReturnValue({
324
+ context: {
325
+ type: 'object',
326
+ label: 'Context',
327
+ category: 'Context',
328
+ requiresRestart: false,
329
+ default: {},
330
+ description: 'Settings for managing context provided to the model.',
331
+ showInDialog: false,
332
+ properties: {
333
+ fileFiltering: {
334
+ type: 'object',
335
+ label: 'File Filtering',
336
+ category: 'Context',
337
+ requiresRestart: true,
338
+ default: {},
339
+ description: 'Settings for git-aware file filtering.',
340
+ showInDialog: false,
341
+ properties: {
342
+ respectGitIgnore: {
343
+ type: 'boolean',
344
+ label: 'Respect .gitignore',
345
+ category: 'Context',
346
+ requiresRestart: true,
347
+ default: true,
348
+ description: 'Respect .gitignore files when searching',
349
+ showInDialog: true,
350
+ },
351
+ },
352
+ },
353
+ },
354
+ },
355
+ });
253
356
  // Test the specific issue with fileFiltering.respectGitIgnore
254
357
  const key = 'context.fileFiltering.respectGitIgnore';
255
- const initialSettings = {};
256
- const pendingSettings = {};
358
+ const initialSettings = makeMockSettings({});
359
+ const pendingSettings = makeMockSettings({});
257
360
  // Set the nested setting to true
258
361
  const updatedPendingSettings = setPendingSettingValue(key, true, pendingSettings);
259
362
  // Check if the setting exists in pending settings
@@ -275,79 +378,83 @@ describe('SettingsUtils', () => {
275
378
  describe('Business Logic Utilities', () => {
276
379
  describe('getSettingValue', () => {
277
380
  it('should return value from settings when set', () => {
278
- const settings = { ui: { showMemoryUsage: true } };
279
- const mergedSettings = { ui: { showMemoryUsage: false } };
280
- const value = getSettingValue('ui.showMemoryUsage', settings, mergedSettings);
381
+ const settings = makeMockSettings({ ui: { requiresRestart: true } });
382
+ const mergedSettings = makeMockSettings({
383
+ ui: { requiresRestart: false },
384
+ });
385
+ const value = getSettingValue('ui.requiresRestart', settings, mergedSettings);
281
386
  expect(value).toBe(true);
282
387
  });
283
388
  it('should return value from merged settings when not set in current scope', () => {
284
- const settings = {};
285
- const mergedSettings = { ui: { showMemoryUsage: true } };
286
- const value = getSettingValue('ui.showMemoryUsage', settings, mergedSettings);
389
+ const settings = makeMockSettings({});
390
+ const mergedSettings = makeMockSettings({
391
+ ui: { requiresRestart: true },
392
+ });
393
+ const value = getSettingValue('ui.requiresRestart', settings, mergedSettings);
287
394
  expect(value).toBe(true);
288
395
  });
289
396
  it('should return default value for invalid setting', () => {
290
- const settings = {};
291
- const mergedSettings = {};
397
+ const settings = makeMockSettings({});
398
+ const mergedSettings = makeMockSettings({});
292
399
  const value = getSettingValue('invalidSetting', settings, mergedSettings);
293
400
  expect(value).toBe(false); // Default fallback
294
401
  });
295
402
  });
296
403
  describe('isSettingModified', () => {
297
404
  it('should return true when value differs from default', () => {
298
- expect(isSettingModified('ui.showMemoryUsage', true)).toBe(true);
299
- expect(isSettingModified('context.fileFiltering.enableRecursiveFileSearch', false)).toBe(true);
405
+ expect(isSettingModified('ui.requiresRestart', true)).toBe(true);
406
+ expect(isSettingModified('ui.accessibility.disableLoadingPhrases', true)).toBe(true);
300
407
  });
301
408
  it('should return false when value matches default', () => {
302
- expect(isSettingModified('ui.showMemoryUsage', false)).toBe(false);
303
- expect(isSettingModified('context.fileFiltering.enableRecursiveFileSearch', true)).toBe(false);
409
+ expect(isSettingModified('ui.requiresRestart', false)).toBe(false);
410
+ expect(isSettingModified('ui.accessibility.disableLoadingPhrases', false)).toBe(false);
304
411
  });
305
412
  });
306
413
  describe('settingExistsInScope', () => {
307
414
  it('should return true for top-level settings that exist', () => {
308
- const settings = { ui: { showMemoryUsage: true } };
309
- expect(settingExistsInScope('ui.showMemoryUsage', settings)).toBe(true);
415
+ const settings = makeMockSettings({ ui: { requiresRestart: true } });
416
+ expect(settingExistsInScope('ui.requiresRestart', settings)).toBe(true);
310
417
  });
311
418
  it('should return false for top-level settings that do not exist', () => {
312
- const settings = {};
313
- expect(settingExistsInScope('ui.showMemoryUsage', settings)).toBe(false);
419
+ const settings = makeMockSettings({});
420
+ expect(settingExistsInScope('ui.requiresRestart', settings)).toBe(false);
314
421
  });
315
422
  it('should return true for nested settings that exist', () => {
316
- const settings = {
423
+ const settings = makeMockSettings({
317
424
  ui: { accessibility: { disableLoadingPhrases: true } },
318
- };
425
+ });
319
426
  expect(settingExistsInScope('ui.accessibility.disableLoadingPhrases', settings)).toBe(true);
320
427
  });
321
428
  it('should return false for nested settings that do not exist', () => {
322
- const settings = {};
429
+ const settings = makeMockSettings({});
323
430
  expect(settingExistsInScope('ui.accessibility.disableLoadingPhrases', settings)).toBe(false);
324
431
  });
325
432
  it('should return false when parent exists but child does not', () => {
326
- const settings = { ui: { accessibility: {} } };
433
+ const settings = makeMockSettings({ ui: { accessibility: {} } });
327
434
  expect(settingExistsInScope('ui.accessibility.disableLoadingPhrases', settings)).toBe(false);
328
435
  });
329
436
  });
330
437
  describe('setPendingSettingValue', () => {
331
438
  it('should set top-level setting value', () => {
332
- const pendingSettings = {};
333
- const result = setPendingSettingValue('ui.showMemoryUsage', true, pendingSettings);
334
- expect(result.ui?.showMemoryUsage).toBe(true);
439
+ const pendingSettings = makeMockSettings({});
440
+ const result = setPendingSettingValue('ui.hideWindowTitle', true, pendingSettings);
441
+ expect(result.ui?.hideWindowTitle).toBe(true);
335
442
  });
336
443
  it('should set nested setting value', () => {
337
- const pendingSettings = {};
444
+ const pendingSettings = makeMockSettings({});
338
445
  const result = setPendingSettingValue('ui.accessibility.disableLoadingPhrases', true, pendingSettings);
339
446
  expect(result.ui?.accessibility?.disableLoadingPhrases).toBe(true);
340
447
  });
341
448
  it('should preserve existing nested settings', () => {
342
- const pendingSettings = {
449
+ const pendingSettings = makeMockSettings({
343
450
  ui: { accessibility: { disableLoadingPhrases: false } },
344
- };
451
+ });
345
452
  const result = setPendingSettingValue('ui.accessibility.disableLoadingPhrases', true, pendingSettings);
346
453
  expect(result.ui?.accessibility?.disableLoadingPhrases).toBe(true);
347
454
  });
348
455
  it('should not mutate original settings', () => {
349
- const pendingSettings = {};
350
- setPendingSettingValue('ui.showMemoryUsage', true, pendingSettings);
456
+ const pendingSettings = makeMockSettings({});
457
+ setPendingSettingValue('ui.requiresRestart', true, pendingSettings);
351
458
  expect(pendingSettings).toEqual({});
352
459
  });
353
460
  });
@@ -355,15 +462,12 @@ describe('SettingsUtils', () => {
355
462
  it('should return true when modified settings require restart', () => {
356
463
  const modifiedSettings = new Set([
357
464
  'advanced.autoConfigureMemory',
358
- 'ui.showMemoryUsage',
465
+ 'ui.requiresRestart',
359
466
  ]);
360
467
  expect(hasRestartRequiredSettings(modifiedSettings)).toBe(true);
361
468
  });
362
469
  it('should return false when no modified settings require restart', () => {
363
- const modifiedSettings = new Set([
364
- 'ui.showMemoryUsage',
365
- 'ui.hideTips',
366
- ]);
470
+ const modifiedSettings = new Set(['test']);
367
471
  expect(hasRestartRequiredSettings(modifiedSettings)).toBe(false);
368
472
  });
369
473
  it('should return false for empty set', () => {
@@ -374,18 +478,16 @@ describe('SettingsUtils', () => {
374
478
  describe('getRestartRequiredFromModified', () => {
375
479
  it('should return only settings that require restart', () => {
376
480
  const modifiedSettings = new Set([
377
- 'advanced.autoConfigureMemory',
378
- 'ui.showMemoryUsage',
379
- 'general.checkpointing.enabled',
481
+ 'ui.requiresRestart',
482
+ 'test',
380
483
  ]);
381
484
  const result = getRestartRequiredFromModified(modifiedSettings);
382
- expect(result).toContain('advanced.autoConfigureMemory');
383
- expect(result).toContain('general.checkpointing.enabled');
384
- expect(result).not.toContain('ui.showMemoryUsage');
485
+ expect(result).toContain('ui.requiresRestart');
486
+ expect(result).not.toContain('test');
385
487
  });
386
488
  it('should return empty array when no settings require restart', () => {
387
489
  const modifiedSettings = new Set([
388
- 'showMemoryUsage',
490
+ 'requiresRestart',
389
491
  'hideTips',
390
492
  ]);
391
493
  const result = getRestartRequiredFromModified(modifiedSettings);
@@ -393,116 +495,276 @@ describe('SettingsUtils', () => {
393
495
  });
394
496
  });
395
497
  describe('getDisplayValue', () => {
498
+ describe('enum behavior', () => {
499
+ let StringEnum;
500
+ (function (StringEnum) {
501
+ StringEnum["FOO"] = "foo";
502
+ StringEnum["BAR"] = "bar";
503
+ StringEnum["BAZ"] = "baz";
504
+ })(StringEnum || (StringEnum = {}));
505
+ let NumberEnum;
506
+ (function (NumberEnum) {
507
+ NumberEnum[NumberEnum["ONE"] = 1] = "ONE";
508
+ NumberEnum[NumberEnum["TWO"] = 2] = "TWO";
509
+ NumberEnum[NumberEnum["THREE"] = 3] = "THREE";
510
+ })(NumberEnum || (NumberEnum = {}));
511
+ const SETTING = {
512
+ type: 'enum',
513
+ label: 'Theme',
514
+ options: [
515
+ {
516
+ value: StringEnum.FOO,
517
+ label: 'Foo',
518
+ },
519
+ {
520
+ value: StringEnum.BAR,
521
+ label: 'Bar',
522
+ },
523
+ {
524
+ value: StringEnum.BAZ,
525
+ label: 'Baz',
526
+ },
527
+ ],
528
+ category: 'UI',
529
+ requiresRestart: false,
530
+ default: StringEnum.BAR,
531
+ description: 'The color theme for the UI.',
532
+ showInDialog: false,
533
+ };
534
+ it('handles display of number-based enums', () => {
535
+ vi.mocked(getSettingsSchema).mockReturnValue({
536
+ ui: {
537
+ properties: {
538
+ theme: {
539
+ ...SETTING,
540
+ options: [
541
+ {
542
+ value: NumberEnum.ONE,
543
+ label: 'One',
544
+ },
545
+ {
546
+ value: NumberEnum.TWO,
547
+ label: 'Two',
548
+ },
549
+ {
550
+ value: NumberEnum.THREE,
551
+ label: 'Three',
552
+ },
553
+ ],
554
+ },
555
+ },
556
+ },
557
+ });
558
+ const settings = makeMockSettings({
559
+ ui: { theme: NumberEnum.THREE },
560
+ });
561
+ const mergedSettings = makeMockSettings({
562
+ ui: { theme: NumberEnum.THREE },
563
+ });
564
+ const modifiedSettings = new Set();
565
+ const result = getDisplayValue('ui.theme', settings, mergedSettings, modifiedSettings);
566
+ expect(result).toBe('Three*');
567
+ });
568
+ it('handles default values for number-based enums', () => {
569
+ vi.mocked(getSettingsSchema).mockReturnValue({
570
+ ui: {
571
+ properties: {
572
+ theme: {
573
+ ...SETTING,
574
+ default: NumberEnum.THREE,
575
+ options: [
576
+ {
577
+ value: NumberEnum.ONE,
578
+ label: 'One',
579
+ },
580
+ {
581
+ value: NumberEnum.TWO,
582
+ label: 'Two',
583
+ },
584
+ {
585
+ value: NumberEnum.THREE,
586
+ label: 'Three',
587
+ },
588
+ ],
589
+ },
590
+ },
591
+ },
592
+ });
593
+ const modifiedSettings = new Set();
594
+ const result = getDisplayValue('ui.theme', makeMockSettings({}), makeMockSettings({}), modifiedSettings);
595
+ expect(result).toBe('Three');
596
+ });
597
+ it('shows the enum display value', () => {
598
+ vi.mocked(getSettingsSchema).mockReturnValue({
599
+ ui: { properties: { theme: { ...SETTING } } },
600
+ });
601
+ const settings = makeMockSettings({ ui: { theme: StringEnum.BAR } });
602
+ const mergedSettings = makeMockSettings({
603
+ ui: { theme: StringEnum.BAR },
604
+ });
605
+ const modifiedSettings = new Set();
606
+ const result = getDisplayValue('ui.theme', settings, mergedSettings, modifiedSettings);
607
+ expect(result).toBe('Bar*');
608
+ });
609
+ it('passes through unknown values verbatim', () => {
610
+ vi.mocked(getSettingsSchema).mockReturnValue({
611
+ ui: {
612
+ properties: {
613
+ theme: { ...SETTING },
614
+ },
615
+ },
616
+ });
617
+ const settings = makeMockSettings({ ui: { theme: 'xyz' } });
618
+ const mergedSettings = makeMockSettings({ ui: { theme: 'xyz' } });
619
+ const modifiedSettings = new Set();
620
+ const result = getDisplayValue('ui.theme', settings, mergedSettings, modifiedSettings);
621
+ expect(result).toBe('xyz*');
622
+ });
623
+ it('shows the default value for string enums', () => {
624
+ vi.mocked(getSettingsSchema).mockReturnValue({
625
+ ui: {
626
+ properties: {
627
+ theme: { ...SETTING, default: StringEnum.BAR },
628
+ },
629
+ },
630
+ });
631
+ const modifiedSettings = new Set();
632
+ const result = getDisplayValue('ui.theme', makeMockSettings({}), makeMockSettings({}), modifiedSettings);
633
+ expect(result).toBe('Bar');
634
+ });
635
+ });
396
636
  it('should show value without * when setting matches default', () => {
397
- const settings = { ui: { showMemoryUsage: false } }; // false matches default, so no *
398
- const mergedSettings = { ui: { showMemoryUsage: false } };
637
+ const settings = makeMockSettings({
638
+ ui: { requiresRestart: false },
639
+ }); // false matches default, so no *
640
+ const mergedSettings = makeMockSettings({
641
+ ui: { requiresRestart: false },
642
+ });
399
643
  const modifiedSettings = new Set();
400
- const result = getDisplayValue('ui.showMemoryUsage', settings, mergedSettings, modifiedSettings);
644
+ const result = getDisplayValue('ui.requiresRestart', settings, mergedSettings, modifiedSettings);
401
645
  expect(result).toBe('false*');
402
646
  });
403
647
  it('should show default value when setting is not in scope', () => {
404
- const settings = {}; // no setting in scope
405
- const mergedSettings = { ui: { showMemoryUsage: false } };
648
+ const settings = makeMockSettings({}); // no setting in scope
649
+ const mergedSettings = makeMockSettings({
650
+ ui: { requiresRestart: false },
651
+ });
406
652
  const modifiedSettings = new Set();
407
- const result = getDisplayValue('ui.showMemoryUsage', settings, mergedSettings, modifiedSettings);
653
+ const result = getDisplayValue('ui.requiresRestart', settings, mergedSettings, modifiedSettings);
408
654
  expect(result).toBe('false'); // shows default value
409
655
  });
410
656
  it('should show value with * when changed from default', () => {
411
- const settings = { ui: { showMemoryUsage: true } }; // true is different from default (false)
412
- const mergedSettings = { ui: { showMemoryUsage: true } };
657
+ const settings = makeMockSettings({ ui: { requiresRestart: true } }); // true is different from default (false)
658
+ const mergedSettings = makeMockSettings({
659
+ ui: { requiresRestart: true },
660
+ });
413
661
  const modifiedSettings = new Set();
414
- const result = getDisplayValue('ui.showMemoryUsage', settings, mergedSettings, modifiedSettings);
662
+ const result = getDisplayValue('ui.requiresRestart', settings, mergedSettings, modifiedSettings);
415
663
  expect(result).toBe('true*');
416
664
  });
417
665
  it('should show default value without * when setting does not exist in scope', () => {
418
- const settings = {}; // setting doesn't exist in scope, show default
419
- const mergedSettings = { ui: { showMemoryUsage: false } };
666
+ const settings = makeMockSettings({}); // setting doesn't exist in scope, show default
667
+ const mergedSettings = makeMockSettings({
668
+ ui: { requiresRestart: false },
669
+ });
420
670
  const modifiedSettings = new Set();
421
- const result = getDisplayValue('ui.showMemoryUsage', settings, mergedSettings, modifiedSettings);
671
+ const result = getDisplayValue('ui.requiresRestart', settings, mergedSettings, modifiedSettings);
422
672
  expect(result).toBe('false'); // default value (false) without *
423
673
  });
424
674
  it('should show value with * when user changes from default', () => {
425
- const settings = {}; // setting doesn't exist in scope originally
426
- const mergedSettings = { ui: { showMemoryUsage: false } };
427
- const modifiedSettings = new Set(['ui.showMemoryUsage']);
428
- const pendingSettings = { ui: { showMemoryUsage: true } }; // user changed to true
429
- const result = getDisplayValue('ui.showMemoryUsage', settings, mergedSettings, modifiedSettings, pendingSettings);
675
+ const settings = makeMockSettings({}); // setting doesn't exist in scope originally
676
+ const mergedSettings = makeMockSettings({
677
+ ui: { requiresRestart: false },
678
+ });
679
+ const modifiedSettings = new Set(['ui.requiresRestart']);
680
+ const pendingSettings = makeMockSettings({
681
+ ui: { requiresRestart: true },
682
+ }); // user changed to true
683
+ const result = getDisplayValue('ui.requiresRestart', settings, mergedSettings, modifiedSettings, pendingSettings);
430
684
  expect(result).toBe('true*'); // changed from default (false) to true
431
685
  });
432
686
  });
433
687
  describe('isDefaultValue', () => {
434
688
  it('should return true when setting does not exist in scope', () => {
435
- const settings = {}; // setting doesn't exist
436
- const result = isDefaultValue('ui.showMemoryUsage', settings);
689
+ const settings = makeMockSettings({}); // setting doesn't exist
690
+ const result = isDefaultValue('ui.requiresRestart', settings);
437
691
  expect(result).toBe(true);
438
692
  });
439
693
  it('should return false when setting exists in scope', () => {
440
- const settings = { ui: { showMemoryUsage: true } }; // setting exists
441
- const result = isDefaultValue('ui.showMemoryUsage', settings);
694
+ const settings = makeMockSettings({ ui: { requiresRestart: true } }); // setting exists
695
+ const result = isDefaultValue('ui.requiresRestart', settings);
442
696
  expect(result).toBe(false);
443
697
  });
444
698
  it('should return true when nested setting does not exist in scope', () => {
445
- const settings = {}; // nested setting doesn't exist
699
+ const settings = makeMockSettings({}); // nested setting doesn't exist
446
700
  const result = isDefaultValue('ui.accessibility.disableLoadingPhrases', settings);
447
701
  expect(result).toBe(true);
448
702
  });
449
703
  it('should return false when nested setting exists in scope', () => {
450
- const settings = {
704
+ const settings = makeMockSettings({
451
705
  ui: { accessibility: { disableLoadingPhrases: true } },
452
- }; // nested setting exists
706
+ }); // nested setting exists
453
707
  const result = isDefaultValue('ui.accessibility.disableLoadingPhrases', settings);
454
708
  expect(result).toBe(false);
455
709
  });
456
710
  });
457
711
  describe('isValueInherited', () => {
458
712
  it('should return false for top-level settings that exist in scope', () => {
459
- const settings = { ui: { showMemoryUsage: true } };
460
- const mergedSettings = { ui: { showMemoryUsage: true } };
461
- const result = isValueInherited('ui.showMemoryUsage', settings, mergedSettings);
713
+ const settings = makeMockSettings({ ui: { requiresRestart: true } });
714
+ const mergedSettings = makeMockSettings({
715
+ ui: { requiresRestart: true },
716
+ });
717
+ const result = isValueInherited('ui.requiresRestart', settings, mergedSettings);
462
718
  expect(result).toBe(false);
463
719
  });
464
720
  it('should return true for top-level settings that do not exist in scope', () => {
465
- const settings = {};
466
- const mergedSettings = { ui: { showMemoryUsage: true } };
467
- const result = isValueInherited('ui.showMemoryUsage', settings, mergedSettings);
721
+ const settings = makeMockSettings({});
722
+ const mergedSettings = makeMockSettings({
723
+ ui: { requiresRestart: true },
724
+ });
725
+ const result = isValueInherited('ui.requiresRestart', settings, mergedSettings);
468
726
  expect(result).toBe(true);
469
727
  });
470
728
  it('should return false for nested settings that exist in scope', () => {
471
- const settings = {
729
+ const settings = makeMockSettings({
472
730
  ui: { accessibility: { disableLoadingPhrases: true } },
473
- };
474
- const mergedSettings = {
731
+ });
732
+ const mergedSettings = makeMockSettings({
475
733
  ui: { accessibility: { disableLoadingPhrases: true } },
476
- };
734
+ });
477
735
  const result = isValueInherited('ui.accessibility.disableLoadingPhrases', settings, mergedSettings);
478
736
  expect(result).toBe(false);
479
737
  });
480
738
  it('should return true for nested settings that do not exist in scope', () => {
481
- const settings = {};
482
- const mergedSettings = {
739
+ const settings = makeMockSettings({});
740
+ const mergedSettings = makeMockSettings({
483
741
  ui: { accessibility: { disableLoadingPhrases: true } },
484
- };
742
+ });
485
743
  const result = isValueInherited('ui.accessibility.disableLoadingPhrases', settings, mergedSettings);
486
744
  expect(result).toBe(true);
487
745
  });
488
746
  });
489
747
  describe('getEffectiveDisplayValue', () => {
490
748
  it('should return value from settings when available', () => {
491
- const settings = { ui: { showMemoryUsage: true } };
492
- const mergedSettings = { ui: { showMemoryUsage: false } };
493
- const result = getEffectiveDisplayValue('ui.showMemoryUsage', settings, mergedSettings);
749
+ const settings = makeMockSettings({ ui: { requiresRestart: true } });
750
+ const mergedSettings = makeMockSettings({
751
+ ui: { requiresRestart: false },
752
+ });
753
+ const result = getEffectiveDisplayValue('ui.requiresRestart', settings, mergedSettings);
494
754
  expect(result).toBe(true);
495
755
  });
496
756
  it('should return value from merged settings when not in scope', () => {
497
- const settings = {};
498
- const mergedSettings = { ui: { showMemoryUsage: true } };
499
- const result = getEffectiveDisplayValue('ui.showMemoryUsage', settings, mergedSettings);
757
+ const settings = makeMockSettings({});
758
+ const mergedSettings = makeMockSettings({
759
+ ui: { requiresRestart: true },
760
+ });
761
+ const result = getEffectiveDisplayValue('ui.requiresRestart', settings, mergedSettings);
500
762
  expect(result).toBe(true);
501
763
  });
502
764
  it('should return default value for undefined values', () => {
503
- const settings = {};
504
- const mergedSettings = {};
505
- const result = getEffectiveDisplayValue('ui.showMemoryUsage', settings, mergedSettings);
765
+ const settings = makeMockSettings({});
766
+ const mergedSettings = makeMockSettings({});
767
+ const result = getEffectiveDisplayValue('ui.requiresRestart', settings, mergedSettings);
506
768
  expect(result).toBe(false); // Default value
507
769
  });
508
770
  });