UNPKG

shipdeck

Version:

Ship MVPs in 48 hours. Fix bugs in 30 seconds. The command deck for developers who ship.

617 lines (499 loc) 18 kB
/** * AI Integration for Component Marketplace * Enables agents to discover, evaluate, and integrate components automatically */ const { ComponentRegistry } = require('./registry'); const EventEmitter = require('events'); class MarketplaceAI extends EventEmitter { constructor(options = {}) { super(); this.registry = options.registry || new ComponentRegistry(); this.aiManager = options.aiManager; // Component selection intelligence this.selectionCriteria = { preferTested: true, preferWCAG: true, maxComplexity: 'complex', minRating: 4.0 }; // Usage patterns for learning this.usagePatterns = new Map(); this.successfulIntegrations = new Map(); } /** * Initialize AI integration */ async initialize() { await this.registry.initialize(); console.log(`🤖 Marketplace AI initialized with ${this.registry.components.size} components`); return this; } /** * Analyze task and recommend components */ async analyzeTask(task, context = {}) { console.log(`🔍 Analyzing task: "${task}"`); const analysis = { task, context, requiredComponents: [], optionalComponents: [], dependencies: new Set(), estimatedTime: 0, complexity: 'simple' }; // Parse task for component needs const taskLower = task.toLowerCase(); // Authentication detection if (this.needsAuthentication(taskLower)) { const authComponents = await this.selectAuthComponents(taskLower, context); analysis.requiredComponents.push(...authComponents); } // Dashboard detection if (this.needsDashboard(taskLower)) { const dashboardComponents = await this.selectDashboardComponents(taskLower, context); analysis.requiredComponents.push(...dashboardComponents); } // Payment detection if (this.needsPayment(taskLower)) { const paymentComponents = await this.selectPaymentComponents(taskLower, context); analysis.requiredComponents.push(...paymentComponents); } // Data display detection if (this.needsDataDisplay(taskLower)) { const dataComponents = await this.selectDataComponents(taskLower, context); analysis.requiredComponents.push(...dataComponents); } // Forms detection if (this.needsForms(taskLower)) { const formComponents = await this.selectFormComponents(taskLower, context); analysis.requiredComponents.push(...formComponents); } // Landing page detection if (this.needsLandingPage(taskLower)) { const landingComponents = await this.selectLandingComponents(taskLower, context); analysis.optionalComponents.push(...landingComponents); } // UI enhancements const uiComponents = await this.selectUIEnhancements(analysis.requiredComponents); analysis.optionalComponents.push(...uiComponents); // Calculate dependencies for (const component of [...analysis.requiredComponents, ...analysis.optionalComponents]) { component.dependencies.forEach(dep => analysis.dependencies.add(dep)); } // Calculate total integration time analysis.estimatedTime = analysis.requiredComponents.reduce( (sum, c) => sum + c.estimatedIntegrationTime, 0 ); // Determine overall complexity analysis.complexity = this.calculateOverallComplexity(analysis.requiredComponents); // Generate integration plan analysis.integrationPlan = this.generateIntegrationPlan(analysis); return analysis; } /** * Auto-select best component for a need */ async autoSelectComponent(need, context = {}) { // Search for matching components const candidates = this.registry.searchComponents(need, { testedOnly: this.selectionCriteria.preferTested, wcagOnly: this.selectionCriteria.preferWCAG, sortBy: 'relevance' }); if (candidates.length === 0) { return null; } // Score each candidate const scoredCandidates = candidates.map(component => ({ component, score: this.scoreComponent(component, need, context) })); // Sort by score scoredCandidates.sort((a, b) => b.score - a.score); // Return best match const selected = scoredCandidates[0].component; console.log(`✅ Selected: ${selected.name} (score: ${scoredCandidates[0].score})`); // Track selection this.trackSelection(selected, need, context); return selected; } /** * Score a component for selection */ scoreComponent(component, need, context) { let score = 0; // Base score from rating score += component.rating * 20; // Bonus for tested components if (component.tested) score += 10; // Bonus for WCAG compliance if (component.wcagCompliant) score += 10; // Bonus for popularity score += Math.min(component.downloads * 0.01, 10); // Complexity penalty/bonus based on context if (context.preferSimple && component.complexity === 'simple') { score += 5; } else if (context.preferComplex && component.complexity === 'complex') { score += 5; } // Framework match bonus if (context.framework && component.framework === context.framework) { score += 15; } // Previous success bonus const successRate = this.getSuccessRate(component.id); score += successRate * 10; // Time constraint penalty if (context.timeLimit && component.estimatedIntegrationTime > context.timeLimit) { score -= 20; } return score; } /** * Generate integration code for selected components */ async generateIntegrationCode(components, projectContext = {}) { const integration = { imports: [], setup: [], usage: [], styles: [], dependencies: new Set() }; for (const component of components) { // Generate import statement integration.imports.push( `import { ${component.name.replace(/\s+/g, '')} } from '@/components/${component.category}/${component.id}';` ); // Generate setup code if needed if (this.needsSetup(component)) { integration.setup.push(this.generateSetupCode(component)); } // Generate usage example integration.usage.push(this.generateUsageCode(component, projectContext)); // Collect dependencies component.dependencies.forEach(dep => integration.dependencies.add(dep)); // Add styles if (component.hasStyles) { integration.styles.push( `import '${component.id}.module.css';` ); } } return this.formatIntegrationCode(integration); } /** * Validate component compatibility */ validateCompatibility(components, projectContext) { const issues = []; // Check framework compatibility const frameworks = new Set(components.map(c => c.framework)); if (frameworks.size > 1) { issues.push({ type: 'warning', message: 'Multiple frameworks detected. Ensure compatibility.' }); } // Check dependency conflicts const dependencyVersions = new Map(); for (const component of components) { for (const dep of component.dependencies) { if (dependencyVersions.has(dep)) { const existing = dependencyVersions.get(dep); if (existing !== component.dependencyVersions?.[dep]) { issues.push({ type: 'warning', message: `Potential version conflict for ${dep}` }); } } dependencyVersions.set(dep, component.dependencyVersions?.[dep]); } } // Check total complexity const totalComplexity = this.calculateOverallComplexity(components); if (totalComplexity === 'complex' && projectContext.experienceLevel === 'beginner') { issues.push({ type: 'info', message: 'Selected components may require advanced knowledge' }); } return { compatible: issues.filter(i => i.type === 'error').length === 0, issues }; } /** * Track component usage for learning */ trackSelection(component, need, context) { const key = `${need}-${context.framework || 'default'}`; if (!this.usagePatterns.has(key)) { this.usagePatterns.set(key, []); } this.usagePatterns.get(key).push({ componentId: component.id, timestamp: Date.now(), context }); } /** * Track integration success/failure */ trackIntegrationResult(componentId, success, details = {}) { if (!this.successfulIntegrations.has(componentId)) { this.successfulIntegrations.set(componentId, { successes: 0, failures: 0, details: [] }); } const tracking = this.successfulIntegrations.get(componentId); if (success) { tracking.successes++; } else { tracking.failures++; } tracking.details.push({ success, timestamp: Date.now(), ...details }); // Update component rating based on success rate this.updateComponentRating(componentId); } /** * Get success rate for a component */ getSuccessRate(componentId) { const tracking = this.successfulIntegrations.get(componentId); if (!tracking) return 0.5; // Default 50% for unknown const total = tracking.successes + tracking.failures; if (total === 0) return 0.5; return tracking.successes / total; } /** * Update component rating based on usage */ updateComponentRating(componentId) { const component = this.registry.getComponent(componentId); if (!component) return; const successRate = this.getSuccessRate(componentId); const tracking = this.successfulIntegrations.get(componentId); const sampleSize = tracking.successes + tracking.failures; // Only update rating after sufficient usage if (sampleSize >= 10) { // Weighted average of original rating and success rate const weight = Math.min(sampleSize / 100, 0.5); // Max 50% weight for usage component.rating = component.rating * (1 - weight) + successRate * 5 * weight; } } // Detection methods needsAuthentication(task) { const authKeywords = ['login', 'signup', 'auth', 'user', 'account', 'signin', 'register']; return authKeywords.some(keyword => task.includes(keyword)); } needsDashboard(task) { const dashboardKeywords = ['dashboard', 'admin', 'analytics', 'metrics', 'charts']; return dashboardKeywords.some(keyword => task.includes(keyword)); } needsPayment(task) { const paymentKeywords = ['payment', 'billing', 'subscription', 'checkout', 'stripe', 'purchase']; return paymentKeywords.some(keyword => task.includes(keyword)); } needsDataDisplay(task) { const dataKeywords = ['table', 'list', 'grid', 'data', 'records', 'items']; return dataKeywords.some(keyword => task.includes(keyword)); } needsForms(task) { const formKeywords = ['form', 'input', 'submit', 'validation', 'field']; return formKeywords.some(keyword => task.includes(keyword)); } needsLandingPage(task) { const landingKeywords = ['landing', 'hero', 'marketing', 'pricing', 'features']; return landingKeywords.some(keyword => task.includes(keyword)); } // Component selection methods async selectAuthComponents(task, context) { const components = []; if (task.includes('login')) { const loginForm = await this.autoSelectComponent('login form', context); if (loginForm) components.push(loginForm); } if (task.includes('signup') || task.includes('register')) { const signupFlow = await this.autoSelectComponent('signup flow', context); if (signupFlow) components.push(signupFlow); } if (task.includes('oauth') || task.includes('social')) { const oauthProviders = await this.autoSelectComponent('oauth providers', context); if (oauthProviders) components.push(oauthProviders); } return components; } async selectDashboardComponents(task, context) { const components = []; const analyticsBoard = await this.autoSelectComponent('analytics dashboard', context); if (analyticsBoard) components.push(analyticsBoard); const sidebar = await this.autoSelectComponent('sidebar navigation', context); if (sidebar) components.push(sidebar); return components; } async selectPaymentComponents(task, context) { const components = []; if (task.includes('checkout') || task.includes('payment')) { const checkout = await this.autoSelectComponent('stripe checkout', context); if (checkout) components.push(checkout); } if (task.includes('subscription')) { const subscriptionManager = await this.autoSelectComponent('subscription manager', context); if (subscriptionManager) components.push(subscriptionManager); } return components; } async selectDataComponents(task, context) { const components = []; if (task.includes('table')) { const dataTable = await this.autoSelectComponent('data table', context); if (dataTable) components.push(dataTable); } if (task.includes('infinite') || task.includes('scroll')) { const infiniteScroll = await this.autoSelectComponent('infinite scroll', context); if (infiniteScroll) components.push(infiniteScroll); } return components; } async selectFormComponents(task, context) { const components = []; if (task.includes('multi') || task.includes('wizard')) { const multiStepForm = await this.autoSelectComponent('multi-step form', context); if (multiStepForm) components.push(multiStepForm); } if (task.includes('upload') || task.includes('file')) { const fileUpload = await this.autoSelectComponent('file upload', context); if (fileUpload) components.push(fileUpload); } return components; } async selectLandingComponents(task, context) { const components = []; const hero = await this.autoSelectComponent('hero section', context); if (hero) components.push(hero); if (task.includes('pricing')) { const pricingTable = await this.autoSelectComponent('pricing table', context); if (pricingTable) components.push(pricingTable); } return components; } async selectUIEnhancements(requiredComponents) { const enhancements = []; // Add notifications if forms are present if (requiredComponents.some(c => c.category === 'forms')) { const notifications = await this.autoSelectComponent('notification system'); if (notifications) enhancements.push(notifications); } // Add modals if complex interactions present if (requiredComponents.length > 3) { const modals = await this.autoSelectComponent('modal dialog'); if (modals) enhancements.push(modals); } return enhancements; } // Helper methods calculateOverallComplexity(components) { if (components.some(c => c.complexity === 'complex')) return 'complex'; if (components.some(c => c.complexity === 'medium')) return 'medium'; return 'simple'; } needsSetup(component) { return component.category === 'payment' || component.category === 'authentication' || component.dependencies.length > 3; } generateSetupCode(component) { const setupTemplates = { 'payment': `// Initialize Stripe const stripe = new Stripe(process.env.STRIPE_PUBLIC_KEY);`, 'authentication': `// Configure authentication const authConfig = { providers: ['google', 'github'], callbacks: { signIn: async (user) => { /* ... */ }, signOut: async () => { /* ... */ } } };` }; return setupTemplates[component.category] || '// Component setup'; } generateUsageCode(component, context) { return `// Using ${component.name} <${component.name.replace(/\s+/g, '')} ${context.props ? Object.entries(context.props).map(([k, v]) => `${k}="${v}"`).join('\n ') : ''} />`; } formatIntegrationCode(integration) { return `// Auto-generated component integration ${integration.imports.join('\n')} ${integration.styles.join('\n')} // Setup ${integration.setup.join('\n\n')} // Usage export function IntegratedComponents() { return ( <> ${integration.usage.join('\n ')} </> ); } // Required dependencies: // ${Array.from(integration.dependencies).join(', ')} `; } generateIntegrationPlan(analysis) { const plan = { steps: [], estimatedTime: analysis.estimatedTime, dependencies: Array.from(analysis.dependencies) }; // Step 1: Install dependencies if (analysis.dependencies.size > 0) { plan.steps.push({ order: 1, action: 'install-dependencies', command: `npm install ${Array.from(analysis.dependencies).join(' ')}`, time: 30 }); } // Step 2: Install required components for (const component of analysis.requiredComponents) { plan.steps.push({ order: plan.steps.length + 1, action: 'install-component', component: component.id, command: `shipdeck install ${component.id}`, time: component.estimatedIntegrationTime }); } // Step 3: Install optional components for (const component of analysis.optionalComponents) { plan.steps.push({ order: plan.steps.length + 1, action: 'install-component-optional', component: component.id, command: `shipdeck install ${component.id}`, time: component.estimatedIntegrationTime, optional: true }); } // Step 4: Integration and testing plan.steps.push({ order: plan.steps.length + 1, action: 'integrate-and-test', command: 'npm test', time: 60 }); return plan; } } module.exports = { MarketplaceAI };