/** * @license * Copyright 2025 Qwen * SPDX-License-Identifier: Apache-2.0 */ import { useCallback, useState, useEffect } from 'react'; import { Box, Text } from 'ink'; import type { WizardStepProps } from '../types.js'; import type { SubagentManager, SubagentConfig, } from '@qwen-code/qwen-code-core'; import { theme } from '../../../semantic-colors.js'; import { shouldShowColor, getColorForDisplay } from '../utils.js'; import { useLaunchEditor } from '../../../hooks/useLaunchEditor.js'; import { useKeypress } from '../../../hooks/useKeypress.js'; import { t } from '../../../../i18n/index.js'; /** * Step 6: Final confirmation and actions. */ export function CreationSummary({ state, onPrevious: _onPrevious, onCancel, config, }: WizardStepProps) { const [saveError, setSaveError] = useState(null); const [saveSuccess, setSaveSuccess] = useState(false); const [warnings, setWarnings] = useState([]); const launchEditor = useLaunchEditor(); const truncateText = (text: string, maxLength: number): string => { if (text.length <= maxLength) return text; return text.substring(0, maxLength - 3) + '...'; }; // Check for warnings useEffect(() => { const checkWarnings = async () => { if (!config || !state.generatedName) return; const allWarnings: string[] = []; try { // Get project root from config const subagentManager = config.getSubagentManager(); // Check for name conflicts const isAvailable = await subagentManager.isNameAvailable( state.generatedName, ); if (!isAvailable) { const existing = await subagentManager.loadSubagent( state.generatedName, ); if (existing) { const conflictLevel = existing.level === 'project' ? 'project' : 'user'; const targetLevel = state.location; if (conflictLevel === targetLevel) { allWarnings.push( t( 'Name "{{name}}" already exists at {{level}} level - will overwrite existing subagent', { name: state.generatedName, level: conflictLevel }, ), ); } else if (targetLevel === 'project') { allWarnings.push( t( 'Name "{{name}}" exists at user level - project level will take precedence', { name: state.generatedName }, ), ); } else { allWarnings.push( t( 'Name "{{name}}" exists at project level - existing subagent will take precedence', { name: state.generatedName }, ), ); } } } } catch (error) { // Silently handle errors in warning checks console.warn('Error checking subagent name availability:', error); } // Check length warnings if (state.generatedDescription.length > 300) { allWarnings.push( t('Description is over {{length}} characters', { length: state.generatedDescription.length.toString(), }), ); } if (state.generatedSystemPrompt.length > 10000) { allWarnings.push( t('System prompt is over {{length}} characters', { length: state.generatedSystemPrompt.length.toString(), }), ); } setWarnings(allWarnings); }; checkWarnings(); }, [ config, state.generatedName, state.generatedDescription, state.generatedSystemPrompt, state.location, ]); // If no tools explicitly selected, it means "all tools" for this agent const toolsDisplay = state.selectedTools.length === 0 ? '*' : state.selectedTools.join(', '); // Common method to save subagent configuration const saveSubagent = useCallback(async (): Promise => { // Create SubagentManager instance if (!config) { throw new Error('Configuration not available'); } const subagentManager = config.getSubagentManager(); // Build subagent configuration const subagentConfig: SubagentConfig = { name: state.generatedName, description: state.generatedDescription, systemPrompt: state.generatedSystemPrompt, level: state.location, filePath: '', // Will be set by manager tools: Array.isArray(state.selectedTools) ? state.selectedTools : undefined, color: state.color, }; // Create the subagent await subagentManager.createSubagent(subagentConfig, { level: state.location, overwrite: true, }); return subagentManager; }, [state, config]); // Common method to show success and auto-close const showSuccessAndClose = useCallback(() => { setSaveSuccess(true); // Auto-close after successful save setTimeout(() => { onCancel(); }, 2000); }, [onCancel]); const handleSave = useCallback(async () => { setSaveError(null); try { await saveSubagent(); showSuccessAndClose(); } catch (error) { setSaveError( error instanceof Error ? error.message : 'Unknown error occurred', ); } }, [saveSubagent, showSuccessAndClose]); const handleEdit = useCallback(async () => { // Clear any previous error messages setSaveError(null); try { // Save the subagent to file first using shared logic const subagentManager = await saveSubagent(); // Get the file path of the created subagent const subagentFilePath = subagentManager.getSubagentPath( state.generatedName, state.location, ); // Launch editor with the actual subagent file await launchEditor(subagentFilePath); // Show success UI and auto-close after successful edit showSuccessAndClose(); } catch (error) { setSaveError( t('Failed to save and edit subagent: {{error}}', { error: error instanceof Error ? error.message : 'Unknown error', }), ); } }, [ saveSubagent, showSuccessAndClose, state.generatedName, state.location, launchEditor, ]); // Handle keyboard input useKeypress( (key) => { if (saveSuccess) return; if (key.name === 'return' || key.sequence === 's') { handleSave(); return; } if (key.sequence === 'e') { handleEdit(); return; } }, { isActive: true }, ); if (saveSuccess) { return ( {t('✅ Subagent Created Successfully!')} {t('Subagent "{{name}}" has been saved to {{level}} level.', { name: state.generatedName, level: state.location, })} ); } return ( {t('Name: ')} {state.generatedName} {t('Location: ')} {state.location === 'project' ? t('Project Level (.qwen/agents/)') : t('User Level (~/.qwen/agents/)')} {t('Tools: ')} {toolsDisplay} {shouldShowColor(state.color) && ( {t('Color: ')} {state.color} )} {t('Description:')} {truncateText(state.generatedDescription, 250)} {t('System Prompt:')} {truncateText(state.generatedSystemPrompt, 250)} {saveError && ( {t('❌ Error saving subagent:')} {saveError} )} {warnings.length > 0 && ( {t('Warnings:')} {warnings.map((warning, index) => ( • {warning} ))} )} ); }