- real-fit.html: API-driven research dashboard with agent/model heatmap, detail modal with score breakdown and evaluator commentary - api.py: FastAPI backend serving /api/real-fit-report (dynamic from SQLite), /api/research, /api/evolve-agent/start - rebuild-report.py: generates real-fit-report.json from SQLite DB for static fallback - docker-compose.yml: add evolution-api service (Python 3.12, uvicorn) for research endpoints - index.standalone.html: sync with dashboard data updates - archive/index.html: standalone dashboard snapshot (263KB) - .gitignore: exclude *.db, research-jobs.json from tracking
139 lines
4.5 KiB
JavaScript
139 lines
4.5 KiB
JavaScript
const fs = require('fs');
|
|
|
|
function parseFrontmatter(content) {
|
|
if (!content.startsWith('---')) return null;
|
|
const end = content.indexOf('---', 3);
|
|
if (end === -1) return null;
|
|
const fm = content.slice(3, end).trim();
|
|
const data = {};
|
|
for (const line of fm.split('\n')) {
|
|
const m = line.match(/^(\w+):\s*(.+)$/);
|
|
if (m) data[m[1]] = m[2].trim();
|
|
}
|
|
return data;
|
|
}
|
|
|
|
function stripComments(str) {
|
|
// Remove single-line comments, but not inside strings
|
|
return str.replace(/\/\/.*$/gm, '');
|
|
}
|
|
|
|
const agents = [];
|
|
const commands = [];
|
|
const issues = [];
|
|
|
|
// 1. Parse agent .md files
|
|
for (const f of fs.readdirSync('.kilo/agents').filter(f => f.endsWith('.md'))) {
|
|
const content = fs.readFileSync('.kilo/agents/' + f, 'utf8');
|
|
const fm = parseFrontmatter(content);
|
|
if (fm && fm.model) {
|
|
agents.push({
|
|
name: f.replace('.md', ''),
|
|
model: fm.model,
|
|
mode: fm.mode || 'subagent',
|
|
source: '.kilo/agents/' + f,
|
|
description: fm.description || ''
|
|
});
|
|
}
|
|
}
|
|
|
|
// 2. Parse command .md files
|
|
for (const f of fs.readdirSync('.kilo/commands').filter(f => f.endsWith('.md'))) {
|
|
const content = fs.readFileSync('.kilo/commands/' + f, 'utf8');
|
|
const fm = parseFrontmatter(content);
|
|
if (fm && fm.model) {
|
|
commands.push({
|
|
name: f.replace('.md', ''),
|
|
model: fm.model,
|
|
mode: fm.mode || 'command',
|
|
source: '.kilo/commands/' + f,
|
|
description: fm.description || ''
|
|
});
|
|
}
|
|
}
|
|
|
|
// 3. Parse kilo-meta.json
|
|
const meta = JSON.parse(fs.readFileSync('kilo-meta.json', 'utf8'));
|
|
for (const a of agents) {
|
|
const m = meta.agents?.[a.name];
|
|
if (m) {
|
|
a.metaModel = m.model;
|
|
if (a.model !== m.model) issues.push(`AGENT ${a.name}: .md=${a.model} vs meta=${m.model}`);
|
|
}
|
|
}
|
|
for (const c of commands) {
|
|
const m = meta.commands?.[c.name];
|
|
if (m) {
|
|
c.metaModel = m.model;
|
|
if (c.model !== m.model) issues.push(`CMD ${c.name}: .md=${c.model} vs meta=${m.model}`);
|
|
}
|
|
}
|
|
|
|
// 4. Parse .kilo/kilo.jsonc
|
|
const dotKiloRaw = stripComments(fs.readFileSync('.kilo/kilo.jsonc', 'utf8'));
|
|
const dotKilo = JSON.parse(dotKiloRaw);
|
|
for (const [name, cfg] of Object.entries(dotKilo.agent || {})) {
|
|
if (!cfg.model) continue;
|
|
const agent = agents.find(a => a.name === name);
|
|
if (agent) {
|
|
agent.kiloModel = cfg.model;
|
|
if (agent.model !== cfg.model) issues.push(`AGENT ${name}: .md=${agent.model} vs .kilo/kilo.jsonc=${cfg.model}`);
|
|
}
|
|
}
|
|
|
|
// 5. Parse root kilo.jsonc
|
|
const rootKiloRaw = stripComments(fs.readFileSync('kilo.jsonc', 'utf8'));
|
|
const rootKilo = JSON.parse(rootKiloRaw);
|
|
for (const [name, cfg] of Object.entries(rootKilo.agent || {})) {
|
|
if (!cfg.model) continue;
|
|
const cmd = commands.find(c => c.name === name);
|
|
if (cmd) {
|
|
cmd.rootModel = cfg.model;
|
|
if (cmd.model !== cfg.model) issues.push(`CMD ${name}: .md=${cmd.model} vs kilo.jsonc=${cfg.model}`);
|
|
}
|
|
}
|
|
|
|
// 6. Check non-ollama
|
|
const nonOllama = [];
|
|
for (const a of agents) if (!a.model.startsWith('ollama-cloud/')) nonOllama.push({type:'agent', name:a.name, model:a.model});
|
|
for (const c of commands) if (!c.model.startsWith('ollama-cloud/')) nonOllama.push({type:'command', name:c.name, model:c.model});
|
|
|
|
// 7. Summary by model
|
|
const modelStats = {};
|
|
for (const a of agents) modelStats[a.model] = (modelStats[a.model] || 0) + 1;
|
|
for (const c of commands) modelStats[c.model] = (modelStats[c.model] || 0) + 1;
|
|
|
|
const state = {
|
|
generated: new Date().toISOString(),
|
|
totalAgents: agents.length,
|
|
totalCommands: commands.length,
|
|
allOllama: nonOllama.length === 0,
|
|
modelDistribution: modelStats,
|
|
agents: agents.sort((a,b) => a.name.localeCompare(b.name)),
|
|
commands: commands.sort((a,b) => a.name.localeCompare(b.name)),
|
|
issues: issues,
|
|
nonOllama: nonOllama
|
|
};
|
|
|
|
fs.writeFileSync('agent-evolution/data/real-state.json', JSON.stringify(state, null, 2) + '\n');
|
|
|
|
// Console report
|
|
console.log('=== REAL SYSTEM STATE ===');
|
|
console.log('Generated:', state.generated);
|
|
console.log('Agents:', state.totalAgents);
|
|
console.log('Commands:', state.totalCommands);
|
|
console.log('All ollama-cloud/:', state.allOllama ? 'YES' : 'NO (' + nonOllama.length + ' exceptions)');
|
|
console.log('\n=== MODEL DISTRIBUTION ===');
|
|
for (const [m, c] of Object.entries(modelStats).sort((a,b) => b[1]-a[1])) {
|
|
console.log(` ${m}: ${c}`);
|
|
}
|
|
if (issues.length > 0) {
|
|
console.log('\n=== ISSUES ===');
|
|
issues.forEach(i => console.log(' ⚠️', i));
|
|
}
|
|
if (nonOllama.length > 0) {
|
|
console.log('\n=== NON-OLLOMA ===');
|
|
nonOllama.forEach(n => console.log(' ❌', n.type, n.name, n.model));
|
|
}
|
|
console.log('\n✅ State written to agent-evolution/data/real-state.json');
|