import React, { useState, useEffect } from 'react' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog' import { Badge } from '@/components/ui/badge' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Alert, AlertDescription } from '@/components/ui/alert' import { Progress } from '@/components/ui/progress' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Sparkles, Settings, Play, Pause, RotateCcw, CheckCircle, AlertCircle, Info, Target, ListTodo, Zap, Brain, TrendingUp, FileText, Tag, Calendar, Users, Shield, WifiOff } from 'lucide-react' interface Task { id: string serial: string title: string description?: string status: 'todo' | 'in_progress' | 'done' | 'blocked' priority: 'low' | 'medium' | 'high' | 'urgent' project: string category?: string created: string updated: string tags?: string[] memory_connections?: Array<{ memory_id: string memory_serial: string connection_type: string relevance: number }> } interface TaskEnhancementProps { tasks: Task[] currentProject?: string onTasksChange: () => void } interface EnhancementProgress { completed: number total: number currentTaskId?: string stage: 'analyzing' | 'generating' | 'completing' | 'idle' errors: Array<{ taskId: string; error: string }> startTime?: Date estimatedTimeRemaining?: number } interface TaskInsight { type: 'quality' | 'completion' | 'organization' | 'metadata' | 'relationships' title: string description: string tasks: Task[] action?: string severity: 'low' | 'medium' | 'high' } interface TaskQualityMetrics { hasGoodTitle: boolean hasDescription: boolean hasTags: boolean hasMemoryConnections: boolean isWellCategorized: boolean overallScore: number } export function TaskEnhancement({ tasks, currentProject = 'all', onTasksChange }: TaskEnhancementProps) { const [enhancementProgress, setEnhancementProgress] = useState({ completed: 0, total: 0, stage: 'idle', errors: [] }) const [isEnhancing, setIsEnhancing] = useState(false) const [showProgressDetails, setShowProgressDetails] = useState(false) const [selectedInsight, setSelectedInsight] = useState(null) const [enhancementSettings, setEnhancementSettings] = useState({ model: 'llama3.1:latest', batchSize: 5, skipExisting: true, enhanceDescriptions: true, generateTags: true, improveCategories: true }) const [ollamaStatus, setOllamaStatus] = useState<{ available: boolean models: string[] checking: boolean }>({ available: false, models: [], checking: false }) // Filter tasks based on current project const filteredTasks = currentProject === 'all' ? tasks : tasks.filter(task => task.project === currentProject) // Check Ollama status on component mount useEffect(() => { checkOllamaStatus() }, []) const checkOllamaStatus = async () => { setOllamaStatus(prev => ({ ...prev, checking: true })) try { const response = await fetch('/api/mcp-tools/check_ollama_status', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ show_models: true }) }) if (response.ok) { const result = await response.json() if (result.content) { // Parse the response text to extract status (same as OllamaEnhancement) const content = result.content const isAvailable = content.includes('✅ Ollama server is running') if (isAvailable) { // Extract models from response const models: string[] = [] const modelLines = content.split('\n').filter((line: string) => line.includes('→')) for (const line of modelLines) { const match = line.match(/→\s*(.+?)\s*\((.+?)\)/) if (match) { models.push(match[1].trim()) } } setOllamaStatus({ available: true, models, checking: false }) } else { setOllamaStatus({ available: false, models: [], checking: false }) } } else if (result.available !== undefined) { // Fallback to direct API response format const modelNames = result.models?.map((model: any) => typeof model === 'string' ? model : model.name ) || [] setOllamaStatus({ available: result.available || false, models: modelNames, checking: false }) } else { setOllamaStatus({ available: false, models: [], checking: false }) } } else { console.log('Response not ok:', response.status, response.statusText) setOllamaStatus({ available: false, models: [], checking: false }) } } catch (error) { console.error('Failed to check Ollama status:', error) setOllamaStatus({ available: false, models: [], checking: false }) } } // Calculate task quality metrics const calculateTaskQuality = (task: Task): TaskQualityMetrics => { const hasGoodTitle = task.title && task.title.length > 10 && !task.title.includes('undefined') const hasDescription = Boolean(task.description && task.description.length > 20) const hasTags = Boolean(task.tags && task.tags.length > 0) const hasMemoryConnections = Boolean(task.memory_connections && task.memory_connections.length > 0) const isWellCategorized = Boolean(task.category) const qualityFactors = [hasGoodTitle, hasDescription, hasTags, hasMemoryConnections, isWellCategorized] const overallScore = Math.round((qualityFactors.filter(Boolean).length / qualityFactors.length) * 100) return { hasGoodTitle, hasDescription, hasTags, hasMemoryConnections, isWellCategorized, overallScore } } // Generate insights about task collection const generateInsights = (): TaskInsight[] => { const insights: TaskInsight[] = [] // Find tasks that need better titles const poorTitles = filteredTasks.filter(task => { const quality = calculateTaskQuality(task) return !quality.hasGoodTitle }) if (poorTitles.length > 0) { insights.push({ type: 'quality', title: 'Poor Task Titles', description: `${poorTitles.length} tasks have titles that could be improved with AI enhancement.`, tasks: poorTitles, action: 'Enhance Titles', severity: 'medium' }) } // Find tasks without descriptions const noDescriptions = filteredTasks.filter(task => { const quality = calculateTaskQuality(task) return !quality.hasDescription }) if (noDescriptions.length > 0) { insights.push({ type: 'metadata', title: 'Missing Descriptions', description: `${noDescriptions.length} tasks lack detailed descriptions.`, tasks: noDescriptions, action: 'Generate Descriptions', severity: 'low' }) } // Find untagged tasks const untagged = filteredTasks.filter(task => { const quality = calculateTaskQuality(task) return !quality.hasTags }) if (untagged.length > 0) { insights.push({ type: 'organization', title: 'Untagged Tasks', description: `${untagged.length} tasks could benefit from AI-generated tags.`, tasks: untagged, action: 'Generate Tags', severity: 'low' }) } // Find tasks without memory connections const disconnected = filteredTasks.filter(task => { const quality = calculateTaskQuality(task) return !quality.hasMemoryConnections }) if (disconnected.length > 0) { insights.push({ type: 'relationships', title: 'Disconnected Tasks', description: `${disconnected.length} tasks have no memory connections.`, tasks: disconnected, action: 'Link Memories', severity: 'medium' }) } // Find low-quality tasks (overall score < 60%) const lowQuality = filteredTasks.filter(task => { const quality = calculateTaskQuality(task) return quality.overallScore < 60 }) if (lowQuality.length > 0) { insights.push({ type: 'quality', title: 'Low Quality Tasks', description: `${lowQuality.length} tasks have an overall quality score below 60%.`, tasks: lowQuality, action: 'Enhance All', severity: 'high' }) } return insights.sort((a, b) => { const severityOrder = { high: 3, medium: 2, low: 1 } return severityOrder[b.severity] - severityOrder[a.severity] }) } // Calculate overall statistics const getTaskStatistics = () => { const total = filteredTasks.length const qualityScores = filteredTasks.map(task => calculateTaskQuality(task).overallScore) const averageQuality = qualityScores.length > 0 ? Math.round(qualityScores.reduce((sum, score) => sum + score, 0) / qualityScores.length) : 0 const statusCounts = filteredTasks.reduce((counts, task) => { counts[task.status] = (counts[task.status] || 0) + 1 return counts }, {} as Record) const needsEnhancement = filteredTasks.filter(task => calculateTaskQuality(task).overallScore < 80 ).length return { total, averageQuality, statusCounts, needsEnhancement, enhancementPercentage: total > 0 ? Math.round(((total - needsEnhancement) / total) * 100) : 0 } } // Start memory linking for tasks const startMemoryLinking = async (insight: TaskInsight) => { const tasksToLink = insight.tasks if (tasksToLink.length === 0) { alert('No tasks need memory linking.') return } setIsEnhancing(true) setEnhancementProgress({ completed: 0, total: tasksToLink.length, stage: 'linking', errors: [], startTime: new Date() }) try { // Use the new batch memory linking endpoint const response = await fetch('/api/mcp-tools/batch_link_memories', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ task_ids: tasksToLink.map(t => t.id), limit: tasksToLink.length }) }) if (response.ok) { const result = await response.json() if (result.content) { // Parse results for stats const successMatch = result.content.match(/✅ Successfully processed: (\d+)/) const failedMatch = result.content.match(/❌ Failed to process: (\d+)/) const connectionsMatch = result.content.match(/🧠 Total memories linked: (\d+)/) if (successMatch && failedMatch) { const successful = parseInt(successMatch[1]) const failed = parseInt(failedMatch[1]) const connections = connectionsMatch ? parseInt(connectionsMatch[1]) : 0 setEnhancementProgress(prev => ({ ...prev, completed: successful + failed, stage: 'completed', errors: failed > 0 ? [`${failed} tasks failed to link memories`] : [] })) } } // Refresh tasks after linking onTasksChange() // Set linking as complete setTimeout(() => { setIsEnhancing(false) setEnhancementProgress(prev => ({ ...prev, stage: 'idle' })) }, 2000) } else { throw new Error(`Memory linking failed: ${response.status} ${response.statusText}`) } } catch (error) { console.error('Memory linking failed:', error) setEnhancementProgress(prev => ({ ...prev, errors: [...prev.errors, 'Memory linking operation failed'] })) setIsEnhancing(false) } } // Start tag generation for tasks const startTagGeneration = async (insight?: TaskInsight) => { if (!ollamaStatus.available) { alert('Ollama is not available. Please ensure Ollama is running and try again.') return } const tasksToTag = insight?.tasks || filteredTasks.filter(task => !task.tags || task.tags.length === 0 ) if (tasksToTag.length === 0) { alert('No tasks need tag generation.') return } setIsEnhancing(true) setEnhancementProgress({ completed: 0, total: tasksToTag.length, stage: 'generating tags', errors: [], startTime: new Date() }) try { // Use the batch enhancement endpoint with specific settings const response = await fetch('/api/mcp-tools/batch_enhance_tasks_ollama', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ project: currentProject === 'all' ? undefined : currentProject, limit: tasksToTag.length, model: enhancementSettings.model, batchSize: enhancementSettings.batchSize, skipExisting: false, // Force tag generation even if task has title category: 'all', status: 'all' }) }) if (response.ok) { const result = await response.json() if (result.content) { // Parse results for stats const successMatch = result.content.match(/✅ Successfully enhanced: (\d+)/) const failedMatch = result.content.match(/❌ Failed to enhance: (\d+)/) const totalMatch = result.content.match(/📊 Total processed: (\d+)/) if (successMatch && failedMatch && totalMatch) { const total = parseInt(totalMatch[1]) const successful = parseInt(successMatch[1]) const failed = parseInt(failedMatch[1]) setEnhancementProgress(prev => ({ ...prev, completed: total, stage: 'completed', errors: failed > 0 ? [`${failed} tasks failed tag generation`] : [] })) } } // Refresh tasks after tag generation onTasksChange() // Set generation as complete setTimeout(() => { setIsEnhancing(false) setEnhancementProgress(prev => ({ ...prev, stage: 'idle' })) }, 2000) } else { throw new Error(`Tag generation failed: ${response.status} ${response.statusText}`) } } catch (error) { console.error('Tag generation failed:', error) setEnhancementProgress(prev => ({ ...prev, errors: [...prev.errors, 'Tag generation operation failed'] })) setIsEnhancing(false) } } // Start batch enhancement const startBatchEnhancement = async (insight?: TaskInsight) => { // Handle specific insight actions if (insight?.action === 'Link Memories') { return await startMemoryLinking(insight) } if (insight?.action === 'Generate Tags') { return await startTagGeneration(insight) } if (!ollamaStatus.available) { alert('Ollama is not available. Please ensure Ollama is running and try again.') return } const tasksToEnhance = insight ? insight.tasks : filteredTasks.filter(task => calculateTaskQuality(task).overallScore < 80 ) if (tasksToEnhance.length === 0) { alert('No tasks need enhancement.') return } setIsEnhancing(true) setEnhancementProgress({ completed: 0, total: tasksToEnhance.length, stage: 'analyzing', errors: [], startTime: new Date() }) try { const response = await fetch('/api/mcp-tools/batch_enhance_tasks_ollama', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ project: currentProject === 'all' ? undefined : currentProject, limit: tasksToEnhance.length, model: enhancementSettings.model, batchSize: enhancementSettings.batchSize, skipExisting: enhancementSettings.skipExisting }) }) if (response.ok) { const result = await response.json() if (result.content) { // Parse results for stats const successMatch = result.content.match(/✅ Successfully enhanced: (\d+)/) const failedMatch = result.content.match(/❌ Failed to enhance: (\d+)/) const totalMatch = result.content.match(/📊 Total processed: (\d+)/) if (successMatch && failedMatch && totalMatch) { const total = parseInt(totalMatch[1]) const successful = parseInt(successMatch[1]) const failed = parseInt(failedMatch[1]) setEnhancementProgress(prev => ({ ...prev, completed: total, stage: 'completed', errors: failed > 0 ? [`${failed} tasks failed to enhance`] : [] })) } } // Refresh tasks after enhancement onTasksChange() // Set enhancement as complete setTimeout(() => { setIsEnhancing(false) setEnhancementProgress(prev => ({ ...prev, stage: 'idle' })) }, 2000) } else { throw new Error(`Enhancement request failed: ${response.status} ${response.statusText}`) } } catch (error) { console.error('Enhancement failed:', error) setEnhancementProgress(prev => ({ ...prev, errors: [...prev.errors, { taskId: 'batch', error: error.message }], stage: 'idle' })) setIsEnhancing(false) } } const insights = generateInsights() const statistics = getTaskStatistics() return (
{/* Header */}

Task Enhancement

AI-powered task optimization and quality improvement

{/* Ollama Status Alert */} {!ollamaStatus.available && !ollamaStatus.checking && ( Ollama is not available. Task enhancement requires a local Ollama installation.
Please install and start Ollama, then click "Check Status" to reconnect.
)} Overview Insights Batch Operations Settings {/* Statistics Cards */}
Total Tasks
{statistics.total}

{currentProject === 'all' ? 'All projects' : currentProject}

Quality Score
{statistics.averageQuality}%
Enhancement Rate
{statistics.enhancementPercentage}%

{statistics.total - statistics.needsEnhancement} of {statistics.total} enhanced

Needs Enhancement
{statistics.needsEnhancement}
{/* Progress Display */} {isEnhancing && ( Enhancement in Progress
Stage: {enhancementProgress.stage} {enhancementProgress.completed} / {enhancementProgress.total}
{enhancementProgress.errors.length > 0 && (
{enhancementProgress.errors.length} errors occurred
)}
)}
{insights.length === 0 ? (

All Tasks Look Great!

Your tasks are well-organized and don't need immediate enhancement.

) : (
{insights.map((insight, index) => ( setSelectedInsight(insight)} >
{insight.type === 'quality' && } {insight.type === 'metadata' && } {insight.type === 'organization' && } {insight.type === 'relationships' && } {insight.title}
{insight.severity} {insight.tasks.length} tasks

{insight.description}

{insight.action && ( )}
))}
)}
Batch Enhancement Operations

Enhancement Options

Enhancement Settings
setEnhancementSettings(prev => ({ ...prev, batchSize: parseInt(e.target.value) }))} className="w-full" />
Number of tasks to process simultaneously
) }