#!/usr/bin/env bun /** * Agent Stats - Analyze agent execution logs * * Usage: * bun run scripts/agent-stats.ts * bun run scripts/agent-stats.ts --last 7 * bun run scripts/agent-stats.ts --project UniqueSoft/my-shop */ import { readFileSync, existsSync } from 'fs'; import { join } from 'path'; interface AgentExecution { ts: string; agent: string; issue: number; project: string; task: string; subtask_type: string; duration_ms: number; tokens_used: number; status: string; files: string[]; score: number | null; next_agent: string | null; } interface AgentStats { calls: number; avgDuration: number; avgTokens: number; avgScore: number; successRate: number; totalDuration: number; totalTokens: number; } function parseArgs(): { lastDays: number; project: string | null } { const args = process.argv.slice(2); let lastDays = 30; let project: string | null = null; for (let i = 0; i < args.length; i++) { if (args[i] === '--last' && args[i + 1]) { lastDays = parseInt(args[i + 1], 10); } if (args[i] === '--project' && args[i + 1]) { project = args[i + 1]; } } return { lastDays, project }; } function loadExecutions(logPath: string): AgentExecution[] { if (!existsSync(logPath)) { console.log('No execution log found. Start using agents to build history.'); return []; } const content = readFileSync(logPath, 'utf-8'); return content .split('\n') .filter(line => line.trim()) .map(line => { try { return JSON.parse(line); } catch { return null; } }) .filter((e): e is AgentExecution => e !== null); } function filterByDate(executions: AgentExecution[], days: number): AgentExecution[] { const cutoff = new Date(); cutoff.setDate(cutoff.getDate() - days); return executions.filter(e => new Date(e.ts) >= cutoff); } function filterByProject(executions: AgentExecution[], project: string): AgentExecution[] { return executions.filter(e => e.project === project); } function computeStats(executions: AgentExecution[]): Map { const stats = new Map(); for (const e of executions) { const existing = stats.get(e.agent) || { calls: 0, avgDuration: 0, avgTokens: 0, avgScore: 0, successRate: 0, totalDuration: 0, totalTokens: 0, }; existing.calls++; existing.totalDuration += e.duration_ms; existing.totalTokens += e.tokens_used; if (e.score) existing.avgScore = (existing.avgScore * (existing.calls - 1) + e.score) / existing.calls; if (e.status === 'success' || e.status === 'pass') { existing.successRate = (existing.successRate * (existing.calls - 1) + 1) / existing.calls; } stats.set(e.agent, existing); } // Compute averages for (const [, s] of stats) { s.avgDuration = s.calls > 0 ? s.totalDuration / s.calls : 0; s.avgTokens = s.calls > 0 ? s.totalTokens / s.calls : 0; } return stats; } function formatDuration(ms: number): string { if (ms < 1000) return `${ms}ms`; if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`; return `${(ms / 60000).toFixed(1)}m`; } function formatTokens(tokens: number): string { if (tokens < 1000) return `${tokens}`; return `${(tokens / 1000).toFixed(1)}k`; } const logPath = join(process.cwd(), '.kilo', 'logs', 'agent-executions.jsonl'); const { lastDays, project } = parseArgs(); let executions = loadExecutions(logPath); if (executions.length === 0) { console.log('\nšŸ“Š Agent Stats - No data yet\n'); console.log('Log file:', logPath); console.log('Start using agents to build execution history.\n'); process.exit(0); } if (lastDays < 9999) { executions = filterByDate(executions, lastDays); } if (project) { executions = filterByProject(executions, project); } const stats = computeStats(executions); console.log(`\nšŸ“Š Agent Stats (Last ${lastDays} days${project ? `, Project: ${project}` : ''})`); console.log('═'.repeat(70)); const sortedStats = [...stats.entries()].sort((a, b) => b[1].calls - a[1].calls); for (const [agent, s] of sortedStats) { console.log( `${agent.padEnd(20)} ${String(s.calls).padStart(3)} calls, ` + `avg ${formatDuration(s.avgDuration).padStart(6)}, ` + `score ${s.avgScore.toFixed(1)}/10, ` + `${(s.successRate * 100).toFixed(0)}% success, ` + `~${formatTokens(s.avgTokens)} tokens` ); } console.log('═'.repeat(70)); console.log(`Total: ${executions.length} executions\n`); // Project breakdown const projects = new Map(); for (const e of executions) { projects.set(e.project, (projects.get(e.project) || 0) + 1); } if (projects.size > 1) { console.log('šŸ“ By Project:'); for (const [proj, count] of projects) { console.log(` ${proj}: ${count} executions`); } console.log(''); } // Status breakdown const statuses = new Map(); for (const e of executions) { statuses.set(e.status, (statuses.get(e.status) || 0) + 1); } console.log('šŸ“ˆ By Status:'); for (const [status, count] of statuses) { console.log(` ${status}: ${count}`); } console.log('');