diff --git a/src/core/config/CustomModesManager.ts b/src/core/config/CustomModesManager.ts index 21c2709f90..5ce94b0e38 100644 --- a/src/core/config/CustomModesManager.ts +++ b/src/core/config/CustomModesManager.ts @@ -3,6 +3,7 @@ import * as path from "path" import * as fs from "fs/promises" import * as yaml from "yaml" +import stripBom from "strip-bom" import { type ModeConfig, customModesSettingsSchema } from "@roo-code/types" @@ -11,6 +12,7 @@ import { getWorkspacePath } from "../../utils/path" import { logger } from "../../utils/logging" import { GlobalFileNames } from "../../shared/globalFileNames" import { ensureSettingsDirectoryExists } from "../../utils/globalContext" +import { t } from "../../i18n" const ROOMODES_FILENAME = ".roomodes" @@ -73,12 +75,88 @@ export class CustomModesManager { return exists ? roomodesPath : undefined } + /** + * Regex pattern for problematic characters that need to be cleaned from YAML content + * Includes: + * - \u00A0: Non-breaking space + * - \u200B-\u200D: Zero-width spaces and joiners + * - \u2010-\u2015, \u2212: Various dash characters + * - \u2018-\u2019: Smart single quotes + * - \u201C-\u201D: Smart double quotes + */ + private static readonly PROBLEMATIC_CHARS_REGEX = + // eslint-disable-next-line no-misleading-character-class + /[\u00A0\u200B\u200C\u200D\u2010\u2011\u2012\u2013\u2014\u2015\u2212\u2018\u2019\u201C\u201D]/g + + /** + * Clean invisible and problematic characters from YAML content + */ + private cleanInvisibleCharacters(content: string): string { + // Single pass replacement for all problematic characters + return content.replace(CustomModesManager.PROBLEMATIC_CHARS_REGEX, (match) => { + switch (match) { + case "\u00A0": // Non-breaking space + return " " + case "\u200B": // Zero-width space + case "\u200C": // Zero-width non-joiner + case "\u200D": // Zero-width joiner + return "" + case "\u2018": // Left single quotation mark + case "\u2019": // Right single quotation mark + return "'" + case "\u201C": // Left double quotation mark + case "\u201D": // Right double quotation mark + return '"' + default: // Dash characters (U+2010 through U+2015, U+2212) + return "-" + } + }) + } + + /** + * Parse YAML content with enhanced error handling and preprocessing + */ + private parseYamlSafely(content: string, filePath: string): any { + // Clean the content + let cleanedContent = stripBom(content) + cleanedContent = this.cleanInvisibleCharacters(cleanedContent) + + try { + return yaml.parse(cleanedContent) + } catch (error) { + const errorMsg = error instanceof Error ? error.message : String(error) + console.error(`[CustomModesManager] Failed to parse YAML from ${filePath}:`, errorMsg) + + // Show user-friendly error message for .roomodes files + if (filePath.endsWith(ROOMODES_FILENAME)) { + const lineMatch = errorMsg.match(/at line (\d+)/) + const line = lineMatch ? lineMatch[1] : "unknown" + vscode.window.showErrorMessage(t("common:customModes.errors.yamlParseError", { line })) + } + + // Return empty object to prevent duplicate error handling + return {} + } + } + private async loadModesFromFile(filePath: string): Promise { try { const content = await fs.readFile(filePath, "utf-8") - const settings = yaml.parse(content) + const settings = this.parseYamlSafely(content, filePath) const result = customModesSettingsSchema.safeParse(settings) + if (!result.success) { + console.error(`[CustomModesManager] Schema validation failed for ${filePath}:`, result.error) + + // Show user-friendly error for .roomodes files + if (filePath.endsWith(ROOMODES_FILENAME)) { + const issues = result.error.issues + .map((issue) => `• ${issue.path.join(".")}: ${issue.message}`) + .join("\n") + + vscode.window.showErrorMessage(t("common:customModes.errors.schemaValidationError", { issues })) + } + return [] } @@ -89,8 +167,11 @@ export class CustomModesManager { // Add source to each mode return result.data.customModes.map((mode) => ({ ...mode, source })) } catch (error) { - const errorMsg = `Failed to load modes from ${filePath}: ${error instanceof Error ? error.message : String(error)}` - console.error(`[CustomModesManager] ${errorMsg}`) + // Only log if the error wasn't already handled in parseYamlSafely + if (!(error as any).alreadyHandled) { + const errorMsg = `Failed to load modes from ${filePath}: ${error instanceof Error ? error.message : String(error)}` + console.error(`[CustomModesManager] ${errorMsg}`) + } return [] } } @@ -124,7 +205,12 @@ export class CustomModesManager { const fileExists = await fileExistsAtPath(filePath) if (!fileExists) { - await this.queueWrite(() => fs.writeFile(filePath, yaml.stringify({ customModes: [] }))) + await this.queueWrite(() => + fs.writeFile( + filePath, + yaml.stringify({ customModes: [] }, { lineWidth: 0, defaultStringType: "PLAIN" }), + ), + ) } return filePath @@ -147,13 +233,12 @@ export class CustomModesManager { await this.getCustomModesFilePath() const content = await fs.readFile(settingsPath, "utf-8") - const errorMessage = - "Invalid custom modes format. Please ensure your settings follow the correct YAML format." + const errorMessage = t("common:customModes.errors.invalidFormat") let config: any try { - config = yaml.parse(content) + config = this.parseYamlSafely(content, settingsPath) } catch (error) { console.error(error) vscode.window.showErrorMessage(errorMessage) @@ -284,7 +369,7 @@ export class CustomModesManager { if (!workspaceFolders || workspaceFolders.length === 0) { logger.error("Failed to update project mode: No workspace folder found", { slug }) - throw new Error("No workspace folder found for project-specific mode") + throw new Error(t("common:customModes.errors.noWorkspaceForProject")) } const workspaceRoot = getWorkspacePath() @@ -318,7 +403,7 @@ export class CustomModesManager { } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error) logger.error("Failed to update custom mode", { slug, error: errorMessage }) - vscode.window.showErrorMessage(`Failed to update custom mode: ${errorMessage}`) + vscode.window.showErrorMessage(t("common:customModes.errors.updateFailed", { error: errorMessage })) } } @@ -329,20 +414,20 @@ export class CustomModesManager { content = await fs.readFile(filePath, "utf-8") } catch (error) { // File might not exist yet. - content = yaml.stringify({ customModes: [] }) + content = yaml.stringify({ customModes: [] }, { lineWidth: 0, defaultStringType: "PLAIN" }) } let settings try { - settings = yaml.parse(content) + settings = this.parseYamlSafely(content, filePath) } catch (error) { - console.error(`[CustomModesManager] Failed to parse YAML from ${filePath}:`, error) + // Error already logged in parseYamlSafely settings = { customModes: [] } } settings.customModes = operation(settings.customModes || []) - await fs.writeFile(filePath, yaml.stringify(settings), "utf-8") + await fs.writeFile(filePath, yaml.stringify(settings, { lineWidth: 0, defaultStringType: "PLAIN" }), "utf-8") } private async refreshMergedState(): Promise { @@ -373,7 +458,7 @@ export class CustomModesManager { const globalMode = settingsModes.find((m) => m.slug === slug) if (!projectMode && !globalMode) { - throw new Error("Write error: Mode not found") + throw new Error(t("common:customModes.errors.modeNotFound")) } await this.queueWrite(async () => { @@ -392,23 +477,24 @@ export class CustomModesManager { await this.refreshMergedState() }) } catch (error) { - vscode.window.showErrorMessage( - `Failed to delete custom mode: ${error instanceof Error ? error.message : String(error)}`, - ) + const errorMessage = error instanceof Error ? error.message : String(error) + vscode.window.showErrorMessage(t("common:customModes.errors.deleteFailed", { error: errorMessage })) } } public async resetCustomModes(): Promise { try { const filePath = await this.getCustomModesFilePath() - await fs.writeFile(filePath, yaml.stringify({ customModes: [] })) + await fs.writeFile( + filePath, + yaml.stringify({ customModes: [] }, { lineWidth: 0, defaultStringType: "PLAIN" }), + ) await this.context.globalState.update("customModes", []) this.clearCache() await this.onUpdate() } catch (error) { - vscode.window.showErrorMessage( - `Failed to reset custom modes: ${error instanceof Error ? error.message : String(error)}`, - ) + const errorMessage = error instanceof Error ? error.message : String(error) + vscode.window.showErrorMessage(t("common:customModes.errors.resetFailed", { error: errorMessage })) } } diff --git a/src/core/config/__tests__/CustomModesManager.spec.ts b/src/core/config/__tests__/CustomModesManager.spec.ts index 7791b36ee8..2af801b646 100644 --- a/src/core/config/__tests__/CustomModesManager.spec.ts +++ b/src/core/config/__tests__/CustomModesManager.spec.ts @@ -754,7 +754,7 @@ describe("CustomModesManager", () => { await manager.deleteCustomMode("non-existent-mode") - expect(mockShowError).toHaveBeenCalledWith(expect.stringContaining("Write error")) + expect(mockShowError).toHaveBeenCalledWith("customModes.errors.deleteFailed") }) }) diff --git a/src/core/config/__tests__/CustomModesManager.yamlEdgeCases.spec.ts b/src/core/config/__tests__/CustomModesManager.yamlEdgeCases.spec.ts new file mode 100644 index 0000000000..251a33d211 --- /dev/null +++ b/src/core/config/__tests__/CustomModesManager.yamlEdgeCases.spec.ts @@ -0,0 +1,474 @@ +// npx vitest core/config/__tests__/CustomModesManager.yamlEdgeCases.spec.ts + +import type { Mock } from "vitest" + +import * as path from "path" +import * as fs from "fs/promises" + +import * as yaml from "yaml" +import * as vscode from "vscode" + +import type { ModeConfig } from "@roo-code/types" + +import { fileExistsAtPath } from "../../../utils/fs" +import { getWorkspacePath } from "../../../utils/path" +import { GlobalFileNames } from "../../../shared/globalFileNames" + +import { CustomModesManager } from "../CustomModesManager" + +vi.mock("vscode", () => ({ + workspace: { + workspaceFolders: [], + onDidSaveTextDocument: vi.fn(), + createFileSystemWatcher: vi.fn(), + }, + window: { + showErrorMessage: vi.fn(), + }, +})) + +vi.mock("fs/promises") + +vi.mock("../../../utils/fs") +vi.mock("../../../utils/path") + +describe("CustomModesManager - YAML Edge Cases", () => { + let manager: CustomModesManager + let mockContext: vscode.ExtensionContext + let mockOnUpdate: Mock + let mockWorkspaceFolders: { uri: { fsPath: string } }[] + + const mockStoragePath = `${path.sep}mock${path.sep}settings` + const mockSettingsPath = path.join(mockStoragePath, "settings", GlobalFileNames.customModes) + const mockRoomodes = `${path.sep}mock${path.sep}workspace${path.sep}.roomodes` + + // Helper function to reduce duplication in fs.readFile mocks + const mockFsReadFile = (files: Record) => { + ;(fs.readFile as Mock).mockImplementation(async (path: string) => { + if (files[path]) return files[path] + throw new Error("File not found") + }) + } + + beforeEach(() => { + mockOnUpdate = vi.fn() + mockContext = { + globalState: { + get: vi.fn(), + update: vi.fn(), + keys: vi.fn(() => []), + setKeysForSync: vi.fn(), + }, + globalStorageUri: { + fsPath: mockStoragePath, + }, + } as unknown as vscode.ExtensionContext + + mockWorkspaceFolders = [{ uri: { fsPath: "/mock/workspace" } }] + ;(vscode.workspace as any).workspaceFolders = mockWorkspaceFolders + ;(vscode.workspace.onDidSaveTextDocument as Mock).mockReturnValue({ dispose: vi.fn() }) + ;(getWorkspacePath as Mock).mockReturnValue("/mock/workspace") + ;(fileExistsAtPath as Mock).mockImplementation(async (path: string) => { + return path === mockSettingsPath || path === mockRoomodes + }) + ;(fs.mkdir as Mock).mockResolvedValue(undefined) + ;(fs.readFile as Mock).mockImplementation(async (path: string) => { + if (path === mockSettingsPath) { + return yaml.stringify({ customModes: [] }) + } + throw new Error("File not found") + }) + + // Mock createFileSystemWatcher to prevent file watching in tests + const mockWatcher = { + onDidChange: vi.fn().mockReturnValue({ dispose: vi.fn() }), + onDidCreate: vi.fn().mockReturnValue({ dispose: vi.fn() }), + onDidDelete: vi.fn().mockReturnValue({ dispose: vi.fn() }), + dispose: vi.fn(), + } + ;(vscode.workspace.createFileSystemWatcher as Mock).mockReturnValue(mockWatcher) + + manager = new CustomModesManager(mockContext, mockOnUpdate) + }) + + afterEach(() => { + vi.clearAllMocks() + }) + + describe("BOM (Byte Order Mark) handling", () => { + it("should handle UTF-8 BOM in YAML files", async () => { + const yamlWithBOM = + "\uFEFF" + + yaml.stringify({ + customModes: [ + { + slug: "test-mode", + name: "Test Mode", + roleDefinition: "Test role", + groups: ["read"], + }, + ], + }) + + mockFsReadFile({ + [mockRoomodes]: yamlWithBOM, + [mockSettingsPath]: yaml.stringify({ customModes: [] }), + }) + + const modes = await manager.getCustomModes() + + expect(modes).toHaveLength(1) + expect(modes[0].slug).toBe("test-mode") + expect(modes[0].name).toBe("Test Mode") + }) + + it("should handle UTF-16 BOM in YAML files", async () => { + // When Node.js reads UTF-16 files, the BOM is correctly decoded as \uFEFF + const yamlWithBOM = + "\uFEFF" + + yaml.stringify({ + customModes: [ + { + slug: "utf16-mode", + name: "UTF-16 Mode", + roleDefinition: "Test role", + groups: ["read"], + }, + ], + }) + + mockFsReadFile({ + [mockRoomodes]: yamlWithBOM, + [mockSettingsPath]: yaml.stringify({ customModes: [] }), + }) + + const modes = await manager.getCustomModes() + + expect(modes).toHaveLength(1) + expect(modes[0].slug).toBe("utf16-mode") + }) + }) + + describe("Invisible character handling", () => { + it("should handle non-breaking spaces in YAML", async () => { + // YAML with non-breaking spaces (U+00A0) instead of regular spaces + const yamlWithNonBreakingSpaces = `customModes: + - slug: "test-mode" + name: "Test\u00A0Mode" + roleDefinition: "Test\u00A0role\u00A0with\u00A0non-breaking\u00A0spaces" + groups: ["read"]` + + mockFsReadFile({ + [mockRoomodes]: yamlWithNonBreakingSpaces, + [mockSettingsPath]: yaml.stringify({ customModes: [] }), + }) + + const modes = await manager.getCustomModes() + + expect(modes).toHaveLength(1) + expect(modes[0].name).toBe("Test Mode") // Non-breaking spaces replaced with regular spaces + expect(modes[0].roleDefinition).toBe("Test role with non-breaking spaces") + }) + + it("should handle zero-width characters", async () => { + // YAML with zero-width characters + const yamlWithZeroWidth = `customModes: + - slug: "test-mode" + name: "Test\u200BMode\u200C" + roleDefinition: "Test\u200Drole" + groups: ["read"]` + + mockFsReadFile({ + [mockRoomodes]: yamlWithZeroWidth, + [mockSettingsPath]: yaml.stringify({ customModes: [] }), + }) + + const modes = await manager.getCustomModes() + + expect(modes).toHaveLength(1) + expect(modes[0].name).toBe("TestMode") // Zero-width characters removed + expect(modes[0].roleDefinition).toBe("Testrole") + }) + + it("should normalize various quote characters", async () => { + // Use fancy quotes that will be normalized before YAML parsing + // The fancy quotes will be normalized to standard quotes + const yamlWithFancyQuotes = yaml.stringify({ + customModes: [ + { + slug: "test-mode", + name: "Test Mode", + roleDefinition: "Test role with \u2018fancy\u2019 quotes and \u201Ccurly\u201D quotes", + groups: ["read"], + }, + ], + }) + + mockFsReadFile({ + [mockRoomodes]: yamlWithFancyQuotes, + [mockSettingsPath]: yaml.stringify({ customModes: [] }), + }) + + const modes = await manager.getCustomModes() + + expect(modes).toHaveLength(1) + expect(modes[0].roleDefinition).toBe("Test role with 'fancy' quotes and \"curly\" quotes") + }) + }) + + // Note: YAML anchor/alias support has been removed to reduce complexity + // If needed in the future, users should pre-process their YAML files + + describe("Complex fileRegex handling", () => { + it("should handle complex fileRegex syntax gracefully", async () => { + const yamlWithComplexFileRegex = yaml.stringify({ + customModes: [ + { + slug: "test-mode", + name: "Test Mode", + roleDefinition: "Test role", + groups: [ + "read", + ["edit", { fileRegex: "\\.md$", description: "Markdown files only" }], + "browser", + ], + }, + ], + }) + + mockFsReadFile({ + [mockRoomodes]: yamlWithComplexFileRegex, + [mockSettingsPath]: yaml.stringify({ customModes: [] }), + }) + + const modes = await manager.getCustomModes() + + // Should successfully parse the complex fileRegex syntax + expect(modes).toHaveLength(1) + expect(modes[0].groups).toHaveLength(3) + expect(modes[0].groups[1]).toEqual(["edit", { fileRegex: "\\.md$", description: "Markdown files only" }]) + }) + + it("should handle invalid fileRegex syntax with clear error", async () => { + // This YAML has invalid structure that might cause parsing issues + const invalidYaml = `customModes: + - slug: "test-mode" + name: "Test Mode" + roleDefinition: "Test role" + groups: + - read + - ["edit", { fileRegex: "\\.md$" }] # This line has invalid YAML syntax + - browser` + + mockFsReadFile({ + [mockRoomodes]: invalidYaml, + [mockSettingsPath]: yaml.stringify({ customModes: [] }), + }) + + const modes = await manager.getCustomModes() + + // Should handle the error gracefully + expect(modes).toHaveLength(0) + expect(vscode.window.showErrorMessage).toHaveBeenCalledWith("customModes.errors.yamlParseError") + }) + }) + + describe("Error messages", () => { + it("should provide detailed syntax error messages with context", async () => { + const invalidYaml = `customModes: + - slug: "test-mode" + name: "Test Mode" + roleDefinition: "Test role + groups: ["read"]` // Missing closing quote + + mockFsReadFile({ + [mockRoomodes]: invalidYaml, + [mockSettingsPath]: yaml.stringify({ customModes: [] }), + }) + + const modes = await manager.getCustomModes() + + // Should fallback to empty array and show detailed error + expect(modes).toHaveLength(0) + expect(vscode.window.showErrorMessage).toHaveBeenCalledWith("customModes.errors.yamlParseError") + }) + + it("should provide schema validation error messages", async () => { + const invalidSchema = yaml.stringify({ + customModes: [ + { + slug: "test-mode", + name: "Test Mode", + // Missing required 'roleDefinition' field + groups: ["read"], + }, + ], + }) + + mockFsReadFile({ + [mockRoomodes]: invalidSchema, + [mockSettingsPath]: yaml.stringify({ customModes: [] }), + }) + + const modes = await manager.getCustomModes() + + // Should show schema validation error + expect(modes).toHaveLength(0) + expect(vscode.window.showErrorMessage).toHaveBeenCalledWith("customModes.errors.schemaValidationError") + }) + }) + + describe("UTF-8 encoding", () => { + it("should handle special characters and emojis", async () => { + const yamlWithEmojis = yaml.stringify({ + customModes: [ + { + slug: "emoji-mode", + name: "📝 Writing Mode", + roleDefinition: "A mode for writing with emojis 🚀", + groups: ["read", "edit"], + }, + ], + }) + + mockFsReadFile({ + [mockRoomodes]: yamlWithEmojis, + [mockSettingsPath]: yaml.stringify({ customModes: [] }), + }) + + const modes = await manager.getCustomModes() + + expect(modes).toHaveLength(1) + expect(modes[0].name).toBe("📝 Writing Mode") + expect(modes[0].roleDefinition).toBe("A mode for writing with emojis 🚀") + }) + + it("should handle various international characters", async () => { + const yamlWithInternational = yaml.stringify({ + customModes: [ + { + slug: "intl-mode", + name: "Mode Français", + roleDefinition: "Mode für Deutsch, 日本語モード, Режим русский", + groups: ["read"], + }, + ], + }) + + mockFsReadFile({ + [mockRoomodes]: yamlWithInternational, + [mockSettingsPath]: yaml.stringify({ customModes: [] }), + }) + + const modes = await manager.getCustomModes() + + expect(modes).toHaveLength(1) + expect(modes[0].roleDefinition).toContain("für Deutsch") + expect(modes[0].roleDefinition).toContain("日本語モード") + expect(modes[0].roleDefinition).toContain("Режим русский") + }) + }) + + describe("Additional edge cases", () => { + it("should handle mixed line endings (CRLF vs LF)", async () => { + // YAML with mixed line endings + const yamlWithMixedLineEndings = + "customModes:\r\n" + + ' - slug: "test-mode"\n' + + ' name: "Test Mode"\r\n' + + ' roleDefinition: "Test role with mixed line endings"\n' + + ' groups: ["read"]' + + mockFsReadFile({ + [mockRoomodes]: yamlWithMixedLineEndings, + [mockSettingsPath]: yaml.stringify({ customModes: [] }), + }) + + const modes = await manager.getCustomModes() + + expect(modes).toHaveLength(1) + expect(modes[0].slug).toBe("test-mode") + expect(modes[0].roleDefinition).toBe("Test role with mixed line endings") + }) + + it("should handle multiple BOMs in sequence", async () => { + // File with multiple BOMs (edge case from file concatenation) + const yamlWithMultipleBOMs = + "\uFEFF\uFEFF" + + yaml.stringify({ + customModes: [ + { + slug: "multi-bom-mode", + name: "Multi BOM Mode", + roleDefinition: "Test role", + groups: ["read"], + }, + ], + }) + + mockFsReadFile({ + [mockRoomodes]: yamlWithMultipleBOMs, + [mockSettingsPath]: yaml.stringify({ customModes: [] }), + }) + + const modes = await manager.getCustomModes() + + expect(modes).toHaveLength(1) + expect(modes[0].slug).toBe("multi-bom-mode") + }) + + it("should handle deeply nested structures with edge case characters", async () => { + const yamlWithComplexNesting = yaml.stringify({ + customModes: [ + { + slug: "complex-mode", + name: "Complex\u00A0Mode\u2019s Name", + roleDefinition: "Complex role with \u201Cquotes\u201D and \u2014dashes\u2014", + groups: [ + "read", + [ + "edit", + { + fileRegex: "\\.md$", + description: "Markdown files with \u2018special\u2019 chars", + }, + ], + [ + "browser", + { + fileRegex: "\\.html?$", + description: "HTML files\u00A0only", + }, + ], + ], + }, + ], + }) + + mockFsReadFile({ + [mockRoomodes]: yamlWithComplexNesting, + [mockSettingsPath]: yaml.stringify({ customModes: [] }), + }) + + const modes = await manager.getCustomModes() + + expect(modes).toHaveLength(1) + expect(modes[0].name).toBe("Complex Mode's Name") + expect(modes[0].roleDefinition).toBe('Complex role with "quotes" and -dashes-') + expect(modes[0].groups[1]).toEqual([ + "edit", + { + fileRegex: "\\.md$", + description: "Markdown files with 'special' chars", + }, + ]) + expect(modes[0].groups[2]).toEqual([ + "browser", + { + fileRegex: "\\.html?$", + description: "HTML files only", + }, + ]) + }) + }) +}) diff --git a/src/i18n/locales/ca/common.json b/src/i18n/locales/ca/common.json index 04733b0cb6..8e9f07c100 100644 --- a/src/i18n/locales/ca/common.json +++ b/src/i18n/locales/ca/common.json @@ -122,6 +122,18 @@ } } }, + "customModes": { + "errors": { + "yamlParseError": "YAML no vàlid al fitxer .roomodes a la línia {{line}}. Comprova:\n• Indentació correcta (utilitza espais, no tabuladors)\n• Cometes i claudàtors coincidents\n• Sintaxi YAML vàlida", + "schemaValidationError": "Format de modes personalitzats no vàlid a .roomodes:\n{{issues}}", + "invalidFormat": "Format de modes personalitzats no vàlid. Assegura't que la teva configuració segueix el format YAML correcte.", + "updateFailed": "Error en actualitzar el mode personalitzat: {{error}}", + "deleteFailed": "Error en eliminar el mode personalitzat: {{error}}", + "resetFailed": "Error en restablir els modes personalitzats: {{error}}", + "modeNotFound": "Error d'escriptura: Mode no trobat", + "noWorkspaceForProject": "No s'ha trobat cap carpeta d'espai de treball per al mode específic del projecte" + } + }, "mdm": { "errors": { "cloud_auth_required": "La teva organització requereix autenticació de Roo Code Cloud. Si us plau, inicia sessió per continuar.", diff --git a/src/i18n/locales/de/common.json b/src/i18n/locales/de/common.json index cf9f363c57..fc4ce25de1 100644 --- a/src/i18n/locales/de/common.json +++ b/src/i18n/locales/de/common.json @@ -122,6 +122,18 @@ } } }, + "customModes": { + "errors": { + "yamlParseError": "Ungültiges YAML in .roomodes-Datei in Zeile {{line}}. Bitte überprüfe:\n• Korrekte Einrückung (verwende Leerzeichen, keine Tabs)\n• Passende Anführungszeichen und Klammern\n• Gültige YAML-Syntax", + "schemaValidationError": "Ungültiges Format für benutzerdefinierte Modi in .roomodes:\n{{issues}}", + "invalidFormat": "Ungültiges Format für benutzerdefinierte Modi. Bitte stelle sicher, dass deine Einstellungen dem korrekten YAML-Format folgen.", + "updateFailed": "Fehler beim Aktualisieren des benutzerdefinierten Modus: {{error}}", + "deleteFailed": "Fehler beim Löschen des benutzerdefinierten Modus: {{error}}", + "resetFailed": "Fehler beim Zurücksetzen der benutzerdefinierten Modi: {{error}}", + "modeNotFound": "Schreibfehler: Modus nicht gefunden", + "noWorkspaceForProject": "Kein Arbeitsbereich-Ordner für projektspezifischen Modus gefunden" + } + }, "mdm": { "errors": { "cloud_auth_required": "Deine Organisation erfordert eine Roo Code Cloud-Authentifizierung. Bitte melde dich an, um fortzufahren.", diff --git a/src/i18n/locales/en/common.json b/src/i18n/locales/en/common.json index 63468914ee..3f19a1dd50 100644 --- a/src/i18n/locales/en/common.json +++ b/src/i18n/locales/en/common.json @@ -111,6 +111,18 @@ "task_prompt": "What should Roo do?", "task_placeholder": "Type your task here" }, + "customModes": { + "errors": { + "yamlParseError": "Invalid YAML in .roomodes file at line {{line}}. Please check for:\n• Proper indentation (use spaces, not tabs)\n• Matching quotes and brackets\n• Valid YAML syntax", + "schemaValidationError": "Invalid custom modes format in .roomodes:\n{{issues}}", + "invalidFormat": "Invalid custom modes format. Please ensure your settings follow the correct YAML format.", + "updateFailed": "Failed to update custom mode: {{error}}", + "deleteFailed": "Failed to delete custom mode: {{error}}", + "resetFailed": "Failed to reset custom modes: {{error}}", + "modeNotFound": "Write error: Mode not found", + "noWorkspaceForProject": "No workspace folder found for project-specific mode" + } + }, "mdm": { "errors": { "cloud_auth_required": "Your organization requires Roo Code Cloud authentication. Please sign in to continue.", diff --git a/src/i18n/locales/es/common.json b/src/i18n/locales/es/common.json index 97076b442c..4b3177619e 100644 --- a/src/i18n/locales/es/common.json +++ b/src/i18n/locales/es/common.json @@ -122,6 +122,18 @@ } } }, + "customModes": { + "errors": { + "yamlParseError": "YAML inválido en archivo .roomodes en línea {{line}}. Verifica:\n• Indentación correcta (usa espacios, no tabs)\n• Comillas y corchetes coincidentes\n• Sintaxis YAML válida", + "schemaValidationError": "Formato inválido de modos personalizados en .roomodes:\n{{issues}}", + "invalidFormat": "Formato inválido de modos personalizados. Asegúrate de que tu configuración siga el formato YAML correcto.", + "updateFailed": "Error al actualizar modo personalizado: {{error}}", + "deleteFailed": "Error al eliminar modo personalizado: {{error}}", + "resetFailed": "Error al restablecer modos personalizados: {{error}}", + "modeNotFound": "Error de escritura: Modo no encontrado", + "noWorkspaceForProject": "No se encontró carpeta de espacio de trabajo para modo específico del proyecto" + } + }, "mdm": { "errors": { "cloud_auth_required": "Tu organización requiere autenticación de Roo Code Cloud. Por favor, inicia sesión para continuar.", diff --git a/src/i18n/locales/fr/common.json b/src/i18n/locales/fr/common.json index c9531d07c3..93cd67ca15 100644 --- a/src/i18n/locales/fr/common.json +++ b/src/i18n/locales/fr/common.json @@ -122,6 +122,18 @@ } } }, + "customModes": { + "errors": { + "yamlParseError": "YAML invalide dans le fichier .roomodes à la ligne {{line}}. Vérifie :\n• L'indentation correcte (utilise des espaces, pas de tabulations)\n• Les guillemets et crochets correspondants\n• La syntaxe YAML valide", + "schemaValidationError": "Format invalide des modes personnalisés dans .roomodes :\n{{issues}}", + "invalidFormat": "Format invalide des modes personnalisés. Assure-toi que tes paramètres suivent le format YAML correct.", + "updateFailed": "Échec de la mise à jour du mode personnalisé : {{error}}", + "deleteFailed": "Échec de la suppression du mode personnalisé : {{error}}", + "resetFailed": "Échec de la réinitialisation des modes personnalisés : {{error}}", + "modeNotFound": "Erreur d'écriture : Mode non trouvé", + "noWorkspaceForProject": "Aucun dossier d'espace de travail trouvé pour le mode spécifique au projet" + } + }, "mdm": { "errors": { "cloud_auth_required": "Votre organisation nécessite une authentification Roo Code Cloud. Veuillez vous connecter pour continuer.", diff --git a/src/i18n/locales/hi/common.json b/src/i18n/locales/hi/common.json index 20e0c1f518..68788beacc 100644 --- a/src/i18n/locales/hi/common.json +++ b/src/i18n/locales/hi/common.json @@ -122,6 +122,18 @@ } } }, + "customModes": { + "errors": { + "yamlParseError": ".roomodes फ़ाइल में लाइन {{line}} पर अमान्य YAML। कृपया जांचें:\n• सही इंडेंटेशन (टैब नहीं, स्पेस का उपयोग करें)\n• मैचिंग कोट्स और ब्रैकेट्स\n• वैध YAML सिंटैक्स", + "schemaValidationError": ".roomodes में अमान्य कस्टम मोड फॉर्मेट:\n{{issues}}", + "invalidFormat": "अमान्य कस्टम मोड फॉर्मेट। कृपया सुनिश्चित करें कि आपकी सेटिंग्स सही YAML फॉर्मेट का पालन करती हैं।", + "updateFailed": "कस्टम मोड अपडेट विफल: {{error}}", + "deleteFailed": "कस्टम मोड डिलीट विफल: {{error}}", + "resetFailed": "कस्टम मोड रीसेट विफल: {{error}}", + "modeNotFound": "लेखन त्रुटि: मोड नहीं मिला", + "noWorkspaceForProject": "प्रोजेक्ट-विशिष्ट मोड के लिए वर्कस्पेस फ़ोल्डर नहीं मिला" + } + }, "mdm": { "errors": { "cloud_auth_required": "आपके संगठन को Roo Code Cloud प्रमाणीकरण की आवश्यकता है। कृपया जारी रखने के लिए साइन इन करें।", diff --git a/src/i18n/locales/id/common.json b/src/i18n/locales/id/common.json index 319c3f9674..d6bb2ffa98 100644 --- a/src/i18n/locales/id/common.json +++ b/src/i18n/locales/id/common.json @@ -122,6 +122,18 @@ } } }, + "customModes": { + "errors": { + "yamlParseError": "YAML tidak valid dalam file .roomodes pada baris {{line}}. Silakan periksa:\n• Indentasi yang benar (gunakan spasi, bukan tab)\n• Tanda kutip dan kurung yang cocok\n• Sintaks YAML yang valid", + "schemaValidationError": "Format mode kustom tidak valid dalam .roomodes:\n{{issues}}", + "invalidFormat": "Format mode kustom tidak valid. Pastikan pengaturan kamu mengikuti format YAML yang benar.", + "updateFailed": "Gagal memperbarui mode kustom: {{error}}", + "deleteFailed": "Gagal menghapus mode kustom: {{error}}", + "resetFailed": "Gagal mereset mode kustom: {{error}}", + "modeNotFound": "Kesalahan tulis: Mode tidak ditemukan", + "noWorkspaceForProject": "Tidak ditemukan folder workspace untuk mode khusus proyek" + } + }, "mdm": { "errors": { "cloud_auth_required": "Organisasi kamu memerlukan autentikasi Roo Code Cloud. Silakan masuk untuk melanjutkan.", diff --git a/src/i18n/locales/it/common.json b/src/i18n/locales/it/common.json index ea919162e4..0ef6ee5b54 100644 --- a/src/i18n/locales/it/common.json +++ b/src/i18n/locales/it/common.json @@ -122,6 +122,18 @@ } } }, + "customModes": { + "errors": { + "yamlParseError": "YAML non valido nel file .roomodes alla riga {{line}}. Controlla:\n• Indentazione corretta (usa spazi, non tab)\n• Virgolette e parentesi corrispondenti\n• Sintassi YAML valida", + "schemaValidationError": "Formato modalità personalizzate non valido in .roomodes:\n{{issues}}", + "invalidFormat": "Formato modalità personalizzate non valido. Assicurati che le tue impostazioni seguano il formato YAML corretto.", + "updateFailed": "Aggiornamento modalità personalizzata fallito: {{error}}", + "deleteFailed": "Eliminazione modalità personalizzata fallita: {{error}}", + "resetFailed": "Reset modalità personalizzate fallito: {{error}}", + "modeNotFound": "Errore di scrittura: Modalità non trovata", + "noWorkspaceForProject": "Nessuna cartella workspace trovata per la modalità specifica del progetto" + } + }, "mdm": { "errors": { "cloud_auth_required": "La tua organizzazione richiede l'autenticazione Roo Code Cloud. Accedi per continuare.", diff --git a/src/i18n/locales/ja/common.json b/src/i18n/locales/ja/common.json index 916f9b2f45..b132470eac 100644 --- a/src/i18n/locales/ja/common.json +++ b/src/i18n/locales/ja/common.json @@ -122,6 +122,18 @@ } } }, + "customModes": { + "errors": { + "yamlParseError": ".roomodes ファイルの {{line}} 行目で無効な YAML です。以下を確認してください:\n• 正しいインデント(タブではなくスペースを使用)\n• 引用符と括弧の対応\n• 有効な YAML 構文", + "schemaValidationError": ".roomodes のカスタムモード形式が無効です:\n{{issues}}", + "invalidFormat": "カスタムモード形式が無効です。設定が正しい YAML 形式に従っていることを確認してください。", + "updateFailed": "カスタムモードの更新に失敗しました:{{error}}", + "deleteFailed": "カスタムモードの削除に失敗しました:{{error}}", + "resetFailed": "カスタムモードのリセットに失敗しました:{{error}}", + "modeNotFound": "書き込みエラー:モードが見つかりません", + "noWorkspaceForProject": "プロジェクト固有モード用のワークスペースフォルダーが見つかりません" + } + }, "mdm": { "errors": { "cloud_auth_required": "あなたの組織では Roo Code Cloud 認証が必要です。続行するにはサインインしてください。", diff --git a/src/i18n/locales/ko/common.json b/src/i18n/locales/ko/common.json index bad3facfd6..079bb56a48 100644 --- a/src/i18n/locales/ko/common.json +++ b/src/i18n/locales/ko/common.json @@ -122,6 +122,18 @@ } } }, + "customModes": { + "errors": { + "yamlParseError": ".roomodes 파일의 {{line}}번째 줄에서 유효하지 않은 YAML입니다. 다음을 확인하세요:\n• 올바른 들여쓰기 (탭이 아닌 공백 사용)\n• 일치하는 따옴표와 괄호\n• 유효한 YAML 구문", + "schemaValidationError": ".roomodes의 사용자 정의 모드 형식이 유효하지 않습니다:\n{{issues}}", + "invalidFormat": "사용자 정의 모드 형식이 유효하지 않습니다. 설정이 올바른 YAML 형식을 따르는지 확인하세요.", + "updateFailed": "사용자 정의 모드 업데이트 실패: {{error}}", + "deleteFailed": "사용자 정의 모드 삭제 실패: {{error}}", + "resetFailed": "사용자 정의 모드 재설정 실패: {{error}}", + "modeNotFound": "쓰기 오류: 모드를 찾을 수 없습니다", + "noWorkspaceForProject": "프로젝트별 모드용 작업 공간 폴더를 찾을 수 없습니다" + } + }, "mdm": { "errors": { "cloud_auth_required": "조직에서 Roo Code Cloud 인증이 필요합니다. 계속하려면 로그인하세요.", diff --git a/src/i18n/locales/nl/common.json b/src/i18n/locales/nl/common.json index a90ff3d00f..ef27bef3d8 100644 --- a/src/i18n/locales/nl/common.json +++ b/src/i18n/locales/nl/common.json @@ -122,6 +122,18 @@ } } }, + "customModes": { + "errors": { + "yamlParseError": "Ongeldige YAML in .roomodes bestand op regel {{line}}. Controleer:\n• Juiste inspringing (gebruik spaties, geen tabs)\n• Overeenkomende aanhalingstekens en haakjes\n• Geldige YAML syntaxis", + "schemaValidationError": "Ongeldig aangepaste modi formaat in .roomodes:\n{{issues}}", + "invalidFormat": "Ongeldig aangepaste modi formaat. Zorg ervoor dat je instellingen het juiste YAML formaat volgen.", + "updateFailed": "Aangepaste modus bijwerken mislukt: {{error}}", + "deleteFailed": "Aangepaste modus verwijderen mislukt: {{error}}", + "resetFailed": "Aangepaste modi resetten mislukt: {{error}}", + "modeNotFound": "Schrijffout: Modus niet gevonden", + "noWorkspaceForProject": "Geen workspace map gevonden voor projectspecifieke modus" + } + }, "mdm": { "errors": { "cloud_auth_required": "Je organisatie vereist Roo Code Cloud-authenticatie. Log in om door te gaan.", diff --git a/src/i18n/locales/pl/common.json b/src/i18n/locales/pl/common.json index d6cb5cb809..777bbd82b7 100644 --- a/src/i18n/locales/pl/common.json +++ b/src/i18n/locales/pl/common.json @@ -122,6 +122,18 @@ } } }, + "customModes": { + "errors": { + "yamlParseError": "Nieprawidłowy YAML w pliku .roomodes w linii {{line}}. Sprawdź:\n• Prawidłowe wcięcia (używaj spacji, nie tabulatorów)\n• Pasujące cudzysłowy i nawiasy\n• Prawidłową składnię YAML", + "schemaValidationError": "Nieprawidłowy format trybów niestandardowych w .roomodes:\n{{issues}}", + "invalidFormat": "Nieprawidłowy format trybów niestandardowych. Upewnij się, że twoje ustawienia są zgodne z prawidłowym formatem YAML.", + "updateFailed": "Aktualizacja trybu niestandardowego nie powiodła się: {{error}}", + "deleteFailed": "Usunięcie trybu niestandardowego nie powiodło się: {{error}}", + "resetFailed": "Resetowanie trybów niestandardowych nie powiodło się: {{error}}", + "modeNotFound": "Błąd zapisu: Tryb nie został znaleziony", + "noWorkspaceForProject": "Nie znaleziono folderu obszaru roboczego dla trybu specyficznego dla projektu" + } + }, "mdm": { "errors": { "cloud_auth_required": "Twoja organizacja wymaga uwierzytelnienia Roo Code Cloud. Zaloguj się, aby kontynuować.", diff --git a/src/i18n/locales/pt-BR/common.json b/src/i18n/locales/pt-BR/common.json index 941dd0f86d..18695588c8 100644 --- a/src/i18n/locales/pt-BR/common.json +++ b/src/i18n/locales/pt-BR/common.json @@ -122,6 +122,18 @@ } } }, + "customModes": { + "errors": { + "yamlParseError": "YAML inválido no arquivo .roomodes na linha {{line}}. Verifique:\n• Indentação correta (use espaços, não tabs)\n• Aspas e colchetes correspondentes\n• Sintaxe YAML válida", + "schemaValidationError": "Formato de modos personalizados inválido em .roomodes:\n{{issues}}", + "invalidFormat": "Formato de modos personalizados inválido. Certifique-se de que suas configurações seguem o formato YAML correto.", + "updateFailed": "Falha ao atualizar modo personalizado: {{error}}", + "deleteFailed": "Falha ao excluir modo personalizado: {{error}}", + "resetFailed": "Falha ao redefinir modos personalizados: {{error}}", + "modeNotFound": "Erro de escrita: Modo não encontrado", + "noWorkspaceForProject": "Nenhuma pasta de workspace encontrada para modo específico do projeto" + } + }, "mdm": { "errors": { "cloud_auth_required": "Sua organização requer autenticação do Roo Code Cloud. Faça login para continuar.", diff --git a/src/i18n/locales/ru/common.json b/src/i18n/locales/ru/common.json index 646d5d3b6b..b0e4f58ceb 100644 --- a/src/i18n/locales/ru/common.json +++ b/src/i18n/locales/ru/common.json @@ -122,6 +122,18 @@ } } }, + "customModes": { + "errors": { + "yamlParseError": "Недопустимый YAML в файле .roomodes на строке {{line}}. Проверь:\n• Правильные отступы (используй пробелы, не табы)\n• Соответствующие кавычки и скобки\n• Допустимый синтаксис YAML", + "schemaValidationError": "Недопустимый формат пользовательских режимов в .roomodes:\n{{issues}}", + "invalidFormat": "Недопустимый формат пользовательских режимов. Убедись, что твои настройки соответствуют правильному формату YAML.", + "updateFailed": "Не удалось обновить пользовательский режим: {{error}}", + "deleteFailed": "Не удалось удалить пользовательский режим: {{error}}", + "resetFailed": "Не удалось сбросить пользовательские режимы: {{error}}", + "modeNotFound": "Ошибка записи: Режим не найден", + "noWorkspaceForProject": "Не найдена папка рабочего пространства для режима, специфичного для проекта" + } + }, "mdm": { "errors": { "cloud_auth_required": "Ваша организация требует аутентификации Roo Code Cloud. Войдите в систему, чтобы продолжить.", diff --git a/src/i18n/locales/tr/common.json b/src/i18n/locales/tr/common.json index 368934fdcc..9663cac808 100644 --- a/src/i18n/locales/tr/common.json +++ b/src/i18n/locales/tr/common.json @@ -122,6 +122,18 @@ } } }, + "customModes": { + "errors": { + "yamlParseError": ".roomodes dosyasının {{line}}. satırında geçersiz YAML. Kontrol et:\n• Doğru girinti (tab değil boşluk kullan)\n• Eşleşen tırnak işaretleri ve parantezler\n• Geçerli YAML sözdizimi", + "schemaValidationError": ".roomodes'ta geçersiz özel mod formatı:\n{{issues}}", + "invalidFormat": "Geçersiz özel mod formatı. Ayarlarının doğru YAML formatını takip ettiğinden emin ol.", + "updateFailed": "Özel mod güncellemesi başarısız: {{error}}", + "deleteFailed": "Özel mod silme başarısız: {{error}}", + "resetFailed": "Özel modları sıfırlama başarısız: {{error}}", + "modeNotFound": "Yazma hatası: Mod bulunamadı", + "noWorkspaceForProject": "Proje özel modu için çalışma alanı klasörü bulunamadı" + } + }, "mdm": { "errors": { "cloud_auth_required": "Kuruluşunuz Roo Code Cloud kimlik doğrulaması gerektiriyor. Devam etmek için giriş yapın.", diff --git a/src/i18n/locales/vi/common.json b/src/i18n/locales/vi/common.json index 9d43acc45a..5a9d0983f8 100644 --- a/src/i18n/locales/vi/common.json +++ b/src/i18n/locales/vi/common.json @@ -122,6 +122,18 @@ } } }, + "customModes": { + "errors": { + "yamlParseError": "YAML không hợp lệ trong tệp .roomodes tại dòng {{line}}. Vui lòng kiểm tra:\n• Thụt lề đúng (dùng dấu cách, không dùng tab)\n• Dấu ngoặc kép và ngoặc đơn khớp nhau\n• Cú pháp YAML hợp lệ", + "schemaValidationError": "Định dạng chế độ tùy chỉnh không hợp lệ trong .roomodes:\n{{issues}}", + "invalidFormat": "Định dạng chế độ tùy chỉnh không hợp lệ. Vui lòng đảm bảo cài đặt của bạn tuân theo định dạng YAML đúng.", + "updateFailed": "Cập nhật chế độ tùy chỉnh thất bại: {{error}}", + "deleteFailed": "Xóa chế độ tùy chỉnh thất bại: {{error}}", + "resetFailed": "Đặt lại chế độ tùy chỉnh thất bại: {{error}}", + "modeNotFound": "Lỗi ghi: Không tìm thấy chế độ", + "noWorkspaceForProject": "Không tìm thấy thư mục workspace cho chế độ dành riêng cho dự án" + } + }, "mdm": { "errors": { "cloud_auth_required": "Tổ chức của bạn yêu cầu xác thực Roo Code Cloud. Vui lòng đăng nhập để tiếp tục.", diff --git a/src/i18n/locales/zh-CN/common.json b/src/i18n/locales/zh-CN/common.json index d21c9ef9b7..b355c2ec35 100644 --- a/src/i18n/locales/zh-CN/common.json +++ b/src/i18n/locales/zh-CN/common.json @@ -127,6 +127,18 @@ } } }, + "customModes": { + "errors": { + "yamlParseError": ".roomodes 文件第 {{line}} 行 YAML 格式无效。请检查:\n• 正确的缩进(使用空格,不要使用制表符)\n• 匹配的引号和括号\n• 有效的 YAML 语法", + "schemaValidationError": ".roomodes 中自定义模式格式无效:\n{{issues}}", + "invalidFormat": "自定义模式格式无效。请确保你的设置遵循正确的 YAML 格式。", + "updateFailed": "更新自定义模式失败:{{error}}", + "deleteFailed": "删除自定义模式失败:{{error}}", + "resetFailed": "重置自定义模式失败:{{error}}", + "modeNotFound": "写入错误:未找到模式", + "noWorkspaceForProject": "未找到项目特定模式的工作区文件夹" + } + }, "mdm": { "errors": { "cloud_auth_required": "您的组织需要 Roo Code Cloud 身份验证。请登录以继续。", diff --git a/src/i18n/locales/zh-TW/common.json b/src/i18n/locales/zh-TW/common.json index 164df43cc8..6c1c9747aa 100644 --- a/src/i18n/locales/zh-TW/common.json +++ b/src/i18n/locales/zh-TW/common.json @@ -122,6 +122,18 @@ } } }, + "customModes": { + "errors": { + "yamlParseError": ".roomodes 檔案第 {{line}} 行 YAML 格式無效。請檢查:\n• 正確的縮排(使用空格,不要使用定位字元)\n• 匹配的引號和括號\n• 有效的 YAML 語法", + "schemaValidationError": ".roomodes 中自訂模式格式無效:\n{{issues}}", + "invalidFormat": "自訂模式格式無效。請確保你的設定遵循正確的 YAML 格式。", + "updateFailed": "更新自訂模式失敗:{{error}}", + "deleteFailed": "刪除自訂模式失敗:{{error}}", + "resetFailed": "重設自訂模式失敗:{{error}}", + "modeNotFound": "寫入錯誤:未找到模式", + "noWorkspaceForProject": "未找到專案特定模式的工作區資料夾" + } + }, "mdm": { "errors": { "cloud_auth_required": "您的組織需要 Roo Code Cloud 身份驗證。請登入以繼續。", diff --git a/src/services/marketplace/SimpleInstaller.ts b/src/services/marketplace/SimpleInstaller.ts index 75f14b0d4c..7ead5e2442 100644 --- a/src/services/marketplace/SimpleInstaller.ts +++ b/src/services/marketplace/SimpleInstaller.ts @@ -84,7 +84,7 @@ export class SimpleInstaller { // Write back to file await fs.mkdir(path.dirname(filePath), { recursive: true }) - const yamlContent = yaml.stringify(existingData) + const yamlContent = yaml.stringify(existingData, { lineWidth: 0, defaultStringType: "PLAIN" }) await fs.writeFile(filePath, yamlContent, "utf-8") // Calculate approximate line number where the new mode was added @@ -282,7 +282,11 @@ export class SimpleInstaller { existingData.customModes = existingData.customModes.filter((mode: any) => mode.slug !== modeData.slug) // Always write back the file, even if empty - await fs.writeFile(filePath, yaml.stringify(existingData), "utf-8") + await fs.writeFile( + filePath, + yaml.stringify(existingData, { lineWidth: 0, defaultStringType: "PLAIN" }), + "utf-8", + ) } } catch (error: any) { if (error.code === "ENOENT") { diff --git a/src/utils/migrateSettings.ts b/src/utils/migrateSettings.ts index 406e5bd051..3007a6bcfb 100644 --- a/src/utils/migrateSettings.ts +++ b/src/utils/migrateSettings.ts @@ -92,8 +92,8 @@ async function migrateCustomModesToYaml(settingsDir: string, outputChannel: vsco // Parse JSON to object (using the yaml library just to be safe/consistent) const customModesData = yaml.parse(jsonContent) - // Convert to YAML - const yamlContent = yaml.stringify(customModesData) + // Convert to YAML with no line width limit to prevent line breaks + const yamlContent = yaml.stringify(customModesData, { lineWidth: 0, defaultStringType: "PLAIN" }) // Write YAML file await fs.writeFile(newYamlPath, yamlContent, "utf-8")