From 4137c023de0d8f875b6376119e81b2740b567197 Mon Sep 17 00:00:00 2001 From: richardwhiteii <2885312+richardwhiteii@users.noreply.github.com> Date: Mon, 10 Feb 2025 23:51:03 -0500 Subject: [PATCH 01/10] Create simpleissue.yaml --- .github/ISSUE_TEMPLATE/simpleissue.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/simpleissue.yaml diff --git a/.github/ISSUE_TEMPLATE/simpleissue.yaml b/.github/ISSUE_TEMPLATE/simpleissue.yaml new file mode 100644 index 00000000000..6bea4c29715 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/simpleissue.yaml @@ -0,0 +1,14 @@ +name: Fork Issue +description: Create an issue to track changes made in this fork +title: "[Fork] " +labels: [] +assignees: [] +body: + - type: textarea + id: description + attributes: + label: Description + description: Describe the purpose of this fork and any planned changes + placeholder: Type your description here... + validations: + required: true From a81f21f91bfc70f9f0b9fb0016164f3dd9837a8f Mon Sep 17 00:00:00 2001 From: richardwhiteii <2885312+richardwhiteii@users.noreply.github.com> Date: Sat, 15 Feb 2025 21:44:18 -0500 Subject: [PATCH 02/10] [Fork] .roomodes json syntax Related to #12 Add error handling for invalid `.roomodes` JSON syntax and display error message in RED on the "Prompts" page. * **CustomModesManager.ts** - Add `errorState` property to store error messages. - Update `loadModesFromFile` method to catch JSON syntax errors and set the error state. - Add `getErrorState`, `setErrorState`, and `clearErrorState` methods to manage the error state. * **CustomModesManager.test.ts** - Add tests to verify that an error message is displayed when there is an error in the `.roomodes` JSON syntax. - Update existing tests to handle the new error state logic. * **PromptsView.tsx** - Add `errorState` to component state. - Add logic to fetch the error state from `CustomModesManager` and update the component state. - Display the error message in RED beside the word "Modes" when there is an error in the `.roomodes` JSON syntax. --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/richardwhiteii/Roo-Code/issues/12?shareId=XXXX-XXXX-XXXX-XXXX). --- src/core/config/CustomModesManager.ts | 20 ++++++++++++++++++- .../__tests__/CustomModesManager.test.ts | 20 +++++++++++++++++++ .../src/components/prompts/PromptsView.tsx | 11 ++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/core/config/CustomModesManager.ts b/src/core/config/CustomModesManager.ts index 331e3fb9097..5ecfa45fc99 100644 --- a/src/core/config/CustomModesManager.ts +++ b/src/core/config/CustomModesManager.ts @@ -12,6 +12,7 @@ export class CustomModesManager { private disposables: vscode.Disposable[] = [] private isWriting = false private writeQueue: Array<() => Promise> = [] + private errorState: string | null = null constructor( private readonly context: vscode.ExtensionContext, @@ -64,6 +65,7 @@ export class CustomModesManager { if (!result.success) { const errorMsg = `Schema validation failed for ${filePath}` console.error(`[CustomModesManager] ${errorMsg}:`, result.error) + this.setErrorState(errorMsg) return [] } @@ -72,6 +74,7 @@ export class CustomModesManager { const source = isRoomodes ? ("project" as const) : ("global" as const) // Add source to each mode + this.clearErrorState() return result.data.customModes.map((mode) => ({ ...mode, source, @@ -79,6 +82,7 @@ export class CustomModesManager { } catch (error) { const errorMsg = `Failed to load modes from ${filePath}: ${error instanceof Error ? error.message : String(error)}` console.error(`[CustomModesManager] ${errorMsg}`) + this.setErrorState(errorMsg) return [] } } @@ -141,12 +145,14 @@ export class CustomModesManager { } catch (error) { console.error(error) vscode.window.showErrorMessage(errorMessage) + this.setErrorState(errorMessage) return } const result = CustomModesSettingsSchema.safeParse(config) if (!result.success) { vscode.window.showErrorMessage(errorMessage) + this.setErrorState(errorMessage) return } @@ -285,7 +291,7 @@ export class CustomModesManager { const roomodesPath = await this.getWorkspaceRoomodes() const settingsModes = await this.loadModesFromFile(settingsPath) - const roomodesModes = roomodesPath ? await this.loadModesFromFile(roomodesPath) : [] + const roomodesModes = await this.loadModesFromFile(roomodesPath) // Find the mode in either file const projectMode = roomodesModes.find((m) => m.slug === slug) @@ -334,6 +340,18 @@ export class CustomModesManager { } } + getErrorState(): string | null { + return this.errorState + } + + private setErrorState(error: string): void { + this.errorState = error + } + + private clearErrorState(): void { + this.errorState = null + } + dispose(): void { for (const disposable of this.disposables) { disposable.dispose() diff --git a/src/core/config/__tests__/CustomModesManager.test.ts b/src/core/config/__tests__/CustomModesManager.test.ts index bbc070b0ba7..e534d7eb4ce 100644 --- a/src/core/config/__tests__/CustomModesManager.test.ts +++ b/src/core/config/__tests__/CustomModesManager.test.ts @@ -317,6 +317,26 @@ describe("CustomModesManager", () => { expect(fs.readFile).toHaveBeenCalledWith(configPath, "utf-8") expect(mockContext.globalState.update).toHaveBeenCalled() expect(mockOnUpdate).toHaveBeenCalled() + expect(manager.getErrorState()).toBeNull() + }) + + it("sets error state for invalid JSON in settings file", async () => { + const configPath = path.join(mockStoragePath, "settings", "cline_custom_modes.json") + ;(fs.readFile as jest.Mock).mockResolvedValue("invalid json") + + // Get the registered callback + const registerCall = (vscode.workspace.onDidSaveTextDocument as jest.Mock).mock.calls[0] + expect(registerCall).toBeDefined() + const [callback] = registerCall + + // Simulate file save event + const mockDocument = { + uri: { fsPath: configPath }, + } + await callback(mockDocument) + + // Verify error state was set + expect(manager.getErrorState()).toContain("Invalid custom modes format") }) }) diff --git a/webview-ui/src/components/prompts/PromptsView.tsx b/webview-ui/src/components/prompts/PromptsView.tsx index 5956ff6265b..00b9683c9f8 100644 --- a/webview-ui/src/components/prompts/PromptsView.tsx +++ b/webview-ui/src/components/prompts/PromptsView.tsx @@ -71,6 +71,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { const [showConfigMenu, setShowConfigMenu] = useState(false) const [isCreateModeDialogOpen, setIsCreateModeDialogOpen] = useState(false) const [activeSupportTab, setActiveSupportTab] = useState("ENHANCE") + const [errorState, setErrorState] = useState(null) // Add error state // Direct update functions const updateAgentPrompt = useCallback( @@ -292,6 +293,9 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { setSelectedPromptTitle(`System Prompt (${message.mode} mode)`) setIsDialogOpen(true) } + // Fetch error state from CustomModesManager + } else if (message.type === "errorState") { + setErrorState(message.text) } } @@ -517,6 +521,13 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { + {/* Display error message in RED */} + {errorState && ( +
+ Error: {errorState} +
+ )} +
{/* Only show name and delete for custom modes */} {mode && findModeBySlug(mode, customModes) && ( From cf3e3cc54f9f5c7a28137cf94a4dbc09fd791f4f Mon Sep 17 00:00:00 2001 From: Richard Date: Sun, 16 Feb 2025 03:10:32 +0000 Subject: [PATCH 03/10] updated --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 211d06aa199..5dc3f49a56a 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ docs/_site/ #Logging logs +.devcontainer \ No newline at end of file From a5c3039039e323eae287292110d338d28ba22343 Mon Sep 17 00:00:00 2001 From: richardwhiteii <2885312+richardwhiteii@users.noreply.github.com> Date: Sun, 16 Feb 2025 12:14:30 -0500 Subject: [PATCH 04/10] Update simpleissue.yaml Just adding more detail to this issues type. Based on the current ISSUE_TEMPLATE structure, it is difficult to just create an issue within a fork. This issue type allows for quickly capture an idea as an issue, without the overhead that is beneficial when contributing to the larger repo. --- .github/ISSUE_TEMPLATE/simpleissue.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/simpleissue.yaml b/.github/ISSUE_TEMPLATE/simpleissue.yaml index 6bea4c29715..16c0db0c0aa 100644 --- a/.github/ISSUE_TEMPLATE/simpleissue.yaml +++ b/.github/ISSUE_TEMPLATE/simpleissue.yaml @@ -1,5 +1,5 @@ name: Fork Issue -description: Create an issue to track changes made in this fork +description: Create a simple issue to track changes when working inside the fork, before going to main. title: "[Fork] " labels: [] assignees: [] From c8ce43972e257f0b225496480a43caa6be4b0cec Mon Sep 17 00:00:00 2001 From: Richard Date: Sun, 16 Feb 2025 20:03:19 +0000 Subject: [PATCH 05/10] Fix loading of roomodes modes and adjust role definition fallback logic --- src/core/config/CustomModesManager.ts | 2 +- src/core/prompts/system.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/config/CustomModesManager.ts b/src/core/config/CustomModesManager.ts index 5ecfa45fc99..e5f65239370 100644 --- a/src/core/config/CustomModesManager.ts +++ b/src/core/config/CustomModesManager.ts @@ -291,7 +291,7 @@ export class CustomModesManager { const roomodesPath = await this.getWorkspaceRoomodes() const settingsModes = await this.loadModesFromFile(settingsPath) - const roomodesModes = await this.loadModesFromFile(roomodesPath) + const roomodesModes = roomodesPath ? await this.loadModesFromFile(roomodesPath) : [] // Find the mode in either file const projectMode = roomodesModes.find((m) => m.slug === slug) diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index 91bbd073870..5c4b4257bf4 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -56,7 +56,7 @@ async function generatePrompt( // Get the full mode config to ensure we have the role definition const modeConfig = getModeBySlug(mode, customModeConfigs) || modes.find((m) => m.slug === mode) || modes[0] - const roleDefinition = promptComponent?.roleDefinition || modeConfig.roleDefinition + const roleDefinition = modeConfig.roleDefinition || promptComponent?.roleDefinition const basePrompt = `${roleDefinition} From f7bf7a8608cc3da8748c352f6a943feaa68660c4 Mon Sep 17 00:00:00 2001 From: Richard Date: Sun, 16 Feb 2025 20:19:16 +0000 Subject: [PATCH 06/10] Refactor CustomModesManager to remove error state handling and update PromptsView to eliminate unused error state --- src/core/config/CustomModesManager.ts | 18 ----------------- .../__tests__/CustomModesManager.test.ts | 20 ------------------- .../src/components/prompts/PromptsView.tsx | 11 ---------- 3 files changed, 49 deletions(-) diff --git a/src/core/config/CustomModesManager.ts b/src/core/config/CustomModesManager.ts index e5f65239370..331e3fb9097 100644 --- a/src/core/config/CustomModesManager.ts +++ b/src/core/config/CustomModesManager.ts @@ -12,7 +12,6 @@ export class CustomModesManager { private disposables: vscode.Disposable[] = [] private isWriting = false private writeQueue: Array<() => Promise> = [] - private errorState: string | null = null constructor( private readonly context: vscode.ExtensionContext, @@ -65,7 +64,6 @@ export class CustomModesManager { if (!result.success) { const errorMsg = `Schema validation failed for ${filePath}` console.error(`[CustomModesManager] ${errorMsg}:`, result.error) - this.setErrorState(errorMsg) return [] } @@ -74,7 +72,6 @@ export class CustomModesManager { const source = isRoomodes ? ("project" as const) : ("global" as const) // Add source to each mode - this.clearErrorState() return result.data.customModes.map((mode) => ({ ...mode, source, @@ -82,7 +79,6 @@ export class CustomModesManager { } catch (error) { const errorMsg = `Failed to load modes from ${filePath}: ${error instanceof Error ? error.message : String(error)}` console.error(`[CustomModesManager] ${errorMsg}`) - this.setErrorState(errorMsg) return [] } } @@ -145,14 +141,12 @@ export class CustomModesManager { } catch (error) { console.error(error) vscode.window.showErrorMessage(errorMessage) - this.setErrorState(errorMessage) return } const result = CustomModesSettingsSchema.safeParse(config) if (!result.success) { vscode.window.showErrorMessage(errorMessage) - this.setErrorState(errorMessage) return } @@ -340,18 +334,6 @@ export class CustomModesManager { } } - getErrorState(): string | null { - return this.errorState - } - - private setErrorState(error: string): void { - this.errorState = error - } - - private clearErrorState(): void { - this.errorState = null - } - dispose(): void { for (const disposable of this.disposables) { disposable.dispose() diff --git a/src/core/config/__tests__/CustomModesManager.test.ts b/src/core/config/__tests__/CustomModesManager.test.ts index e534d7eb4ce..bbc070b0ba7 100644 --- a/src/core/config/__tests__/CustomModesManager.test.ts +++ b/src/core/config/__tests__/CustomModesManager.test.ts @@ -317,26 +317,6 @@ describe("CustomModesManager", () => { expect(fs.readFile).toHaveBeenCalledWith(configPath, "utf-8") expect(mockContext.globalState.update).toHaveBeenCalled() expect(mockOnUpdate).toHaveBeenCalled() - expect(manager.getErrorState()).toBeNull() - }) - - it("sets error state for invalid JSON in settings file", async () => { - const configPath = path.join(mockStoragePath, "settings", "cline_custom_modes.json") - ;(fs.readFile as jest.Mock).mockResolvedValue("invalid json") - - // Get the registered callback - const registerCall = (vscode.workspace.onDidSaveTextDocument as jest.Mock).mock.calls[0] - expect(registerCall).toBeDefined() - const [callback] = registerCall - - // Simulate file save event - const mockDocument = { - uri: { fsPath: configPath }, - } - await callback(mockDocument) - - // Verify error state was set - expect(manager.getErrorState()).toContain("Invalid custom modes format") }) }) diff --git a/webview-ui/src/components/prompts/PromptsView.tsx b/webview-ui/src/components/prompts/PromptsView.tsx index 00b9683c9f8..5956ff6265b 100644 --- a/webview-ui/src/components/prompts/PromptsView.tsx +++ b/webview-ui/src/components/prompts/PromptsView.tsx @@ -71,7 +71,6 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { const [showConfigMenu, setShowConfigMenu] = useState(false) const [isCreateModeDialogOpen, setIsCreateModeDialogOpen] = useState(false) const [activeSupportTab, setActiveSupportTab] = useState("ENHANCE") - const [errorState, setErrorState] = useState(null) // Add error state // Direct update functions const updateAgentPrompt = useCallback( @@ -293,9 +292,6 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { setSelectedPromptTitle(`System Prompt (${message.mode} mode)`) setIsDialogOpen(true) } - // Fetch error state from CustomModesManager - } else if (message.type === "errorState") { - setErrorState(message.text) } } @@ -521,13 +517,6 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
- {/* Display error message in RED */} - {errorState && ( -
- Error: {errorState} -
- )} -
{/* Only show name and delete for custom modes */} {mode && findModeBySlug(mode, customModes) && ( From 5b57244e859503811f860e456020ddefd58fa586 Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 17 Feb 2025 01:25:42 +0000 Subject: [PATCH 07/10] Remove simple issue template from GitHub repository --- .github/ISSUE_TEMPLATE/simpleissue.yaml | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/simpleissue.yaml diff --git a/.github/ISSUE_TEMPLATE/simpleissue.yaml b/.github/ISSUE_TEMPLATE/simpleissue.yaml deleted file mode 100644 index 6bea4c29715..00000000000 --- a/.github/ISSUE_TEMPLATE/simpleissue.yaml +++ /dev/null @@ -1,14 +0,0 @@ -name: Fork Issue -description: Create an issue to track changes made in this fork -title: "[Fork] " -labels: [] -assignees: [] -body: - - type: textarea - id: description - attributes: - label: Description - description: Describe the purpose of this fork and any planned changes - placeholder: Type your description here... - validations: - required: true From 609920c978b4abd6c5e14e87d9c1f42d25023c31 Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 17 Feb 2025 01:27:03 +0000 Subject: [PATCH 08/10] Update .gitignore to include .devcontainer and .roomodes directories --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5dc3f49a56a..04a07128c14 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,6 @@ docs/_site/ #Logging logs -.devcontainer \ No newline at end of file +.devcontainer +.roomodes +.gitignore \ No newline at end of file From 57d41e5e5181487bcb3cbffca05e901cde01eb96 Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 17 Feb 2025 01:48:37 +0000 Subject: [PATCH 09/10] Fix optional chaining for role definition retrieval in generatePrompt function --- src/core/prompts/system.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index 5c4b4257bf4..b949a9f0179 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -56,7 +56,7 @@ async function generatePrompt( // Get the full mode config to ensure we have the role definition const modeConfig = getModeBySlug(mode, customModeConfigs) || modes.find((m) => m.slug === mode) || modes[0] - const roleDefinition = modeConfig.roleDefinition || promptComponent?.roleDefinition + const roleDefinition = modeConfig?.roleDefinition || promptComponent?.roleDefinition const basePrompt = `${roleDefinition} From 7698623a9ead4ba6019ac4f4ea4faf2459c1abcc Mon Sep 17 00:00:00 2001 From: richardwhiteii <2885312+richardwhiteii@users.noreply.github.com> Date: Mon, 17 Feb 2025 11:00:04 -0500 Subject: [PATCH 10/10] Update .gitignore removed .roomodes .gitignore based on feedback. --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index 04a07128c14..3d1bfd00e9a 100644 --- a/.gitignore +++ b/.gitignore @@ -29,5 +29,3 @@ docs/_site/ #Logging logs .devcontainer -.roomodes -.gitignore \ No newline at end of file