diff --git a/src/core/__tests__/mode-validator.test.ts b/src/core/__tests__/mode-validator.test.ts index bd6d3237fd..632ca8a8ab 100644 --- a/src/core/__tests__/mode-validator.test.ts +++ b/src/core/__tests__/mode-validator.test.ts @@ -9,8 +9,8 @@ describe("mode-validator", () => { it("allows all code mode tools", () => { const mode = getModeConfig(codeMode) // Code mode has all groups - Object.entries(TOOL_GROUPS).forEach(([_, tools]) => { - tools.forEach((tool) => { + Object.entries(TOOL_GROUPS).forEach(([_, config]) => { + config.tools.forEach((tool: string) => { expect(isToolAllowedForMode(tool, codeMode, [])).toBe(true) }) }) @@ -25,7 +25,11 @@ describe("mode-validator", () => { it("allows configured tools", () => { const mode = getModeConfig(architectMode) // Architect mode has read, browser, and mcp groups - const architectTools = [...TOOL_GROUPS.read, ...TOOL_GROUPS.browser, ...TOOL_GROUPS.mcp] + const architectTools = [ + ...TOOL_GROUPS.read.tools, + ...TOOL_GROUPS.browser.tools, + ...TOOL_GROUPS.mcp.tools, + ] architectTools.forEach((tool) => { expect(isToolAllowedForMode(tool, architectMode, [])).toBe(true) }) @@ -36,7 +40,7 @@ describe("mode-validator", () => { it("allows configured tools", () => { const mode = getModeConfig(askMode) // Ask mode has read, browser, and mcp groups - const askTools = [...TOOL_GROUPS.read, ...TOOL_GROUPS.browser, ...TOOL_GROUPS.mcp] + const askTools = [...TOOL_GROUPS.read.tools, ...TOOL_GROUPS.browser.tools, ...TOOL_GROUPS.mcp.tools] askTools.forEach((tool) => { expect(isToolAllowedForMode(tool, askMode, [])).toBe(true) }) diff --git a/src/core/prompts/tools/index.ts b/src/core/prompts/tools/index.ts index c4bdfc98c3..1b9b9a43d9 100644 --- a/src/core/prompts/tools/index.ts +++ b/src/core/prompts/tools/index.ts @@ -66,7 +66,7 @@ export function getToolDescriptionsForMode( const groupName = getGroupName(groupEntry) const toolGroup = TOOL_GROUPS[groupName] if (toolGroup) { - toolGroup.forEach((tool) => { + toolGroup.tools.forEach((tool) => { if (isToolAllowedForMode(tool as ToolName, mode, customModes ?? [], experiments ?? {})) { tools.add(tool) } diff --git a/src/shared/modes.ts b/src/shared/modes.ts index abbeea72b7..3e9b0a32e2 100644 --- a/src/shared/modes.ts +++ b/src/shared/modes.ts @@ -59,7 +59,8 @@ export function getToolsForMode(groups: readonly GroupEntry[]): string[] { // Add tools from each group groups.forEach((group) => { const groupName = getGroupName(group) - TOOL_GROUPS[groupName].forEach((tool) => tools.add(tool)) + const groupConfig = TOOL_GROUPS[groupName] + groupConfig.tools.forEach((tool: string) => tools.add(tool)) }) // Always add required tools @@ -190,8 +191,10 @@ export function isToolAllowedForMode( const groupName = getGroupName(group) const options = getGroupOptions(group) - // If the tool isn't in this group, continue to next group - if (!TOOL_GROUPS[groupName].includes(tool)) { + const groupConfig = TOOL_GROUPS[groupName] + + // If the tool isn't in this group's tools, continue to next group + if (!groupConfig.tools.includes(tool)) { continue } diff --git a/src/shared/tool-groups.ts b/src/shared/tool-groups.ts index 055fb11660..2728d42319 100644 --- a/src/shared/tool-groups.ts +++ b/src/shared/tool-groups.ts @@ -1,5 +1,8 @@ -// Define tool group values -export type ToolGroupValues = readonly string[] +// Define tool group configuration +export type ToolGroupConfig = { + tools: readonly string[] + alwaysAvailable?: boolean // Whether this group is always available and shouldn't show in prompts view +} // Map of tool slugs to their display names export const TOOL_DISPLAY_NAMES = { @@ -20,13 +23,26 @@ export const TOOL_DISPLAY_NAMES = { } as const // Define available tool groups -export const TOOL_GROUPS: Record = { - read: ["read_file", "search_files", "list_files", "list_code_definition_names"], - edit: ["write_to_file", "apply_diff", "insert_content", "search_and_replace"], - browser: ["browser_action"], - command: ["execute_command"], - mcp: ["use_mcp_tool", "access_mcp_resource"], - modes: ["switch_mode", "new_task"], +export const TOOL_GROUPS: Record = { + read: { + tools: ["read_file", "search_files", "list_files", "list_code_definition_names"], + }, + edit: { + tools: ["write_to_file", "apply_diff", "insert_content", "search_and_replace"], + }, + browser: { + tools: ["browser_action"], + }, + command: { + tools: ["execute_command"], + }, + mcp: { + tools: ["use_mcp_tool", "access_mcp_resource"], + }, + modes: { + tools: ["switch_mode", "new_task"], + alwaysAvailable: true, + }, } export type ToolGroup = keyof typeof TOOL_GROUPS diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index 519b7e5efb..d9cbe62453 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -994,7 +994,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie }}> {showAnnouncement && }
-

What can I do for you?

+

What can Roo do for you?

Thanks to the latest breakthroughs in agentic coding capabilities, I can handle complex software development tasks step-by-step. With tools that let me create & edit files, explore diff --git a/webview-ui/src/components/prompts/PromptsView.tsx b/webview-ui/src/components/prompts/PromptsView.tsx index eff9f99f48..e76e5e43ac 100644 --- a/webview-ui/src/components/prompts/PromptsView.tsx +++ b/webview-ui/src/components/prompts/PromptsView.tsx @@ -26,8 +26,8 @@ import { import { TOOL_GROUPS, GROUP_DISPLAY_NAMES, ToolGroup } from "../../../../src/shared/tool-groups" import { vscode } from "../../utils/vscode" -// Get all available groups from GROUP_DISPLAY_NAMES -const availableGroups = Object.keys(TOOL_GROUPS) as ToolGroup[] +// Get all available groups that should show in prompts view +const availableGroups = (Object.keys(TOOL_GROUPS) as ToolGroup[]).filter((group) => !TOOL_GROUPS[group].alwaysAvailable) type PromptsViewProps = { onDone: () => void @@ -65,6 +65,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { const [isToolsEditMode, setIsToolsEditMode] = useState(false) const [isCreateModeDialogOpen, setIsCreateModeDialogOpen] = useState(false) const [activeSupportTab, setActiveSupportTab] = useState("ENHANCE") + const [selectedModeTab, setSelectedModeTab] = useState(mode) // Direct update functions const updateAgentPrompt = useCallback( @@ -110,26 +111,23 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { text: slug, }) }, []) - - // Handle mode switching with explicit state initialization + // Handle mode tab selection without actually switching modes const handleModeSwitch = useCallback( (modeConfig: ModeConfig) => { - if (modeConfig.slug === mode) return // Prevent unnecessary updates + if (modeConfig.slug === selectedModeTab) return // Prevent unnecessary updates - // First switch the mode - switchMode(modeConfig.slug) - - // Exit tools edit mode when switching modes + // Update selected tab and reset tools edit mode + setSelectedModeTab(modeConfig.slug) setIsToolsEditMode(false) }, - [mode, switchMode, setIsToolsEditMode], + [selectedModeTab, setIsToolsEditMode], ) // Helper function to get current mode's config const getCurrentMode = useCallback((): ModeConfig | undefined => { - const findMode = (m: ModeConfig): boolean => m.slug === mode + const findMode = (m: ModeConfig): boolean => m.slug === selectedModeTab return customModes?.find(findMode) || modes.find(findMode) - }, [mode, customModes, modes]) + }, [selectedModeTab, customModes, modes]) // Helper function to safely access mode properties const getModeProperty = ( @@ -155,6 +153,11 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { } }, [isCreateModeDialogOpen]) + // Keep selected tab in sync with actual mode + useEffect(() => { + setSelectedModeTab(mode) + }, [mode]) + // Helper function to generate a unique slug from a name const generateSlug = useCallback((name: string, attempt = 0): string => { const baseSlug = name @@ -184,22 +187,13 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { groups: newModeGroups, } updateCustomMode(newModeSlug, newMode) - switchMode(newModeSlug) setIsCreateModeDialogOpen(false) setNewModeName("") setNewModeSlug("") setNewModeRoleDefinition("") setNewModeCustomInstructions("") setNewModeGroups(availableGroups) - }, [ - newModeName, - newModeSlug, - newModeRoleDefinition, - newModeCustomInstructions, - newModeGroups, - updateCustomMode, - switchMode, - ]) + }, [newModeName, newModeSlug, newModeRoleDefinition, newModeCustomInstructions, newModeGroups, updateCustomMode]) const isNameOrSlugTaken = useCallback( (name: string, slug: string) => { @@ -479,7 +473,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { padding: "4px 0", }}> {modes.map((modeConfig) => { - const isActive = mode === modeConfig.slug + const isActive = selectedModeTab === modeConfig.slug return (