#!/usr/bin/env node /** * Export unified dashboard data to JSON by reading files directly: * - .kilo/agents/*.md (YAML frontmatter: model, mode, color, description) * - kilo-meta.json (model assignments, categories, fallback info) * - model-benchmarks-verified.json (IF scores, context window) * - agent-versions.json (real history with dates, commits, reasons) * * Run: node agent-evolution/scripts/export-data-direct.cjs */ const fs = require('fs'); const path = require('path'); const META_FILE = path.join(__dirname, '../../kilo-meta.json'); const BENCHMARK_FILE = path.join(__dirname, '../data/model-benchmarks-verified.json'); const AGENTS_DIR = path.join(__dirname, '../../.kilo/agents'); const HISTORY_FILE = path.join(__dirname, '../data/agent-versions.json'); const OUTPUT_FILE = path.join(__dirname, '../data/evolution-export.json'); // ---------- YAML frontmatter parser (lightweight, no deps) ---------- function parseYamlFrontmatter(text) { if (!text.startsWith('---')) return null; const end = text.indexOf('---', 4); if (end === -1) return null; const lines = text.slice(4, end).trim().split('\n'); const fm = {}; for (const raw of lines) { const line = raw.trim(); if (!line || line.startsWith('#')) continue; const m = line.match(/^([a-z_]+):\s*(.*)$/); if (!m) continue; const key = m[1]; let val = m[2].replace(/"/g, '').trim(); fm[key] = val; } return fm; } // ---------- Compute composite score (v2 formula) ---------- function computeScore(modelName, bmMap) { const key = Object.keys(bmMap).find(k => modelName.includes(k)); if (!key) return 60; const m = bmMap[key]; let score = (m.if_score || 70) * 0.85; const ctx = m.context_window || 128; score += ctx >= 1000 ? 15 : ctx >= 256 ? 8 : 4; return Math.round(Math.min(100, score)); } // ---------- Main ---------- try { // Load model benchmarks console.log('Reading benchmarks from:', BENCHMARK_FILE); const bmData = JSON.parse(fs.readFileSync(BENCHMARK_FILE, 'utf-8')); const bmMap = {}; for (const m of bmData.models || []) { bmMap[m.id] = { if_score: m.if_score, context_window: typeof m.context_window === 'number' ? m.context_window : parseInt(String(m.context_window).replace(/\D/g, '')) || 128, organization: m.organization, parameters: m.parameters }; } const modelIds = Object.keys(bmMap); // Load meta console.log('Reading meta from:', META_FILE); const metaRaw = JSON.parse(fs.readFileSync(META_FILE, 'utf-8')); const meta = metaRaw.agents || {}; // Load agent history (real data from Git/Gitea with dates, commits, reasons) console.log('Reading history from:', HISTORY_FILE); let historyData = { agents: {} }; try { historyData = JSON.parse(fs.readFileSync(HISTORY_FILE, 'utf-8')); } catch (e) { console.warn(' No history file found, using empty history'); } // Scan agent files console.log('Reading agents from:', AGENTS_DIR); const agentFiles = fs.readdirSync(AGENTS_DIR).filter(f => f.endsWith('.md')); const agents = {}; let withHistory = 0; for (const fn of agentFiles) { const text = fs.readFileSync(path.join(AGENTS_DIR, fn), 'utf-8'); const fm = parseYamlFrontmatter(text); if (!fm) continue; const name = fn.replace('.md', ''); const metaAgent = meta[name] || {}; const model = (fm.model || metaAgent.model || 'unknown'); const provider = model.startsWith('ollama-cloud/') ? 'Ollama Cloud' : 'Unknown'; const category = metaAgent.category || 'General'; const mode = fm.mode || metaAgent.mode || fm.subagent ? 'subagent' : 'subagent'; const description = fm.description || metaAgent.description || ''; const color = (fm.color || metaAgent.color || '#6B7280'); const fitScore = computeScore(model, bmMap); // Real history from agent-versions.json const agentHistory = historyData.agents?.[name]?.history || []; if (agentHistory.length > 0) { withHistory++; } // Compute heatmap scores for all models const heatmapScores = {}; for (const mid of modelIds) { heatmapScores[mid] = computeScore(`ollama-cloud/${mid}`, bmMap); } // Generate recommendations: compare current model vs best alternative let bestModel = model; let bestScore = fitScore; for (const mid of modelIds) { const s = computeScore(`ollama-cloud/${mid}`, bmMap); if (s > bestScore) { bestScore = s; bestModel = mid; } } const recommendations = []; if (bestScore > fitScore + 2 && !model.includes(bestModel)) { recommendations.push({ priority: (bestScore - fitScore >= 8) ? 'critical' : (bestScore - fitScore >= 5 ? 'high' : 'medium'), target: `ollama-cloud/${bestModel}`, reason: `${name} could improve from ${model} to ${bestModel}. Score: ${fitScore} → ${bestScore} (+${bestScore - fitScore}). Verified IF scores from artificialanalysis.ai.`, score_before: fitScore, score_after: bestScore, score_delta: bestScore - fitScore, applied: false }); } agents[name] = { current: { description, mode, model, provider, color, category, capabilities: metaAgent.capabilities || [], recommendations, benchmark: { fit_score: fitScore, instruction_following: bmMap[model.split('/').pop()]?.if_score || 0 } }, history: agentHistory, heatmap_scores: heatmapScores, performance_log: historyData.agents?.[name]?.performance_log || [] }; } const totalAgents = Object.keys(agents).length; const pendingRecs = Object.values(agents).reduce((s, a) => s + a.current.recommendations.length, 0); const unifiedData = { "$schema": "./data/evolution.schema.json", "version": "2.1.0", "lastUpdated": new Date().toISOString(), "agents": agents, "model_benchmarks": bmMap, "evolution_metrics": { "total_agents": totalAgents, "agents_with_history": withHistory, "pending_recommendations": pendingRecs, "last_sync": new Date().toISOString(), "sync_sources": [".kilo/agents/*.md", "kilo-meta.json", "model-benchmarks-verified.json"] } }; console.log(`Unified data: ${totalAgents} agents, ${modelIds.length} models, ${pendingRecs} recommendations`); // Write to JSON file fs.writeFileSync(OUTPUT_FILE, JSON.stringify(unifiedData, null, 2)); console.log('\nExported data to JSON'); console.log(' Output:', OUTPUT_FILE); console.log(' Size:', (fs.statSync(OUTPUT_FILE).size / 1024).toFixed(1), 'KB'); // Also copy to data/evolution.json for the container to consume fs.copyFileSync(OUTPUT_FILE, path.join(__dirname, '../data/evolution.json')); console.log('Also written:', path.join(__dirname, '../data/evolution.json')); // Return the data for use by other scripts module.exports = unifiedData; } catch (error) { console.error('Error:', error.message); console.error(error.stack); process.exit(1); }