Files
APAW/agent-evolution/scripts/audit-system.cjs
Deploy Bot b95fd41587 feat(evolution): add real-fit dashboard, API, report builder, and docker compose
- 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
2026-05-28 11:55:49 +01:00

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');