/** * @license * Copyright 2025 Qwen * SPDX-License-Identifier: Apache-2.0 */ import { useState, useMemo } from 'react'; import { Box, Text } from 'ink'; import { RadioButtonSelect } from '../shared/RadioButtonSelect.js'; import { WizardStepProps, ToolCategory } from './types.js'; import { Kind } from '@qwen-code/qwen-code-core'; import { Colors } from '../../colors.js'; interface ToolOption { label: string; value: string; category: ToolCategory; } /** * Step 4: Tool selection with categories. */ export function ToolSelector({ state: _state, dispatch, onNext, onPrevious: _onPrevious, config, }: WizardStepProps) { const [selectedCategory, setSelectedCategory] = useState('all'); // Generate tool categories from actual tool registry const { toolCategories, readTools, editTools, executeTools } = useMemo(() => { if (!config) { // Fallback categories if config not available return { toolCategories: [ { id: 'all', name: 'All Tools (Default)', tools: [], }, ], readTools: [], editTools: [], executeTools: [], }; } const toolRegistry = config.getToolRegistry(); const allTools = toolRegistry.getAllTools(); // Categorize tools by Kind const readTools = allTools .filter( (tool) => tool.kind === Kind.Read || tool.kind === Kind.Search || tool.kind === Kind.Fetch, ) .map((tool) => tool.displayName) .sort(); const editTools = allTools .filter( (tool) => tool.kind === Kind.Edit || tool.kind === Kind.Delete || tool.kind === Kind.Move || tool.kind === Kind.Think, ) .map((tool) => tool.displayName) .sort(); const executeTools = allTools .filter((tool) => tool.kind === Kind.Execute) .map((tool) => tool.displayName) .sort(); const toolCategories = [ { id: 'all', name: 'All Tools', tools: [], }, { id: 'read', name: 'Read-only Tools', tools: readTools, }, { id: 'edit', name: 'Read & Edit Tools', tools: [...readTools, ...editTools], }, { id: 'execute', name: 'Read & Edit & Execution Tools', tools: [...readTools, ...editTools, ...executeTools], }, ].filter((category) => category.id === 'all' || category.tools.length > 0); return { toolCategories, readTools, editTools, executeTools }; }, [config]); const toolOptions: ToolOption[] = toolCategories.map((category) => ({ label: category.name, value: category.id, category, })); const handleHighlight = (selectedValue: string) => { setSelectedCategory(selectedValue); }; const handleSelect = (selectedValue: string) => { const category = toolCategories.find((cat) => cat.id === selectedValue); if (category) { if (category.id === 'all') { dispatch({ type: 'SET_TOOLS', tools: 'all' }); } else { dispatch({ type: 'SET_TOOLS', tools: category.tools }); } onNext(); } }; // Get the currently selected category for displaying tools const currentCategory = toolCategories.find( (cat) => cat.id === selectedCategory, ); return ( ({ label: option.label, value: option.value, }))} initialIndex={toolOptions.findIndex( (opt) => opt.value === selectedCategory, )} onSelect={handleSelect} onHighlight={handleHighlight} isFocused={true} /> {/* Show help information or tools for selected category */} {currentCategory && ( {currentCategory.id === 'all' ? ( All tools selected, including MCP tools ) : currentCategory.tools.length > 0 ? ( <> Selected tools: {(() => { // Filter the already categorized tools to show only those in current category const categoryReadTools = currentCategory.tools.filter( (tool) => readTools.includes(tool), ); const categoryEditTools = currentCategory.tools.filter( (tool) => editTools.includes(tool), ); const categoryExecuteTools = currentCategory.tools.filter( (tool) => executeTools.includes(tool), ); return ( <> {categoryReadTools.length > 0 && ( • Read-only tools: {categoryReadTools.join(', ')} )} {categoryEditTools.length > 0 && ( • Edit tools: {categoryEditTools.join(', ')} )} {categoryExecuteTools.length > 0 && ( • Execution tools: {categoryExecuteTools.join(', ')} )} ); })()} ) : null} )} ); }