(file)
| 28 | : n.includes('codegraph') ? cgShort(n) : n; |
| 29 | |
| 30 | function parse(file) { |
| 31 | if (!existsSync(file)) return null; |
| 32 | const lines = readFileSync(file, 'utf8').split('\n').filter(Boolean); |
| 33 | const calls = []; let result = null, initCg = 0; |
| 34 | for (const l of lines) { |
| 35 | let ev; try { ev = JSON.parse(l); } catch { continue; } |
| 36 | if (ev.type === 'system' && ev.subtype === 'init') initCg = (ev.tools || []).filter(t => /codegraph/.test(t)).length; |
| 37 | if (ev.type === 'assistant') for (const b of (ev.message?.content || [])) if (b.type === 'tool_use') { |
| 38 | const i = b.input || {}; |
| 39 | const q = i.query ?? i.symbol ?? i.task ?? (i.from && i.to ? `${i.from}->${i.to}` : (i.file_path || i.command || '')); |
| 40 | calls.push({ id: b.id, name: b.name, q: String(q ?? '').slice(0, 38), out: 0 }); |
| 41 | } |
| 42 | if (ev.type === 'user') for (const b of (ev.message?.content || [])) if (b.type === 'tool_result') { |
| 43 | const c = b.content; |
| 44 | const txt = typeof c === 'string' ? c : Array.isArray(c) ? c.map(x => x?.text || '').join('') : ''; |
| 45 | const call = calls.find(k => k.id === b.tool_use_id); if (call) call.out = txt.length; |
| 46 | } |
| 47 | if (ev.type === 'result') result = ev; |
| 48 | } |
| 49 | const cg = calls.filter(c => c.name.includes('codegraph')); |
| 50 | const perTool = {}; |
| 51 | for (const c of cg) { const k = cgShort(c.name); (perTool[k] ??= { n: 0, out: 0 }); perTool[k].n++; perTool[k].out += c.out; } |
| 52 | const traceIdx = cg.findIndex(c => c.name.includes('trace')); |
| 53 | const u = result?.usage || {}; |
| 54 | return { |
| 55 | initCg, cg, perTool, |
| 56 | cgSeq: cg.map(c => cgShort(c.name)), |
| 57 | seq: calls.map(c => tag(c.name)), |
| 58 | reads: calls.filter(c => c.name === 'Read').length, |
| 59 | greps: calls.filter(c => c.name === 'Grep').length, |
| 60 | cgOut: cg.reduce((s, c) => s + c.out, 0), |
| 61 | traceUsed: traceIdx >= 0, |
| 62 | afterTrace: traceIdx >= 0 ? cg.slice(traceIdx + 1).map(c => cgShort(c.name)) : null, |
| 63 | turns: result?.num_turns ?? null, |
| 64 | dur: result?.duration_ms ? Math.round(result.duration_ms / 1000) : null, |
| 65 | cost: result?.total_cost_usd || 0, |
| 66 | }; |
| 67 | } |
| 68 | |
| 69 | const cells = []; |
| 70 | for (const d of readdirSync(AB)) { |
no test coverage detected