From f80a3c031bca7006814f90ebbef1f02e7f62fec3 Mon Sep 17 00:00:00 2001 From: "Steven T. Cramer" Date: Sun, 23 Mar 2025 00:17:03 +0700 Subject: [PATCH 1/9] Replace hardcoded custom modes filename with GlobalFileNames constant --- src/core/config/CustomModesManager.ts | 3 ++- .../config/__tests__/CustomModesManager.test.ts | 14 +++++++------- src/shared/globalFileNames.ts | 1 + 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/core/config/CustomModesManager.ts b/src/core/config/CustomModesManager.ts index cb4759fca43..c701f38f910 100644 --- a/src/core/config/CustomModesManager.ts +++ b/src/core/config/CustomModesManager.ts @@ -6,6 +6,7 @@ import { ModeConfig } from "../../shared/modes" import { fileExistsAtPath } from "../../utils/fs" import { arePathsEqual, getWorkspacePath } from "../../utils/path" import { logger } from "../../utils/logging" +import { GlobalFileNames } from "../../shared/globalFileNames" const ROOMODES_FILENAME = ".roomodes" @@ -113,7 +114,7 @@ export class CustomModesManager { async getCustomModesFilePath(): Promise { const settingsDir = await this.ensureSettingsDirectoryExists() - const filePath = path.join(settingsDir, "cline_custom_modes.json") + const filePath = path.join(settingsDir, GlobalFileNames.customModes) const fileExists = await fileExistsAtPath(filePath) if (!fileExists) { await this.queueWrite(async () => { diff --git a/src/core/config/__tests__/CustomModesManager.test.ts b/src/core/config/__tests__/CustomModesManager.test.ts index 300f1b7c0ff..3af26c92b80 100644 --- a/src/core/config/__tests__/CustomModesManager.test.ts +++ b/src/core/config/__tests__/CustomModesManager.test.ts @@ -7,6 +7,7 @@ import { CustomModesManager } from "../CustomModesManager" import { ModeConfig } from "../../../shared/modes" import { fileExistsAtPath } from "../../../utils/fs" import { getWorkspacePath, arePathsEqual } from "../../../utils/path" +import { GlobalFileNames } from "../../../shared/globalFileNames" jest.mock("vscode") jest.mock("fs/promises") @@ -21,7 +22,7 @@ describe("CustomModesManager", () => { // Use path.sep to ensure correct path separators for the current platform const mockStoragePath = `${path.sep}mock${path.sep}settings` - const mockSettingsPath = path.join(mockStoragePath, "settings", "cline_custom_modes.json") + const mockSettingsPath = path.join(mockStoragePath, "settings", GlobalFileNames.customModes) const mockRoomodes = `${path.sep}mock${path.sep}workspace${path.sep}.roomodes` beforeEach(() => { @@ -333,17 +334,16 @@ describe("CustomModesManager", () => { expect(mockOnUpdate).toHaveBeenCalled() }) }) - describe("File Operations", () => { it("creates settings directory if it doesn't exist", async () => { - const configPath = path.join(mockStoragePath, "settings", "cline_custom_modes.json") + const settingsPath = path.join(mockStoragePath, "settings", GlobalFileNames.customModes) await manager.getCustomModesFilePath() - expect(fs.mkdir).toHaveBeenCalledWith(path.dirname(configPath), { recursive: true }) + expect(fs.mkdir).toHaveBeenCalledWith(path.dirname(settingsPath), { recursive: true }) }) it("creates default config if file doesn't exist", async () => { - const configPath = path.join(mockStoragePath, "settings", "cline_custom_modes.json") + const settingsPath = path.join(mockStoragePath, "settings", GlobalFileNames.customModes) // Mock fileExists to return false first time, then true let firstCall = true @@ -358,13 +358,13 @@ describe("CustomModesManager", () => { await manager.getCustomModesFilePath() expect(fs.writeFile).toHaveBeenCalledWith( - configPath, + settingsPath, expect.stringMatching(/^\{\s+"customModes":\s+\[\s*\]\s*\}$/), ) }) it("watches file for changes", async () => { - const configPath = path.join(mockStoragePath, "settings", "cline_custom_modes.json") + const configPath = path.join(mockStoragePath, "settings", GlobalFileNames.customModes) ;(fs.readFile as jest.Mock).mockResolvedValue(JSON.stringify({ customModes: [] })) ;(arePathsEqual as jest.Mock).mockImplementation((path1: string, path2: string) => { diff --git a/src/shared/globalFileNames.ts b/src/shared/globalFileNames.ts index 6088e95d999..55b88b1df2c 100644 --- a/src/shared/globalFileNames.ts +++ b/src/shared/globalFileNames.ts @@ -6,4 +6,5 @@ export const GlobalFileNames = { requestyModels: "requesty_models.json", mcpSettings: "cline_mcp_settings.json", unboundModels: "unbound_models.json", + customModes: "custom_modes.json", } From 3b351b746ee00ee17b390a9095d60af5c73eff50 Mon Sep 17 00:00:00 2001 From: "Steven T. Cramer" Date: Sun, 23 Mar 2025 00:32:27 +0700 Subject: [PATCH 2/9] Remove 'cline' prefix from mcpSettings filename --- src/shared/globalFileNames.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/globalFileNames.ts b/src/shared/globalFileNames.ts index 55b88b1df2c..f26174d224c 100644 --- a/src/shared/globalFileNames.ts +++ b/src/shared/globalFileNames.ts @@ -4,7 +4,7 @@ export const GlobalFileNames = { glamaModels: "glama_models.json", openRouterModels: "openrouter_models.json", requestyModels: "requesty_models.json", - mcpSettings: "cline_mcp_settings.json", + mcpSettings: "mcp_settings.json", unboundModels: "unbound_models.json", customModes: "custom_modes.json", } From 151fabb6d542ec3cd0b5fd5c98a95ed1631813f7 Mon Sep 17 00:00:00 2001 From: "Steven T. Cramer" Date: Sun, 23 Mar 2025 01:20:27 +0700 Subject: [PATCH 3/9] Use GlobalFileNames.customModes in modes.ts --- src/core/prompts/sections/modes.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/prompts/sections/modes.ts b/src/core/prompts/sections/modes.ts index 78b94ec9e76..0015b8917c3 100644 --- a/src/core/prompts/sections/modes.ts +++ b/src/core/prompts/sections/modes.ts @@ -2,11 +2,12 @@ import * as path from "path" import * as vscode from "vscode" import { promises as fs } from "fs" import { ModeConfig, getAllModesWithPrompts } from "../../../shared/modes" +import { GlobalFileNames } from "../../../shared/globalFileNames" export async function getModesSection(context: vscode.ExtensionContext): Promise { const settingsDir = path.join(context.globalStorageUri.fsPath, "settings") await fs.mkdir(settingsDir, { recursive: true }) - const customModesPath = path.join(settingsDir, "cline_custom_modes.json") + const customModesPath = path.join(settingsDir, GlobalFileNames.customModes) // Get all modes with their overrides from extension state const allModes = await getAllModesWithPrompts(context) From bbe91b46e8714e2a26add9f9f1814c86f2f8917d Mon Sep 17 00:00:00 2001 From: "Steven T. Cramer" Date: Sun, 23 Mar 2025 02:19:50 +0700 Subject: [PATCH 4/9] replace two more `cline_mcp_settings` with `mcp_settings` in tests. --- src/__mocks__/fs/promises.ts | 2 +- src/services/mcp/__tests__/McpHub.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/__mocks__/fs/promises.ts b/src/__mocks__/fs/promises.ts index e496a7fa510..f673f876882 100644 --- a/src/__mocks__/fs/promises.ts +++ b/src/__mocks__/fs/promises.ts @@ -162,7 +162,7 @@ const mockFs = { _setInitialMockData: () => { // Set up default MCP settings mockFiles.set( - "/mock/settings/path/cline_mcp_settings.json", + "/mock/settings/path/mcp_settings.json", JSON.stringify({ mcpServers: { "test-server": { diff --git a/src/services/mcp/__tests__/McpHub.test.ts b/src/services/mcp/__tests__/McpHub.test.ts index 737ed7f11e7..7fcce6662a0 100644 --- a/src/services/mcp/__tests__/McpHub.test.ts +++ b/src/services/mcp/__tests__/McpHub.test.ts @@ -14,7 +14,7 @@ jest.mock("../../../core/webview/ClineProvider") describe("McpHub", () => { let mcpHub: McpHubType let mockProvider: Partial - const mockSettingsPath = "/mock/settings/path/cline_mcp_settings.json" + const mockSettingsPath = "/mock/settings/path/mcp_settings.json" beforeEach(() => { jest.clearAllMocks() From 07a622015d66a763195d5c73a496dc82e1975f21 Mon Sep 17 00:00:00 2001 From: "Steven T. Cramer" Date: Mon, 24 Mar 2025 01:59:30 +0700 Subject: [PATCH 5/9] feat: add settings file migration for new file naming convention - Implement migrateSettings function to rename legacy settings files to new format - Migrate cline_custom_modes.json to new custom modes filename - Migrate cline_mcp_settings.json to new MCP settings filename - Add TODO to remove migration code in September 2025 (6 months after implementation) - Make activate function async to support migration on startup --- src/extension.ts | 55 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/src/extension.ts b/src/extension.ts index 05f8afe969b..a9f01ce4e43 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,5 +1,8 @@ import * as vscode from "vscode" import * as dotenvx from "@dotenvx/dotenvx" +import * as path from "path" +import * as fs from "fs/promises" +import * as fsSync from "fs" // Load environment variables from .env file try { @@ -21,6 +24,8 @@ import { McpServerManager } from "./services/mcp/McpServerManager" import { telemetryService } from "./services/telemetry/TelemetryService" import { TerminalRegistry } from "./integrations/terminal/TerminalRegistry" import { API } from "./exports/api" +import { fileExistsAtPath } from "./utils/fs" +import { GlobalFileNames } from "./shared/globalFileNames" import { handleUri, registerCommands, registerCodeActions, registerTerminalActions } from "./activate" import { formatLanguage } from "./shared/language" @@ -36,14 +41,62 @@ import { formatLanguage } from "./shared/language" let outputChannel: vscode.OutputChannel let extensionContext: vscode.ExtensionContext +/** + * Migrates old settings files to new file names + * + * TODO: Remove this migration code in September 2025 (6 months after implementation) + */ +export async function migrateSettings(context: vscode.ExtensionContext): Promise { + // Legacy file names that need to be migrated to the new names in GlobalFileNames + const fileMigrations = [ + { oldName: "cline_custom_modes.json", newName: GlobalFileNames.customModes }, + { oldName: "cline_mcp_settings.json", newName: GlobalFileNames.mcpSettings }, + ] + + try { + const settingsDir = path.join(context.globalStorageUri.fsPath, "settings") + + // Check if settings directory exists first + if (!(await fileExistsAtPath(settingsDir))) { + outputChannel.appendLine("No settings directory found, no migrations necessary") + return + } + + // Process each file migration + for (const migration of fileMigrations) { + const oldPath = path.join(settingsDir, migration.oldName) + const newPath = path.join(settingsDir, migration.newName) + + // Only migrate if old file exists and new file doesn't exist yet + // This ensures we don't overwrite any existing new files + const oldFileExists = await fileExistsAtPath(oldPath) + const newFileExists = await fileExistsAtPath(newPath) + + if (oldFileExists && !newFileExists) { + await fs.rename(oldPath, newPath) + outputChannel.appendLine(`Renamed ${migration.oldName} to ${migration.newName}`) + } else { + outputChannel.appendLine( + `Skipping migration of ${migration.oldName} to ${migration.newName}: ${oldFileExists ? "new file already exists" : "old file not found"}`, + ) + } + } + } catch (error) { + outputChannel.appendLine(`Error migrating settings files: ${error}`) + } +} + // This method is called when your extension is activated. // Your extension is activated the very first time the command is executed. -export function activate(context: vscode.ExtensionContext) { +export async function activate(context: vscode.ExtensionContext) { extensionContext = context outputChannel = vscode.window.createOutputChannel("Roo-Code") context.subscriptions.push(outputChannel) outputChannel.appendLine("Roo-Code extension activated") + // Migrate old settings to new + await migrateSettings(context) + // Initialize telemetry service after environment variables are loaded. telemetryService.initialize() From 9aa112e161756a488fd6a2689a1d72be428f33d5 Mon Sep 17 00:00:00 2001 From: "Steven T. Cramer" Date: Mon, 24 Mar 2025 02:12:41 +0700 Subject: [PATCH 6/9] Add associated changeset --- .changeset/heavy-eyes-reply.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/heavy-eyes-reply.md diff --git a/.changeset/heavy-eyes-reply.md b/.changeset/heavy-eyes-reply.md new file mode 100644 index 00000000000..8a43a7c05f5 --- /dev/null +++ b/.changeset/heavy-eyes-reply.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Add settings migration to support renaming legacy settings files to new format From 8cdd6d51a8371b4fadd2eeb7e01962de65f18b95 Mon Sep 17 00:00:00 2001 From: "Steven T. Cramer" Date: Mon, 24 Mar 2025 08:27:45 +0700 Subject: [PATCH 7/9] removed unused import --- src/extension.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/extension.ts b/src/extension.ts index a9f01ce4e43..f64d6de7929 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -2,7 +2,6 @@ import * as vscode from "vscode" import * as dotenvx from "@dotenvx/dotenvx" import * as path from "path" import * as fs from "fs/promises" -import * as fsSync from "fs" // Load environment variables from .env file try { From 24cc9cf7e6aa506566d2499fc19c75e7ccfab0db Mon Sep 17 00:00:00 2001 From: "Steven T. Cramer" Date: Mon, 24 Mar 2025 23:54:13 +0700 Subject: [PATCH 8/9] refactor: move migrateSettings to dedicated utility file - Extract migrateSettings function from extension.ts to src/utils/migrateSettings.ts - Update extension.ts to import and use the extracted function - Update tests to use the real implementation - Improve dependency injection by passing outputChannel as parameter - Enhance maintainability by isolating temporary migration code (to be removed Sept 2025) --- src/__mocks__/fs/promises.ts | 16 ++++ src/__tests__/migrateSettings.test.ts | 119 ++++++++++++++++++++++++++ src/extension.ts | 53 +----------- src/utils/migrateSettings.ts | 53 ++++++++++++ 4 files changed, 192 insertions(+), 49 deletions(-) create mode 100644 src/__tests__/migrateSettings.test.ts create mode 100644 src/utils/migrateSettings.ts diff --git a/src/__mocks__/fs/promises.ts b/src/__mocks__/fs/promises.ts index f673f876882..b037cd24573 100644 --- a/src/__mocks__/fs/promises.ts +++ b/src/__mocks__/fs/promises.ts @@ -152,6 +152,22 @@ const mockFs = { throw error }), + rename: jest.fn().mockImplementation(async (oldPath: string, newPath: string) => { + // Check if the old file exists + if (mockFiles.has(oldPath)) { + // Copy content to new path + const content = mockFiles.get(oldPath) + mockFiles.set(newPath, content) + // Delete old file + mockFiles.delete(oldPath) + return Promise.resolve() + } + // If old file doesn't exist, throw an error + const error = new Error(`ENOENT: no such file or directory, rename '${oldPath}'`) + ;(error as any).code = "ENOENT" + throw error + }), + constants: jest.requireActual("fs").constants, // Expose mock data for test assertions diff --git a/src/__tests__/migrateSettings.test.ts b/src/__tests__/migrateSettings.test.ts new file mode 100644 index 00000000000..107f3106396 --- /dev/null +++ b/src/__tests__/migrateSettings.test.ts @@ -0,0 +1,119 @@ +import * as vscode from "vscode" +import * as path from "path" +import * as fs from "fs/promises" +import { fileExistsAtPath } from "../utils/fs" +import { GlobalFileNames } from "../shared/globalFileNames" +import { migrateSettings } from "../utils/migrateSettings" + +// Mock dependencies +jest.mock("vscode") +jest.mock("fs/promises") +jest.mock("fs") +jest.mock("../utils/fs") +// We're testing the real migrateSettings function + +describe("Settings Migration", () => { + let mockContext: vscode.ExtensionContext + let mockOutputChannel: vscode.OutputChannel + const mockStoragePath = "/mock/storage" + const mockSettingsDir = path.join(mockStoragePath, "settings") + + // Legacy file names + const legacyCustomModesPath = path.join(mockSettingsDir, "cline_custom_modes.json") + const legacyMcpSettingsPath = path.join(mockSettingsDir, "cline_mcp_settings.json") + + // New file names + const newCustomModesPath = path.join(mockSettingsDir, GlobalFileNames.customModes) + const newMcpSettingsPath = path.join(mockSettingsDir, GlobalFileNames.mcpSettings) + + beforeEach(() => { + jest.clearAllMocks() + + // Mock output channel + mockOutputChannel = { + appendLine: jest.fn(), + append: jest.fn(), + clear: jest.fn(), + show: jest.fn(), + hide: jest.fn(), + dispose: jest.fn(), + } as unknown as vscode.OutputChannel + + // Mock extension context + mockContext = { + globalStorageUri: { fsPath: mockStoragePath }, + } as unknown as vscode.ExtensionContext + + // The fs/promises mock is already set up in src/__mocks__/fs/promises.ts + // We don't need to manually mock these methods + + // Set global outputChannel for all tests + ;(global as any).outputChannel = mockOutputChannel + }) + + it("should migrate custom modes file if old file exists and new file doesn't", async () => { + const mockCustomModesContent = '{"customModes":[{"slug":"test-mode"}]}' as string + + // Mock file existence checks + ;(fileExistsAtPath as jest.Mock).mockImplementation(async (path: string) => { + if (path === mockSettingsDir) return true + if (path === legacyCustomModesPath) return true + if (path === newCustomModesPath) return false + return false + }) + + await migrateSettings(mockContext, mockOutputChannel) + + // Verify file was renamed + expect(fs.rename).toHaveBeenCalledWith(legacyCustomModesPath, newCustomModesPath) + }) + + it("should migrate MCP settings file if old file exists and new file doesn't", async () => { + const mockMcpSettingsContent = '{"mcpServers":{"test-server":{}}}' as string + + // Mock file existence checks + ;(fileExistsAtPath as jest.Mock).mockImplementation(async (path: string) => { + if (path === mockSettingsDir) return true + if (path === legacyMcpSettingsPath) return true + if (path === newMcpSettingsPath) return false + return false + }) + + await migrateSettings(mockContext, mockOutputChannel) + + // Verify file was renamed + expect(fs.rename).toHaveBeenCalledWith(legacyMcpSettingsPath, newMcpSettingsPath) + }) + + it("should not migrate if new file already exists", async () => { + // Mock file existence checks + ;(fileExistsAtPath as jest.Mock).mockImplementation(async (path: string) => { + if (path === mockSettingsDir) return true + if (path === legacyCustomModesPath) return true + if (path === newCustomModesPath) return true + if (path === legacyMcpSettingsPath) return true + if (path === newMcpSettingsPath) return true + return false + }) + + await migrateSettings(mockContext, mockOutputChannel) + + // Verify no files were renamed + expect(fs.rename).not.toHaveBeenCalled() + }) + + it("should handle errors gracefully", async () => { + // Mock file existence checks to throw an error + ;(fileExistsAtPath as jest.Mock).mockRejectedValue(new Error("Test error")) + + // Set the global outputChannel for the test + ;(global as any).outputChannel = mockOutputChannel + + await migrateSettings(mockContext, mockOutputChannel) + + // Verify error was logged + expect(mockOutputChannel.appendLine).toHaveBeenCalledWith( + expect.stringContaining("Error migrating settings files"), + ) + }) +}) diff --git a/src/extension.ts b/src/extension.ts index f64d6de7929..a2bece9ec76 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,12 +1,11 @@ import * as vscode from "vscode" import * as dotenvx from "@dotenvx/dotenvx" import * as path from "path" -import * as fs from "fs/promises" // Load environment variables from .env file try { // Specify path to .env file in the project root directory - const envPath = __dirname + "/../.env" + const envPath = path.join(__dirname, "..", ".env") dotenvx.config({ path: envPath }) } catch (e) { // Silently handle environment loading errors @@ -23,8 +22,7 @@ import { McpServerManager } from "./services/mcp/McpServerManager" import { telemetryService } from "./services/telemetry/TelemetryService" import { TerminalRegistry } from "./integrations/terminal/TerminalRegistry" import { API } from "./exports/api" -import { fileExistsAtPath } from "./utils/fs" -import { GlobalFileNames } from "./shared/globalFileNames" +import { migrateSettings } from "./utils/migrateSettings" import { handleUri, registerCommands, registerCodeActions, registerTerminalActions } from "./activate" import { formatLanguage } from "./shared/language" @@ -40,50 +38,7 @@ import { formatLanguage } from "./shared/language" let outputChannel: vscode.OutputChannel let extensionContext: vscode.ExtensionContext -/** - * Migrates old settings files to new file names - * - * TODO: Remove this migration code in September 2025 (6 months after implementation) - */ -export async function migrateSettings(context: vscode.ExtensionContext): Promise { - // Legacy file names that need to be migrated to the new names in GlobalFileNames - const fileMigrations = [ - { oldName: "cline_custom_modes.json", newName: GlobalFileNames.customModes }, - { oldName: "cline_mcp_settings.json", newName: GlobalFileNames.mcpSettings }, - ] - - try { - const settingsDir = path.join(context.globalStorageUri.fsPath, "settings") - - // Check if settings directory exists first - if (!(await fileExistsAtPath(settingsDir))) { - outputChannel.appendLine("No settings directory found, no migrations necessary") - return - } - - // Process each file migration - for (const migration of fileMigrations) { - const oldPath = path.join(settingsDir, migration.oldName) - const newPath = path.join(settingsDir, migration.newName) - - // Only migrate if old file exists and new file doesn't exist yet - // This ensures we don't overwrite any existing new files - const oldFileExists = await fileExistsAtPath(oldPath) - const newFileExists = await fileExistsAtPath(newPath) - - if (oldFileExists && !newFileExists) { - await fs.rename(oldPath, newPath) - outputChannel.appendLine(`Renamed ${migration.oldName} to ${migration.newName}`) - } else { - outputChannel.appendLine( - `Skipping migration of ${migration.oldName} to ${migration.newName}: ${oldFileExists ? "new file already exists" : "old file not found"}`, - ) - } - } - } catch (error) { - outputChannel.appendLine(`Error migrating settings files: ${error}`) - } -} +// migrateSettings function moved to src/utils/migrateSettings.ts // This method is called when your extension is activated. // Your extension is activated the very first time the command is executed. @@ -94,7 +49,7 @@ export async function activate(context: vscode.ExtensionContext) { outputChannel.appendLine("Roo-Code extension activated") // Migrate old settings to new - await migrateSettings(context) + await migrateSettings(context, outputChannel) // Initialize telemetry service after environment variables are loaded. telemetryService.initialize() diff --git a/src/utils/migrateSettings.ts b/src/utils/migrateSettings.ts new file mode 100644 index 00000000000..a4d414b52ff --- /dev/null +++ b/src/utils/migrateSettings.ts @@ -0,0 +1,53 @@ +import * as vscode from "vscode" +import * as path from "path" +import * as fs from "fs/promises" +import { fileExistsAtPath } from "./fs" +import { GlobalFileNames } from "../shared/globalFileNames" + +/** + * Migrates old settings files to new file names + * + * TODO: Remove this migration code in September 2025 (6 months after implementation) + */ +export async function migrateSettings( + context: vscode.ExtensionContext, + outputChannel: vscode.OutputChannel, +): Promise { + // Legacy file names that need to be migrated to the new names in GlobalFileNames + const fileMigrations = [ + { oldName: "cline_custom_modes.json", newName: GlobalFileNames.customModes }, + { oldName: "cline_mcp_settings.json", newName: GlobalFileNames.mcpSettings }, + ] + + try { + const settingsDir = path.join(context.globalStorageUri.fsPath, "settings") + + // Check if settings directory exists first + if (!(await fileExistsAtPath(settingsDir))) { + outputChannel.appendLine("No settings directory found, no migrations necessary") + return + } + + // Process each file migration + for (const migration of fileMigrations) { + const oldPath = path.join(settingsDir, migration.oldName) + const newPath = path.join(settingsDir, migration.newName) + + // Only migrate if old file exists and new file doesn't exist yet + // This ensures we don't overwrite any existing new files + const oldFileExists = await fileExistsAtPath(oldPath) + const newFileExists = await fileExistsAtPath(newPath) + + if (oldFileExists && !newFileExists) { + await fs.rename(oldPath, newPath) + outputChannel.appendLine(`Renamed ${migration.oldName} to ${migration.newName}`) + } else { + outputChannel.appendLine( + `Skipping migration of ${migration.oldName} to ${migration.newName}: ${oldFileExists ? "new file already exists" : "old file not found"}`, + ) + } + } + } catch (error) { + outputChannel.appendLine(`Error migrating settings files: ${error}`) + } +} From a2553e41bb935f67308f2148f34520fc06496f62 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Mon, 24 Mar 2025 13:30:30 -0400 Subject: [PATCH 9/9] Update src/extension.ts --- src/extension.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index a2bece9ec76..a232cb51d08 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -38,8 +38,6 @@ import { formatLanguage } from "./shared/language" let outputChannel: vscode.OutputChannel let extensionContext: vscode.ExtensionContext -// migrateSettings function moved to src/utils/migrateSettings.ts - // This method is called when your extension is activated. // Your extension is activated the very first time the command is executed. export async function activate(context: vscode.ExtensionContext) {