import { useEffect, useState } from 'react'; import { api } from '../api'; import { CapabilitySearch } from './CapabilitySearch'; import type { CapabilityResult, IndexQueryResponse, IndexStatusResponse } from '../types'; export function Explore({ refreshTick = 0 }: { refreshTick?: number }) { const [body, setBody] = useState<{ type: string; name: string; body: string } | null>(null); const [status, setStatus] = useState(null); const [query, setQuery] = useState('requirements'); const [queryResults, setQueryResults] = useState(null); const [graphName, setGraphName] = useState(''); const [scanDirs, setScanDirs] = useState('docs'); const [busy, setBusy] = useState(''); const [err, setErr] = useState(''); const [stale, setStale] = useState(false); const loadStatus = () => { api('/api/index/status').then((next) => { setStatus(next); setStale(false); setErr(''); }).catch((e) => { setErr((e as Error).message); setStale(true); }); }; useEffect(() => { loadStatus(); }, [refreshTick]); const show = (r: CapabilityResult) => { setBody(null); setErr(''); // Fetch by the discovered path — deterministic even when a name is shared by two // artifacts (e.g. two `aiwg-steward` agents), which would otherwise 502 (#1643). const qs = `type=${encodeURIComponent(r.type)}&name=${encodeURIComponent(r.name)}&path=${encodeURIComponent(r.path)}`; api<{ type: string; name: string; body: string }>(`/api/show?${qs}`) .then(setBody).catch((e) => setErr((e as Error).message)); }; const runIndexQuery = async () => { const q = query.trim(); if (!q) return; setBusy('query'); setErr(''); try { setQueryResults(await api(`/api/index/query?q=${encodeURIComponent(q)}&limit=8`)); } catch (e) { setErr((e as Error).message); } finally { setBusy(''); } }; const rebuild = async () => { setBusy('rebuild'); setErr(''); try { const result = await api<{ status: IndexStatusResponse }>('/api/index/rebuild', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ all: true }), }); setStatus(result.status); setQueryResults(null); } catch (e) { setErr((e as Error).message); } finally { setBusy(''); } }; const createGraph = async () => { if (!graphName.trim() || !scanDirs.trim()) return; setBusy('create'); setErr(''); try { await api('/api/index/graphs', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ name: graphName.trim(), scanDirs: scanDirs.split(',').map((value) => value.trim()).filter(Boolean) }), }); setGraphName(''); loadStatus(); } catch (e) { setErr((e as Error).message); } finally { setBusy(''); } }; const graphs = status?.graphs ?? []; const queryItems = queryResults?.results ?? []; return ( <>

Read-only catalog from the AIWG registry — display, not execution. To run a capability, inject it into a session (Actions/Sessions). Search modeled on the fortemi-react patterns.

{err &&

{err}{status ? ' — showing last-known index state.' : ''}

}

Live Index

{status ? `${status.summary.built}/${status.summary.total} graphs built, ${status.summary.missing} missing, ${status.summary.orphans} orphan dirs.` : 'Loading index status.'} {stale && status ? ' Stale: live refresh is temporarily unavailable.' : ''}

{graphs.map((g) => (
{g.name} {g.origin}

{g.built ? `${g.entries ?? 0} entries` : 'not built'}{g.ageHours !== null ? ` · ${g.ageHours}h old` : ''}

))}
{queryResults && (
{queryItems.length ? queryItems.map((r) => (
{r.title || r.path}

{r.summary || r.path}

{r.type || 'artifact'}{r.phase ? ` · ${r.phase}` : ''}{typeof r.score === 'number' ? ` · ${r.score.toFixed(2)}` : ''}
)) :

No index hits.

}
)}
{!body ?

Select a capability to inspect its definition.

: ( <>
{body.type} {body.name}
{body.body}
)}
); }