const fs = require("fs"); const path = require("path"); const DEFAULT_BASELINE_MULTIPLIER = 2.9; function readJsonl(filePath) { if (!filePath || !fs.existsSync(filePath)) return []; return fs .readFileSync(filePath, "utf8") .split(/\r?\n/) .filter(Boolean) .map((line) => { try { return JSON.parse(line); } catch { return null; } }) .filter(Boolean); } function collectUsage(entry) { const candidates = [ entry.usage, entry.message && entry.message.usage, entry.response && entry.response.usage, entry.result && entry.result.usage, ].filter(Boolean); return candidates.reduce( (sum, usage) => { sum.input += Number(usage.input_tokens || usage.inputTokens || usage.prompt_tokens || 0); sum.output += Number(usage.output_tokens || usage.outputTokens || usage.completion_tokens || 0); return sum; }, { input: 0, output: 0 }, ); } function compactNumber(value) { if (value >= 1_000_000) return `${(value / 1_000_000).toFixed(1)}m`; if (value >= 1_000) return `${(value / 1_000).toFixed(1)}k`; return String(value); } function formatNumber(value) { return Math.round(value).toLocaleString("en-US"); } function buildStats(input) { const transcriptPath = input.transcript_path || input.transcriptPath; const entries = readJsonl(transcriptPath); const usage = entries.reduce( (sum, entry) => { const item = collectUsage(entry); sum.input += item.input; sum.output += item.output; return sum; }, { input: 0, output: 0 }, ); const turns = entries.filter((entry) => entry.type === "assistant" || entry.type === "user").length; const baselineOutput = Math.round(usage.output * DEFAULT_BASELINE_MULTIPLIER); const saved = Math.max(0, baselineOutput - usage.output); const percent = baselineOutput ? Math.round((saved / baselineOutput) * 100) : 0; return { turns, input: usage.input, output: usage.output, baselineOutput, saved, percent, transcriptPath, }; } function writeLifetimeSuffix(input, stats) { const cwd = input.cwd || process.cwd(); const outPath = path.join(cwd, ".agents", "hooks", ".caveman-lifetime-savings"); try { fs.writeFileSync(outPath, ` ${compactNumber(stats.saved)}`, "utf8"); } catch { // Stats display should still work if this optional badge write fails. } } function formatStats(stats) { if (!stats.transcriptPath) { return "No transcript path in hook input. Cannot compute real token stats."; } if (!stats.input && !stats.output) { return [ "No token usage found in current transcript.", `Transcript: ${stats.transcriptPath}`, ].join("\n"); } return [ `Session: ${formatNumber(stats.turns)} turns`, `Input: ${formatNumber(stats.input)} tokens`, `Output: ${formatNumber(stats.output)} tokens (caveman)`, `Baseline:${formatNumber(stats.baselineOutput).padStart(10)} tokens (estimated without caveman)`, `Saved: ${formatNumber(stats.saved).padStart(10)} tokens (~${stats.percent}%)`, ].join("\n"); } function run(input) { const stats = buildStats(input || {}); writeLifetimeSuffix(input || {}, stats); return formatStats(stats); } module.exports = { run, buildStats, formatStats };