import React, { useState, useRef, useEffect } from 'react'; import { marked } from 'marked'; import { Send, Bot, User, Shield, Layers, Terminal, Play, HelpCircle, Cpu, Award, Activity, ChevronRight } from 'lucide-react'; import { apiUrl } from '../api'; const SUGGESTED = [ 'How does authentication work?', 'What is the architecture pattern?', 'Which API endpoints exist?', 'What are the main dependencies?', 'Where should I start reading code?', ]; const getAgentIcon = (name) => { const map = { PlannerAgent: , ArchitectureAgent: , SecurityAgent: , ApiAgent: , DependencyAgent: , QualityAgent: , OnboardingAgent: , }; return map[name] || ; }; const getAgentLabel = (name) => name.replace('Agent', ' Agent'); const renderMarkdown = (text) => { try { return { __html: marked.parse(text || '') }; } catch (e) { return { __html: text || '' }; } }; export default function RepositoryAssistant({ repo_id, apiKey }) { const [messages, setMessages] = useState([{ id: 'welcome', role: 'assistant', content: 'Hello! Ask me anything about this codebase — architecture, security, APIs, dependencies, or how to get started.', timeline: null, planner_decision: null, confidence: null, total_time_ms: null, references: [], retrieved_context: [] }]); const [input, setInput] = useState(''); const [loading, setLoading] = useState(false); const [sessionId, setSessionId] = useState(null); const [selectedId, setSelectedId] = useState('welcome'); const chatEndRef = useRef(null); useEffect(() => { chatEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages, loading]); const sendMessage = async (text) => { if (!text.trim() || loading) return; setLoading(true); const userMsg = { id: `u-${Date.now()}`, role: 'user', content: text.trim() }; setMessages(prev => [...prev, userMsg]); setSelectedId(userMsg.id); setInput(''); const headers = { 'Content-Type': 'application/json' }; if (apiKey) headers['x-gemini-key'] = apiKey; try { const res = await fetch(apiUrl('/api/chat'), { method: 'POST', headers, body: JSON.stringify({ repo_id, question: text.trim(), session_id: sessionId, }), }); const raw = await res.text(); let data; try { data = raw ? JSON.parse(raw) : {}; } catch { throw new Error(raw || `Server error (${res.status})`); } if (!res.ok) throw new Error(data.detail || 'Agent orchestration failed.'); if (data.session_id) setSessionId(data.session_id); const assistMsg = { id: `a-${Date.now()}`, role: 'assistant', content: data.answer || data.summary || (data.agent_contributions || []).join('\n\n') || 'No answer returned from agent.', summary: data.summary, agents_used: data.agents_used, confidence: data.confidence, references: data.references || [], agent_contributions: data.agent_contributions, planner_decision: data.planner_decision, timeline: data.timeline, total_time_ms: data.total_time_ms, retrieved_context: data.retrieved_context || [], rag_latency_ms: data.rag_latency_ms, session_id: data.session_id, }; setMessages(prev => [...prev, assistMsg]); setSelectedId(assistMsg.id); } catch (err) { const errMsg = { id: `e-${Date.now()}`, role: 'assistant', content: `**Error:** ${err.message}`, isError: true }; setMessages(prev => [...prev, errMsg]); } finally { setLoading(false); } }; const handleSubmit = (e) => { e.preventDefault(); sendMessage(input); }; const activeMsg = messages.find(m => m.id === selectedId && m.role === 'assistant') || [...messages].reverse().find(m => m.role === 'assistant' && !m.isError) || messages[0]; const confLevel = (c) => c >= 0.75 ? 'high' : c >= 0.5 ? 'medium' : 'low'; return (
{/* ── Chat Column ── */}
AI Repository Assistant
Multi-Agent Online
{/* Suggested questions — shown only when just the welcome message exists */} {messages.length === 1 && (
🤖

{messages[0].content}

Try one of these questions:

{SUGGESTED.map(q => ( ))}
)} {messages.slice(1).map(msg => (
msg.role === 'assistant' && setSelectedId(msg.id)} style={{ cursor: msg.role === 'assistant' ? 'pointer' : 'default' }} >
{msg.role === 'assistant' ? (
) : ( {msg.content} )}
{msg.role === 'assistant' && msg.confidence != null && ( {Math.round(msg.confidence * 100)}% confidence )} {msg.role === 'assistant' && msg.total_time_ms && ( {msg.total_time_ms}ms )} {msg.role === 'assistant' && msg.agents_used?.length > 0 && (
{msg.agents_used.map(a => ( {getAgentIcon(a)} {getAgentLabel(a)} ))}
)} {msg.role === 'assistant' && msg.retrieved_context?.length > 0 && ( {msg.retrieved_context.length} RAG chunks )}
{/* References */} {msg.references?.length > 0 && (
{msg.references.map((r, i) => ( {r} ))}
)}
))} {loading && (
Orchestrating agents...
)}