
ECC/cost-report命令实战从本地成本追踪数据生成 Claude Code 支出报告【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC导读ECCEverything Claude Code提供了一条cost-report命令用于从本地成本追踪数据中汇总 Claude Code 的使用支出按日、按模型/项目、按会话维度生成紧凑报告并支持一键导出 CSV。本文以仓库内docs/ja-JP/commands/cost-report.md日文版命令文档为主体脉络结合当前仓库中命令文档、stop:cost-tracker钩子源码与测试用例完整讲解该命令的查询逻辑、数据来源、每条查询语句的含义与底层实现原理。读完本文你将掌握如何独立复现这条命令的所有聚合查询并能理解追踪器写入的每一条数据的语义。命令定位与数据来源cost-report是 ECC 的命令体系之一在 docs/COMMAND-REGISTRY.json 中注册其描述为「从 ECC cost-tracker 指标日志生成本地 Claude Code 成本报告」类型标记为testing。命令文档本体位于 commands/cost-report.md。需要特别注意这条命令存在两个历史版本数据存储形态不同。旧版社区 PR 复活版即日文文档docs/ja-JP/commands/cost-report.md所描述的形态。它假设一个成本追踪钩子或插件已经把使用记录写入 SQLite 数据库~/.claude-cost-tracker/usage.db命令通过sqlite3查询usage表。日文文档末尾注明该命令源自MayurBhavsar的旧社区 PR #1304。当前仓库实现v2英文命令文档 commands/cost-report.md 描述的是演进后的形态。ECC 的stop:cost-tracker钩子scripts/hooks/cost-tracker.js在每个会话结束时向~/.claude/metrics/costs.jsonl追加一行 JSON命令改用node读取该 JSONL 日志做聚合。改用node而非sqlite3/jq的原因在文档中写得很明确保证在 macOS、Linux、Windows 三平台上行为一致。当前仓库中commands/cost-report.md、钩子源码与测试均为 JSONL 版本因此本文以 JSONL 版本为准讲解实际行为同时完整保留日文文档中 SQLite 版本的查询骨架汇总、分组、最近 7 天、CSV 导出两者聚合逻辑一一对应方便读者理解演进脉络。命令执行流程无论哪个版本cost-report的工作流程都是四步检查查询工具是否可用SQLite 版本检查sqlite3JSONL 版本使用node天然跨平台检查数据文件是否存在~/.claude-cost-tracker/usage.db或~/.claude/metrics/costs.jsonl对数据执行聚合查询输出紧凑报告若参数为csv则导出最近的使用行。日文文档给出了数据文件存在性检查的等价命令test -f ~/.claude-cost-tracker/usage.db echo Database found || echo Database not found对应到当前仓库的 JSONL 实现检查命令为node -e const fsrequire(fs),osrequire(os),prequire(path);const fp.join(os.homedir(),claude,metrics,costs.jsonl);console.log(fs.existsSync(f)?cost log found:cost log not found: f)前提条件数据文件必须由本地成本追踪器写入。文件不存在时命令会告知用户追踪器尚未启用并提示先安装/启用可信的 Claude Code 成本追踪钩子或插件。对当前仓库而言就是启用stop:cost-tracker钩子并完成至少一个会话——钩子只在会话结束时Stop 事件写入首行数据。数据模型每行的语义理解报告的聚合逻辑必须先理解数据行的语义。scripts/hooks/cost-tracker.js的头部注释与 skills/cost-tracking/SKILL.md 给出了行结构字段含义timestamp快照的 ISO 时间戳session_idClaude Code 会话标识transcript_path会话 transcript 文件路径model使用的模型input_tokens/output_tokensToken 计数cache_write_tokens/cache_read_tokens提示缓存写入/读取的 Token 计数estimated_cost_usd该会话累计成本的预估美元值预计算两个关键语义约束每行是会话的累计快照cumulative snapshotStop 事件按「每次助手响应」触发而非按会话触发因此同一会话会产生多行每行代表截至该时刻的累计总量。汇总时必须取每个session_id的最后一行再跨会话求和——把每一行都加起来会造成重复计数。这正是报告脚本中bySessionMap 去重逻辑存在的根本原因。成本取预计算值报告依赖追踪器写入的estimated_cost_usd绝不从原始 Token 数重新估算价格。因为模型与缓存价格会变化追踪器才是事实来源。汇总查询今日 / 昨日 / 总计日文文档给出了 SQLite 版本的汇总查询usage表完整继承如下sqlite3 -header -column ~/.claude-cost-tracker/usage.db SELECT ROUND(COALESCE(SUM(CASE WHEN date(timestamp) date(now) THEN cost_usd END), 0), 4) AS today_cost, ROUND(COALESCE(SUM(CASE WHEN date(timestamp) date(now, -1 day) THEN cost_usd END), 0), 4) AS yesterday_cost, ROUND(COALESCE(SUM(cost_usd), 0), 4) AS total_cost, COUNT(*) AS total_calls, COUNT(DISTINCT session_id) AS sessions FROM usage; 对应到当前仓库的 JSONL 版本命令文档中的等价实现为node -e const fsrequire(fs),osrequire(os),pathrequire(path); const fpath.join(os.homedir(),.claude,metrics,costs.jsonl); if(!fs.existsSync(f)){console.log(Cost tracker not set up: f not found. Enable the stop:cost-tracker hook and finish a session first.);process.exit(0);} const rowsfs.readFileSync(f,utf8).split(/\r?\n/).filter(Boolean).map(l{try{return JSON.parse(l)}catch{return null}}).filter(Boolean); const bySessionnew Map(); for(const r of rows){const kr.session_id||r.transcript_path||r.timestamp;const pbySession.get(k);if(!p||String(r.timestamp)String(p.timestamp))bySession.set(k,r);} const latest[...bySession.values()]; const costrNumber(r.estimated_cost_usd)||0; const dayrString(r.timestamp||).slice(0,10); const todaynew Date().toISOString().slice(0,10); const dnew Date(Date.now()-864e5).toISOString().slice(0,10); const sumaa.reduce((s,r)scost(r),0); const f4n$n.toFixed(4); console.log( Cost summary ); console.log(today: f4(sum(latest.filter(rday(r)today)))); console.log(yesterday: f4(sum(latest.filter(rday(r)d)))); console.log(total: f4(sum(latest)) (latest.length sessions)); 实现要点逐条对应去重bySession以session_id缺失时回退到transcript_path或timestamp为键仅保留时间戳最新的一行——对应 SQL 版中「按会话取累计快照」的要求。日切割day()截取 ISO 时间戳前 10 位得到YYYY-MM-DDtoday用new Date().toISOString().slice(0,10)计算yesterday用Date.now()-864e5即 24 小时前计算——对应 SQL 版中的date(now)与date(now,-1 day)。成本归一cost()用Number(...) || 0兜底任何缺失或非法值都按 0 处理对应 SQL 版中的COALESCE(..., 0)。金额格式f4统一输出四位小数即文档约定的「1 美元以下金额保留 4 位小数」规则。按维度分组项目 / 工具 / 模型日文文档提供了两个 SQLite 分组查询分别按项目与工具聚合均按成本降序排列# 按项目 sqlite3 -header -column ~/.claude-cost-tracker/usage.db SELECT project, ROUND(SUM(cost_usd), 4) AS cost, COUNT(*) AS calls FROM usage GROUP BY project ORDER BY cost DESC; # 按工具 sqlite3 -header -column ~/.claude-cost-tracker/usage.db SELECT tool_name, ROUND(SUM(cost_usd), 4) AS cost, COUNT(*) AS calls FROM usage GROUP BY tool_name ORDER BY cost DESC; 当前仓库的 JSONL 行结构中没有project与tool_name字段其维度被model取代。命令文档中的按模型分组实现为console.log(\n By model ); for(const [k,v] of by(rr.model))console.log(f4(v).padStart(12) k);其中by是通用的分组聚合器const by(key){ const mnew Map(); for(const r of latest){ const kkey(r)||(unknown); m.set(k,(m.get(k)||0)cost(r)); } return [...m.entries()].sort((a,b)b[1]-a[1]); };分组键取r.model缺失时归入(unknown)组求和后按成本降序排列输出时f4(...).padStart(12)右对齐保证列对齐语义与 SQL 版的GROUP BY ... ORDER BY cost DESC完全一致。最近 7 天趋势日文文档的 SQLite 版本sqlite3 -header -column ~/.claude-cost-tracker/usage.db SELECT date(timestamp) AS date, ROUND(SUM(cost_usd), 4) AS cost, COUNT(*) AS calls FROM usage GROUP BY date(timestamp) ORDER BY date DESC LIMIT 7; JSONL 版本实现console.log(\n Last 7 days ); const daysnew Map(); for(const r of latest){const kday(r);days.set(k,(days.get(k)||0)cost(r));} [...days.entries()].sort((a,b)b[0]a[0]?-1:1).slice(0,7).forEach(([k,v])console.log(k f4(v)));按日期分组求和后按日期倒序取前 7 天输出格式为日期 成本。CSV 导出/cost-report csv当用户以/cost-report csv调用时导出最近的使用行。SQLite 版本使用显式列名sqlite3 -csv -header ~/.claude-cost-tracker/usage.db SELECT timestamp, project, tool_name, input_tokens, output_tokens, cost_usd, session_id, model FROM usage ORDER BY timestamp DESC LIMIT 100; JSONL 版本使用与行结构一致的列node -e const fsrequire(fs),osrequire(os),pathrequire(path); const fpath.join(os.homedir(),.claude,metrics,costs.jsonl); if(!fs.existsSync(f)){console.error(no data);process.exit(0);} const rowsfs.readFileSync(f,utf8).split(/\r?\n/).filter(Boolean).map(l{try{return JSON.parse(l)}catch{return null}}).filter(Boolean).slice(-100); console.log(timestamp,session_id,model,input_tokens,output_tokens,cache_write_tokens,cache_read_tokens,estimated_cost_usd); for(const r of rows)console.log([r.timestamp,r.session_id,r.model,r.input_tokens,r.output_tokens,r.cache_write_tokens,r.cache_read_tokens,r.estimated_cost_usd].join(,)); 要点取原始行数组的最后 100 行slice(-100)等价于 SQL 的ORDER BY timestamp DESC LIMIT 100的最近行首行输出带表头的 CSV 列名字段用逗号直接拼接与 SQL 版相比字段从cost_usd变为estimated_cost_usd并增加了cache_write_tokens/cache_read_tokens两个缓存字段使 CSV 保留了完整的会话成本结构。报告格式约定命令将响应整理为固定结构汇总Summary今日、昨日、总计、调用次数会话数按维度分组按总成本降序排名SQLite 版为项目/工具JSONL 版为模型最近 7 天日期、成本、调用次数。金额格式化规则1 美元以下保留 4 位小数$0.0123更大金额可缩减位数。命令本身不做价格估算——它只信任追踪器写入的预计算成本值。源码级原理stop:cost-tracker钩子如何产出数据要彻底理解报告需要知道costs.jsonl的行是怎么算出来的。核心实现在 scripts/hooks/cost-tracker.js该钩子通过 hooks/hooks.json 注册为stop:cost-tracker匹配所有会话matcher: .*由run-with-flags.js以minimal,standard,strict模式运行异步执行、超时 10 秒。1. 数据不是从 Stop 载荷直接拿的Stop 事件的 stdin 载荷形如{ session_id, transcript_path, cwd, hook_event_name, ... }并不直接包含usage或model字段。钩子文件头部注释记录了一个真实的教训旧版本期望这些字段存在结果 52 天内在 2340 行记录中非零 Token 率为 0.0%——全部是零值行。修复方式是改读 Claude Code 已经传入的 transcript 文件。2. Transcript 解析与按 message.id 去重Claude Code 的 transcript 是 JSONL每行一个内容块。sumUsageFromTranscript()只处理type assistant且带message.usage的行并做一次关键去重Claude Code 每个内容块写一行 JSONL因此同一次 API 响应同一个message.id会横跨多行 assistant 记录且每行重复相同的 usage。逐行求和会把总量放大 2.5~3 倍。实测一个 704 行 assistant 记录的会话只有 286 个唯一message.id——逐行求和得 $867按 id 去重后仅 $333。因此钩子以message.id为键、保留每个 id 的最后一行 usage再累计input_tokens、output_tokens、cache_creation_input_tokens、cache_read_input_tokens。对没有message.id的旧格式行用合成键__line_N维持原有逐行行为。模型名取最后一个非unknown值。3. 内置费率表钩子内置了一张近似费率表每百万 Token 美元用于把 Token 折算为成本档位输入输出缓存写入缓存读取haiku1.005.01.250.10sonnetSonnet 4.6 等3.0015.03.750.30sonnet52.0010.02.500.20opusOpus 4.55.0025.06.250.50opusLegacyOpus 3 / 4.0 / 4.115.0075.018.751.50fable/mythos10.0050.012.501.00模型匹配规则由getRates()实现包含fable/mythos走 Fable 档包含haiku走 Haiku 档正则精确匹配sonnet-5避免把claude-sonnet-50误判LEGACY_OPUS_RE用于识别带日期快照的 Opus 4.0如claude-opus-4-20250514等旧档模型。4. Harness 权威成本优先钩子还支持一个可选的权威成本通道如果用户的 statusline 在每次渲染时把 Claude Code 直接下发的cost.total_cost_usd写入os.tmpdir()/harness-cost-session_id.json内容为{ts, cost_usd}钩子会优先采用该值前提是缓存新鲜HARNESS_COST_MAX_AGE_SECONDS 300即 5 秒内。优先的原因写在文件头硬编码费率表无法表达 Opus 4.7 的 200K Token 2x 档位与 1 小时缓存 2x 档位长会话会低估对整个 transcript 求和会在--resume边界重复计算而cost.total_cost_usd是按进程计的不会漂移。缓存缺失或过期则回退到 transcript 求和值。最终行写入~/.claude/metrics/costs.jsonl实际目录经getClaudeDir()解析测试中以临时 HOME 覆盖验证。5. 失败不阻塞会话整个处理包裹在 try/catch 中任何解析错误都不会让 Stop 钩子失败fail-openstdin 超过 1MB 时Stop 载荷携带last_assistant_message时常超旧版 64KB 上限抑制透传并在 stderr 告警避免把截断的 JSON 回显到 stdout 被报告为钩子失败。测试验证行为被测试用例锁死tests/hooks/cost-tracker.test.jsnode tests/hooks/cost-tracker.test.js运行覆盖了上述全部关键行为可作为复现与验证的依据stdin 透传钩子必须原样回写 stdin空输入与非法 JSON 均不崩溃fail-open 契约Token 汇总从 transcript 正确累计 input/output/cache 四类 Token最后出现的 assistant 模型被记录按 message.id 去重同一msg_01AAA出现 3 行只计 1 次input_tokens精确为 1025 而非 3 倍会话 ID 优先级ECC_SESSION_ID优先于CLAUSE_SESSION_ID其次才用载荷中的session_id——这是与 ECC2 会话关联的约定Harness 成本优先新鲜缓存cost_usd: 1.23胜过 transcript 估算且 Token 仍来自 transcript超过 300 秒的陈旧缓存999.99被忽略并回退到 transcript 估算费率准确性Sonnet 5 的 1M/1M Token 精确等于 $12含缓存写入/读取时 $14.70Sonnet 4.6 保持 $18 且不被误判为 Sonnet 5带日期的 Sonnet 5claude-sonnet-5-20261001按 $12claude-sonnet-50近误判回退 $18带日期的 Opus 4.0 保留 $15/$75 旧档1M/1M $90Opus 4.5 用现行 $5/$25 $30。这些断言直接印证了「报告只信任预计算值」的约定估算精度由钩子侧统一保证报告侧不重复定价。关联资源命令文档当前实现commands/cost-report.md英文原版数据为costs.jsonl日文命令文档SQLite 旧版脉络docs/ja-JP/commands/cost-report.md钩子源码scripts/hooks/cost-tracker.js钩子注册hooks/hooks.json测试用例tests/hooks/cost-tracker.test.js成本追踪技能skills/cost-tracking/SKILL.md含行结构表格、反模式清单不要全量求和、不要拿原始 Token 自行估价、不要假定日志存在、不要在面向用户的回答中硬编码模型价格命令注册表docs/COMMAND-REGISTRY.json小结/cost-report的价值在于把「成本数据收集」与「成本数据展示」解耦追踪侧由stop:cost-tracker钩子在会话停止时按message.id去重汇总 transcript叠加内置费率表或优先采用 harness 权威成本产出带estimated_cost_usd的累计快照报告侧只做「每会话取最新一行 → 去重 → 聚合」这一件事通过摘要、按模型分组、最近 7 天与 CSV 导出四种视图回答「今天/昨天花了多少、哪个模型最贵、趋势如何」等问题。理解行语义累计快照而非增量行是正确解读一切报告的前提也是本命令最值得记住的设计要点。【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考