diff --git a/packages/cli/src/config/config.test.ts b/packages/cli/src/config/config.test.ts index 67d3b114b..16ec45c65 100644 --- a/packages/cli/src/config/config.test.ts +++ b/packages/cli/src/config/config.test.ts @@ -2045,13 +2045,13 @@ describe('loadCliConfig fileFiltering', () => { value: boolean; }> = [ { - property: 'disableFuzzySearch', - getter: (c) => c.getFileFilteringDisableFuzzySearch(), + property: 'enableFuzzySearch', + getter: (c) => c.getFileFilteringEnableFuzzySearch(), value: true, }, { - property: 'disableFuzzySearch', - getter: (c) => c.getFileFilteringDisableFuzzySearch(), + property: 'enableFuzzySearch', + getter: (c) => c.getFileFilteringEnableFuzzySearch(), value: false, }, { diff --git a/packages/cli/src/config/settings.ts b/packages/cli/src/config/settings.ts index ea41c578c..1f8e2efa6 100644 --- a/packages/cli/src/config/settings.ts +++ b/packages/cli/src/config/settings.ts @@ -229,6 +229,14 @@ function setNestedProperty( current[lastKey] = value; } +// Dynamically determine the top-level keys from the V2 settings structure. +const KNOWN_V2_CONTAINERS = new Set([ + ...Object.values(MIGRATION_MAP).map((path) => path.split('.')[0]), + ...Object.values(INVERTED_BOOLEAN_MIGRATIONS).map( + (path) => path.split('.')[0], + ), +]); + export function needsMigration(settings: Record): boolean { // Check version field first - if present and matches current version, no migration needed if (SETTINGS_VERSION_KEY in settings) { @@ -410,9 +418,26 @@ const REVERSE_MIGRATION_MAP: Record = Object.fromEntries( Object.entries(MIGRATION_MAP).map(([key, value]) => [value, key]), ); -// Dynamically determine the top-level keys from the V2 settings structure. -const KNOWN_V2_CONTAINERS = new Set( - Object.values(MIGRATION_MAP).map((path) => path.split('.')[0]), +// Reverse map for old V2 paths (before rename) to V1 keys. +// Used when migrating settings that still have old V2 naming (e.g., general.disableAutoUpdate). +const OLD_V2_TO_V1_MAP: Record = {}; +for (const [oldV2Path, newV3Path] of Object.entries(INVERTED_V2_PATHS)) { + // Find the V1 key that maps to this V3 path + for (const [v1Key, v3Path] of Object.entries(INVERTED_BOOLEAN_MIGRATIONS)) { + if (v3Path === newV3Path) { + OLD_V2_TO_V1_MAP[oldV2Path] = v1Key; + break; + } + } +} + +// Reverse map for new V3 paths to V1 keys (with boolean inversion). +// Used when migrating settings that have new V3 naming (e.g., general.enableAutoUpdate). +const V3_TO_V1_INVERTED_MAP: Record = Object.fromEntries( + Object.entries(INVERTED_BOOLEAN_MIGRATIONS).map(([v1Key, v3Path]) => [ + v3Path, + v1Key, + ]), ); function getSettingsFileKeyWarnings( @@ -451,7 +476,7 @@ function getSettingsFileKeyWarnings( ignoredLegacyKeys.add(oldKey); warnings.push( - `⚠️ Legacy setting '${oldKey}' will be ignored in ${settingsFilePath}. Please use '${newPath}' instead.`, + `Warning: Legacy setting '${oldKey}' will be ignored in ${settingsFilePath}. Please use '${newPath}' instead.`, ); } @@ -469,7 +494,7 @@ function getSettingsFileKeyWarnings( } warnings.push( - `⚠️ Unknown setting '${key}' will be ignored in ${settingsFilePath}.`, + `Warning: Unknown setting '${key}' will be ignored in ${settingsFilePath}.`, ); } @@ -488,7 +513,8 @@ export function getSettingsWarnings(loadedSettings: LoadedSettings): string[] { for (const scope of [SettingScope.User, SettingScope.Workspace]) { const settingsFile = loadedSettings.forScope(scope); if (settingsFile.rawJson === undefined) { - continue; // File not present / not loaded. + continue; + // File not present / not loaded. } const settingsObject = settingsFile.originalSettings as unknown as Record< string, @@ -520,6 +546,26 @@ export function migrateSettingsToV1( } } + // Handle old V2 inverted paths (no value inversion needed) + // e.g., general.disableAutoUpdate -> disableAutoUpdate + for (const [oldV2Path, v1Key] of Object.entries(OLD_V2_TO_V1_MAP)) { + const value = getNestedProperty(v2Settings, oldV2Path); + if (value !== undefined) { + v1Settings[v1Key] = value; + v2Keys.delete(oldV2Path.split('.')[0]); + } + } + + // Handle new V3 inverted paths (WITH value inversion) + // e.g., general.enableAutoUpdate -> disableAutoUpdate (inverted) + for (const [v3Path, v1Key] of Object.entries(V3_TO_V1_INVERTED_MAP)) { + const value = getNestedProperty(v2Settings, v3Path); + if (value !== undefined && typeof value === 'boolean') { + v1Settings[v1Key] = !value; + v2Keys.delete(v3Path.split('.')[0]); + } + } + // Preserve mcpServers at the top level if (v2Settings['mcpServers']) { v1Settings['mcpServers'] = v2Settings['mcpServers']; diff --git a/packages/cli/src/ui/hooks/useAtCompletion.test.ts b/packages/cli/src/ui/hooks/useAtCompletion.test.ts index e379b87b0..1c7795453 100644 --- a/packages/cli/src/ui/hooks/useAtCompletion.test.ts +++ b/packages/cli/src/ui/hooks/useAtCompletion.test.ts @@ -197,7 +197,7 @@ describe('useAtCompletion', () => { cache: false, cacheTtl: 0, enableRecursiveFileSearch: true, - disableFuzzySearch: false, + enableFuzzySearch: true, }); await realFileSearch.initialize();