UNPKG

@quantumai/quantum-cli-core

Version:

Quantum CLI Core - Multi-LLM Collaboration System

565 lines 25.8 kB
/** * @license * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ export class OnboardingSystem { features = new Map(); userState; historyManager; preferenceEngine; constructor(historyManager, preferenceEngine, initialState) { this.historyManager = historyManager; this.preferenceEngine = preferenceEngine; this.userState = { completedFeatures: new Set(), dismissedFeatures: new Set(), currentlyLearning: null, learningStartTime: null, skillLevel: 'beginner', preferredLearningStyle: 'guided', lastOnboardingTime: 0, onboardingBudget: 10, // Default 10 minutes per session ...initialState }; this.initializeFeatures(); } initializeFeatures() { // Multi-LLM Collaboration Features this.addFeature({ id: 'multi_llm_verification', name: 'AI 응답 검증', description: '중요한 작업에서 여러 AI 모델의 의견을 비교하여 더 정확한 결과를 얻습니다', category: 'collaboration', prerequisites: [], estimatedTimeToValue: 5, complexity: 'beginner', triggers: [ { type: 'usage_pattern', condition: 'high_complexity_queries > 3', weight: 0.8 }, { type: 'context_based', condition: 'security_related_query', weight: 0.9 }, { type: 'explicit_interest', condition: 'quality_concern_expressed', weight: 1.0 } ], tutorial: { steps: [ { title: '검증 모드 활성화', description: '/verify 명령어로 다중 AI 검증을 활성화할 수 있습니다', action: '/verify auto', example: '/verify auto "보안 관련 코드 검토해줘"', hint: '보안, 배포, 중요한 결정이 필요한 작업에 특히 유용합니다' }, { title: '자동 트리거 설정', description: '특정 조건에서 자동으로 검증 모드를 활성화하도록 설정할 수 있습니다', action: '/settings collaboration.autoVerifyThreshold 0.7', hint: '불확실성이 70% 이상일 때 자동으로 검증 모드가 활성화됩니다' } ], estimatedDuration: 5, interactiveDemo: true } }); this.addFeature({ id: 'uncertainty_detection', name: '불확실성 감지', description: 'AI가 응답에 확신이 없을 때를 자동으로 감지하고 추가 검증을 제안합니다', category: 'collaboration', prerequisites: ['multi_llm_verification'], estimatedTimeToValue: 3, complexity: 'intermediate', triggers: [ { type: 'usage_pattern', condition: 'verification_mode_used > 2', weight: 0.7 }, { type: 'context_based', condition: 'complex_technical_query', weight: 0.8 } ], tutorial: { steps: [ { title: '불확실성 점수 확인', description: 'AI 응답의 신뢰도를 실시간으로 확인할 수 있습니다', hint: '신뢰도가 낮을 때는 자동으로 다른 모델의 의견을 요청합니다' }, { title: '임계값 조정', description: '개인의 요구에 맞게 불확실성 감지 임계값을 조정할 수 있습니다', action: '/settings uncertainty.threshold 0.6' } ], estimatedDuration: 3, interactiveDemo: false } }); // Advanced Productivity Features this.addFeature({ id: 'smart_context_management', name: '스마트 컨텍스트 관리', description: '프로젝트 컨텍스트를 자동으로 분석하고 관련 파일을 지능적으로 포함합니다', category: 'productivity', prerequisites: [], estimatedTimeToValue: 8, complexity: 'intermediate', triggers: [ { type: 'usage_pattern', condition: 'large_project_detected', weight: 0.9 }, { type: 'usage_pattern', condition: 'frequent_file_operations > 10', weight: 0.7 } ], tutorial: { steps: [ { title: 'QUANTUM.md 설정', description: '프로젝트 루트에 QUANTUM.md 파일을 생성하여 컨텍스트를 정의합니다', example: '# 프로젝트 개요\n이 프로젝트는 CLI 도구입니다...', hint: '프로젝트 구조, 코딩 스타일, 중요한 파일들을 명시하세요' }, { title: '자동 파일 발견', description: '@로 시작하는 경로를 사용하여 관련 파일을 자동으로 포함시킵니다', action: '@src/components', example: '@src/components "Button 컴포넌트 수정해줘"' } ], estimatedDuration: 8, interactiveDemo: true } }); this.addFeature({ id: 'automatic_testing', name: '자동 테스트 생성', description: '코드 변경 시 자동으로 테스트를 생성하고 실행합니다', category: 'productivity', prerequisites: ['smart_context_management'], estimatedTimeToValue: 12, complexity: 'advanced', triggers: [ { type: 'usage_pattern', condition: 'code_modification_frequency > 5', weight: 0.8 }, { type: 'context_based', condition: 'test_directory_exists', weight: 0.9 } ], tutorial: { steps: [ { title: '테스트 자동 생성', description: '코드 수정 후 /test generate 명령어로 테스트를 자동 생성합니다', action: '/test generate', hint: '기존 테스트 패턴을 분석하여 일관된 테스트를 생성합니다' }, { title: '지속적 검증', description: '변경사항에 대해 자동으로 테스트를 실행하고 결과를 보고합니다', action: '/test watch' } ], estimatedDuration: 12, interactiveDemo: true } }); // Customization Features this.addFeature({ id: 'theme_customization', name: '테마 커스터마이징', description: '개인 취향에 맞는 색상과 레이아웃으로 CLI 인터페이스를 커스터마이징합니다', category: 'customization', prerequisites: [], estimatedTimeToValue: 3, complexity: 'beginner', triggers: [ { type: 'time_based', condition: 'sessions > 5', weight: 0.6 }, { type: 'explicit_interest', condition: 'ui_customization_mentioned', weight: 0.9 } ], tutorial: { steps: [ { title: '테마 변경', description: '/theme 명령어로 다양한 색상 테마를 적용할 수 있습니다', action: '/theme', example: '/theme dracula 또는 /theme github-light' }, { title: '사용자 정의 테마', description: '개인적인 색상 조합으로 나만의 테마를 만들 수 있습니다', hint: '~/.quantum/themes/ 폴더에 사용자 정의 테마를 추가할 수 있습니다' } ], estimatedDuration: 3, interactiveDemo: true } }); this.addFeature({ id: 'custom_commands', name: '사용자 정의 명령어', description: '자주 사용하는 작업을 단축 명령어로 만들어 생산성을 향상시킵니다', category: 'customization', prerequisites: [], estimatedTimeToValue: 10, complexity: 'intermediate', triggers: [ { type: 'usage_pattern', condition: 'repeated_command_patterns > 3', weight: 0.8 }, { type: 'explicit_interest', condition: 'automation_interest_expressed', weight: 0.9 } ], tutorial: { steps: [ { title: '매크로 생성', description: '반복되는 명령어 시퀀스를 매크로로 저장합니다', action: '/macro create deploy', example: '/macro create deploy "npm run build && npm run test && git push"' }, { title: '조건부 명령어', description: '프로젝트나 상황에 따라 다르게 동작하는 스마트 명령어를 만듭니다', hint: '프로젝트 타입, 환경 변수 등을 기반으로 동적으로 실행됩니다' } ], estimatedDuration: 10, interactiveDemo: true } }); } addFeature(feature) { this.features.set(feature.id, feature); } /** * 현재 사용자 상태와 패턴을 기반으로 온보딩 추천을 생성합니다 */ getPersonalizedRecommendations() { const userPattern = this.historyManager.getUserPattern(); const preferences = this.preferenceEngine.getPreferences(); const recommendations = []; for (const [featureId, feature] of this.features) { // Skip already completed or dismissed features if (this.userState.completedFeatures.has(featureId) || this.userState.dismissedFeatures.has(featureId)) { continue; } // Check prerequisites if (!this.arePrerequisitesMet(feature.prerequisites)) { continue; } // Calculate recommendation priority const priority = this.calculateFeaturePriority(feature, userPattern, preferences); if (priority > 0.5) { // Threshold for recommendation const recommendation = { feature, priority, reason: this.generateRecommendationReason(feature, userPattern), personalizedMessage: this.generatePersonalizedMessage(feature, userPattern, preferences), optimalTiming: this.determineOptimalTiming(feature, userPattern) }; recommendations.push(recommendation); } } // Sort by priority and return top recommendations return recommendations .sort((a, b) => b.priority - a.priority) .slice(0, 3); // Limit to top 3 recommendations } /** * 현재 컨텍스트에서 즉시 도움이 될 수 있는 기능을 추천합니다 */ getContextualSuggestion(context) { const userPattern = this.historyManager.getUserPattern(); for (const [featureId, feature] of this.features) { if (this.userState.completedFeatures.has(featureId) || this.userState.dismissedFeatures.has(featureId)) { continue; } // Check if feature is relevant to current context for (const trigger of feature.triggers) { if (trigger.type === 'context_based' && this.evaluateTrigger(trigger, context, userPattern)) { return { feature, priority: trigger.weight, reason: `현재 작업과 관련된 기능입니다: ${context.queryType}`, personalizedMessage: this.generatePersonalizedMessage(feature, userPattern), optimalTiming: 'immediate' }; } } } return null; } /** * 사용자의 스킬 레벨에 맞는 학습 경로를 추천합니다 */ getLearningPath() { const path = []; const userPattern = this.historyManager.getUserPattern(); // Determine appropriate skill level progression const targetComplexity = this.getTargetComplexity(); // Find features that match the user's learning progression const availableFeatures = Array.from(this.features.values()) .filter(feature => !this.userState.completedFeatures.has(feature.id) && !this.userState.dismissedFeatures.has(feature.id) && this.isFeatureAppropriate(feature, targetComplexity, userPattern)) .sort((a, b) => { // Sort by complexity, then by estimated time to value const complexityOrder = { 'beginner': 0, 'intermediate': 1, 'advanced': 2 }; const aDiff = complexityOrder[a.complexity] - complexityOrder[targetComplexity]; const bDiff = complexityOrder[b.complexity] - complexityOrder[targetComplexity]; if (aDiff !== bDiff) return Math.abs(aDiff) - Math.abs(bDiff); return a.estimatedTimeToValue - b.estimatedTimeToValue; }); return availableFeatures.slice(0, 5); // Return top 5 features for learning path } /** * 온보딩 기능 완료를 기록합니다 */ markFeatureCompleted(featureId, completionTime) { this.userState.completedFeatures.add(featureId); this.userState.currentlyLearning = null; this.userState.learningStartTime = null; // Update skill level based on completed features this.updateSkillLevel(); // Log completion for learning this.preferenceEngine.recordInteraction({ type: 'feature_completion', featureId, timestamp: completionTime, context: { skillLevel: this.userState.skillLevel } }); } /** * 사용자가 기능을 거부했을 때 기록합니다 */ markFeatureDismissed(featureId, reason) { this.userState.dismissedFeatures.add(featureId); // Learn from dismissal this.preferenceEngine.recordInteraction({ type: 'feature_dismissal', featureId, timestamp: Date.now(), context: { reason, skillLevel: this.userState.skillLevel } }); } /** * 현재 학습 중인 기능을 설정합니다 */ startLearningFeature(featureId) { this.userState.currentlyLearning = featureId; this.userState.learningStartTime = Date.now(); } /** * 사용자의 온보딩 상태를 반환합니다 */ getOnboardingState() { return { ...this.userState }; } arePrerequisitesMet(prerequisites) { return prerequisites.every(prereq => this.userState.completedFeatures.has(prereq)); } calculateFeaturePriority(feature, userPattern, preferences) { let priority = 0; // Calculate priority based on triggers for (const trigger of feature.triggers) { if (this.evaluateTrigger(trigger, null, userPattern)) { priority += trigger.weight; } } // Adjust based on user preferences if (preferences.learningStyle === 'exploratory' && feature.complexity === 'advanced') { priority *= 1.2; } else if (preferences.learningStyle === 'guided' && feature.complexity === 'beginner') { priority *= 1.1; } // Consider time constraints if (feature.estimatedTimeToValue <= this.userState.onboardingBudget) { priority *= 1.3; } else { priority *= 0.7; } // Boost features in user's preferred categories if (preferences.interestedAreas.includes(feature.category)) { priority *= 1.4; } return Math.min(priority, 1.0); } evaluateTrigger(trigger, context, userPattern) { switch (trigger.type) { case 'usage_pattern': return this.evaluateUsagePattern(trigger.condition, userPattern); case 'time_based': return this.evaluateTimeCondition(trigger.condition, userPattern); case 'context_based': return context ? this.evaluateContextCondition(trigger.condition, context) : false; case 'explicit_interest': return this.evaluateInterestCondition(trigger.condition, userPattern); default: return false; } } evaluateUsagePattern(condition, userPattern) { // Parse condition like "high_complexity_queries > 3" const parts = condition.split(/\s*(>|<|=|>=|<=)\s*/); if (parts.length !== 3) return false; const [metric, operator, valueStr] = parts; const value = parseInt(valueStr, 10); let actualValue = 0; switch (metric) { case 'high_complexity_queries': actualValue = userPattern.queryComplexityDistribution.filter(c => c.complexity > 0.7).length; break; case 'large_project_detected': actualValue = userPattern.contextMetrics.avgFilesPerQuery > 10 ? 1 : 0; break; case 'frequent_file_operations': actualValue = userPattern.toolUsage.fileOperations || 0; break; case 'verification_mode_used': actualValue = userPattern.toolUsage.collaborationFeatures || 0; break; case 'code_modification_frequency': actualValue = userPattern.toolUsage.codeModification || 0; break; case 'repeated_command_patterns': actualValue = userPattern.behaviorMetrics.commandRepetition; break; case 'sessions': actualValue = userPattern.sessionMetrics.totalSessions; break; default: return false; } switch (operator) { case '>': return actualValue > value; case '<': return actualValue < value; case '=': return actualValue === value; case '>=': return actualValue >= value; case '<=': return actualValue <= value; default: return false; } } evaluateTimeCondition(condition, userPattern) { const parts = condition.split(/\s*(>|<|=|>=|<=)\s*/); if (parts.length !== 3) return false; const [metric, operator, valueStr] = parts; const value = parseInt(valueStr, 10); let actualValue = 0; switch (metric) { case 'sessions': actualValue = userPattern.sessionMetrics.totalSessions; break; case 'days_since_start': actualValue = Math.floor((Date.now() - userPattern.sessionMetrics.firstSessionTime) / (1000 * 60 * 60 * 24)); break; default: return false; } switch (operator) { case '>': return actualValue > value; case '<': return actualValue < value; case '=': return actualValue === value; case '>=': return actualValue >= value; case '<=': return actualValue <= value; default: return false; } } evaluateContextCondition(condition, context) { switch (condition) { case 'security_related_query': return context.queryType === 'security' || !!context.keywords?.some(k => ['security', 'auth', 'password', 'token'].includes(k.toLowerCase())); case 'complex_technical_query': return context.complexity != null && context.complexity > 0.7; case 'test_directory_exists': return !!context.projectStructure?.some(path => path.includes('test') || path.includes('spec')); case 'ui_customization_mentioned': return !!context.keywords?.some(k => ['theme', 'color', 'ui', 'interface', 'style'].includes(k.toLowerCase())); default: return false; } } evaluateInterestCondition(condition, userPattern) { const recentQueries = userPattern.recentQueries.slice(-10); switch (condition) { case 'quality_concern_expressed': return recentQueries.some(q => q.text.toLowerCase().includes('correct') || q.text.toLowerCase().includes('sure') || q.text.toLowerCase().includes('double check')); case 'automation_interest_expressed': return recentQueries.some(q => q.text.toLowerCase().includes('automate') || q.text.toLowerCase().includes('script') || q.text.toLowerCase().includes('repeat')); default: return false; } } generateRecommendationReason(feature, userPattern) { const reasons = []; if (userPattern.sessionMetrics.totalSessions > 5) { reasons.push('경험이 쌓였으니 고급 기능을 사용해볼 시간입니다'); } if (feature.category === 'productivity' && userPattern.behaviorMetrics.commandRepetition > 3) { reasons.push('반복되는 작업 패턴이 감지되어 생산성 향상이 필요해 보입니다'); } if (feature.category === 'collaboration' && userPattern.sessionMetrics.avgSessionDuration > 30) { reasons.push('복잡한 작업을 많이 하시는 것 같아 품질 검증 기능이 도움이 될 것 같습니다'); } return reasons.length > 0 ? reasons[0] : `${feature.category} 영역에서 도움이 될 수 있는 기능입니다`; } generatePersonalizedMessage(feature, userPattern, preferences) { const skillLevel = this.userState.skillLevel; const timeToValue = feature.estimatedTimeToValue; let message = `💡 **${feature.name}**\n${feature.description}\n\n`; if (skillLevel === 'beginner') { message += `초보자에게 친화적인 기능으로, 약 ${timeToValue}분이면 익힐 수 있습니다. `; } else if (skillLevel === 'advanced') { message += `고급 사용자를 위한 강력한 기능으로, ${timeToValue}분 투자로 큰 효율성 향상을 얻을 수 있습니다. `; } if (userPattern.behaviorMetrics.errorRate > 0.1) { message += '현재 작업 중 발생하는 오류를 줄이는데 특히 도움이 될 것 같습니다.'; } else if (userPattern.sessionMetrics.avgSessionDuration > 45) { message += '긴 작업 시간을 단축하는데 큰 도움이 될 것 같습니다.'; } return message; } determineOptimalTiming(feature, userPattern) { if (feature.complexity === 'beginner') { return 'immediate'; } if (userPattern.sessionMetrics.avgSessionDuration < 15) { return 'next_session'; } if (feature.estimatedTimeToValue > this.userState.onboardingBudget) { return 'when_idle'; } return 'contextual'; } getTargetComplexity() { const completedCount = this.userState.completedFeatures.size; const sessionCount = this.historyManager.getUserPattern().sessionMetrics.totalSessions; if (completedCount >= 5 || sessionCount >= 20) { return 'advanced'; } else if (completedCount >= 2 || sessionCount >= 10) { return 'intermediate'; } else { return 'beginner'; } } isFeatureAppropriate(feature, targetComplexity, userPattern) { const complexityOrder = { 'beginner': 0, 'intermediate': 1, 'advanced': 2 }; const featureLevel = complexityOrder[feature.complexity]; const targetLevel = complexityOrder[targetComplexity]; // Allow features up to one level above target return featureLevel <= targetLevel + 1; } updateSkillLevel() { const completedCount = this.userState.completedFeatures.size; const sessionCount = this.historyManager.getUserPattern().sessionMetrics.totalSessions; if (completedCount >= 8 || sessionCount >= 30) { this.userState.skillLevel = 'advanced'; } else if (completedCount >= 4 || sessionCount >= 15) { this.userState.skillLevel = 'intermediate'; } else { this.userState.skillLevel = 'beginner'; } } } //# sourceMappingURL=onboarding-system.js.map