<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dhanush Kumar Sivaji</title>
    <description>The latest articles on DEV Community by Dhanush Kumar Sivaji (@dhanushkumarsivaji).</description>
    <link>https://dev.to/dhanushkumarsivaji</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F909684%2Ff0e49f96-ee40-4b7b-a54a-f6694ecf40f1.png</url>
      <title>DEV Community: Dhanush Kumar Sivaji</title>
      <link>https://dev.to/dhanushkumarsivaji</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dhanushkumarsivaji"/>
    <language>en</language>
    <item>
      <title>I built kerf-cli because Claude Code told me not to worry about cost</title>
      <dc:creator>Dhanush Kumar Sivaji</dc:creator>
      <pubDate>Wed, 08 Apr 2026 01:07:15 +0000</pubDate>
      <link>https://dev.to/dhanushkumarsivaji/i-built-kerf-cli-because-claude-code-told-me-not-to-worry-about-cost-1ma7</link>
      <guid>https://dev.to/dhanushkumarsivaji/i-built-kerf-cli-because-claude-code-told-me-not-to-worry-about-cost-1ma7</guid>
      <description>&lt;p&gt;A few weeks ago I logged into Claude Code, typed &lt;code&gt;/cost&lt;/code&gt;, and got back this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;With your Claude Max subscription, no need to monitor cost.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Two days later I had used 91% of my weekly limit in a single morning of work. I had no idea which session, which project, or which model was responsible. I tried &lt;code&gt;ccusage&lt;/code&gt; (which is great) and it gave me totals — but I wanted to ask questions like "which of my projects is eating Opus tokens unnecessarily?" and "what's my actual cache hit rate over the last 30 days?" Those answers weren't there.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;kerf-cli&lt;/strong&gt; — a local-first cost intelligence tool for Claude Code. This post is about why it exists, what it does, and what I learned about Claude Code billing along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The actual problem
&lt;/h2&gt;

&lt;p&gt;Anthropic gives you a lot of data and almost no analytics on top of it. Every Claude Code session is logged to &lt;code&gt;~/.claude/projects/&amp;lt;encoded-cwd&amp;gt;/&amp;lt;session-id&amp;gt;.jsonl&lt;/code&gt; with full token breakdowns: input, output, cache_read, cache_creation, model, timestamp, git branch. The data is rich. The tooling on top of it is thin.&lt;/p&gt;

&lt;p&gt;Here's what existed when I started:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code's &lt;code&gt;/cost&lt;/code&gt; command&lt;/strong&gt; — current session only, and it actively discourages Max subscribers from looking&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anthropic's web console&lt;/strong&gt; — org-level dashboards for Teams/Enterprise, nothing for solo developers on Pro/Max&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ccusage&lt;/strong&gt; — excellent for quick reports, but parses JSONL on every invocation with no persistence&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A handful of other CLIs and menu-bar apps&lt;/strong&gt; — most are read-only reporters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I wanted was three things none of them offered:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A persistent analytical layer I could query with SQL&lt;/li&gt;
&lt;li&gt;Real budget enforcement that blocks Claude Code when I exceed a cap, not just a warning&lt;/li&gt;
&lt;li&gt;Concrete optimization recommendations — not "you spent $47" but "switch these 12 sessions from Opus to Sonnet and you'll save $140/month"&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How kerf works
&lt;/h2&gt;

&lt;p&gt;Kerf is a TypeScript CLI built on &lt;code&gt;commander&lt;/code&gt;, &lt;code&gt;ink&lt;/code&gt;, and &lt;code&gt;better-sqlite3&lt;/code&gt;. The architecture is dead simple: it reads Claude Code's existing JSONL session files, ingests them into a local SQLite database, and then every command and the web dashboard query that database directly. You run &lt;code&gt;kerf sync&lt;/code&gt; once and it ingests every Claude Code session you've ever had. Subsequent syncs are incremental — only changed files are re-parsed. Then everything else is fast queries against the local DB.&lt;/p&gt;

&lt;h2&gt;
  
  
  The commands that matter
&lt;/h2&gt;

&lt;h3&gt;
  
  
  kerf summary
&lt;/h3&gt;

&lt;p&gt;The bread and butter — what did I spend?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kerf summary --period week --by-project

For week (Apr 1 → Apr 7):
  Total cost:     $178.04
  Sessions:       25
  Tokens:         454.0M
  Cache hit:      98%

By project:
  projects       $117.00  (66%)   5 sessions
  subagents       $42.50  (24%)  14 sessions
  kerf            $14.54   (8%)   4 sessions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  kerf query
&lt;/h3&gt;

&lt;p&gt;The SQL escape hatch I built mostly for myself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;kerf&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="nv"&gt;"SELECT date(timestamp) as day, ROUND(SUM(cost_usd), 2) as cost
              FROM messages
              WHERE timestamp &amp;gt; date('now', '-7 days')
              GROUP BY day ORDER BY day DESC"&lt;/span&gt;

&lt;span class="k"&gt;day&lt;/span&gt;         &lt;span class="n"&gt;cost&lt;/span&gt;
&lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;04&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;  &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;62&lt;/span&gt;
&lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;04&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;06&lt;/span&gt;  &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;84&lt;/span&gt;
&lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;04&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;04&lt;/span&gt;  &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;51&lt;/span&gt;
&lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;04&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;  &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;
&lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;04&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;02&lt;/span&gt;  &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;33&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--examples&lt;/code&gt; flag prints a dozen useful queries to copy. The &lt;code&gt;--schema&lt;/code&gt; flag prints the database schema. Writes are blocked — only SELECT statements are allowed.&lt;/p&gt;

&lt;h3&gt;
  
  
  kerf efficiency
&lt;/h3&gt;

&lt;p&gt;The command that actually saves money. This is the one I use every Monday morning.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyu4g1k8nlo4lh7qnm46v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyu4g1k8nlo4lh7qnm46v.png" alt="kerf efficiency terminal output" width="732" height="275"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This was the moment kerf paid for itself. I had been blindly using Opus for everything because it was the default. The analyzer pointed out that most of my Opus sessions had patterns that would have been fine on Sonnet — file edits, small refactors, dependency bumps. I switched those workflows, cut a meaningful chunk of my monthly Claude bill, and noticed zero quality difference.&lt;/p&gt;

&lt;h3&gt;
  
  
  kerf budget + kerf init --enforce-budgets
&lt;/h3&gt;

&lt;p&gt;The killer feature. &lt;code&gt;kerf budget set 50 --period weekly&lt;/code&gt; sets a cap, then &lt;code&gt;kerf init --enforce-budgets&lt;/code&gt; installs a Claude Code PreToolUse hook that runs &lt;code&gt;kerf budget check&lt;/code&gt; before every tool call. If you're over budget, the hook returns exit code 2 and Claude Code blocks the action.&lt;/p&gt;

&lt;p&gt;This is the difference between knowing you blew your budget and not being able to. Other tools warn. Kerf enforces.&lt;/p&gt;

&lt;h3&gt;
  
  
  kerf dashboard
&lt;/h3&gt;

&lt;p&gt;The local web UI for visual people. Opens at &lt;code&gt;http://localhost:3847&lt;/code&gt; — SQLite-backed so queries are sub-100ms, three killer-features cards (budget, efficiency, cache) front and center, sortable session table with drill-down, stacked cost-over-time chart by model. Zero auth, zero cloud, zero data leaving your machine. That's the screenshot at the top of this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned about Claude Code billing
&lt;/h2&gt;

&lt;p&gt;A few non-obvious things from spending too much time staring at JSONL files:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Cache reads can be 60–80% of your total cost.&lt;/strong&gt; This was the biggest surprise. Cache reads are billed at 10% of standard input rate, which sounds cheap until you realize you're caching 50K tokens per turn and reading them on every message. Optimizing your CLAUDE.md and reducing cache invalidation was the biggest single lever I found.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Opus is the default and it almost never needs to be.&lt;/strong&gt; I ran kerf efficiency on a month of data: 90% of my Opus tokens were on sessions that had no complexity signal (no debugging, no architecture decisions, no large refactors — just file edits and small fixes). Switching them to Sonnet was a 4x cost reduction with no measurable quality drop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Claude Code's JSONL streams partial usage updates.&lt;/strong&gt; When you parse them, you have to keep the MAX value per field across duplicate message IDs, not the latest value. I learned this the hard way — my v2.1 parser was undercounting input tokens because it kept the "last" entry instead of the "max," which meant the final zero-input chunk overwrote the real numbers from earlier chunks. Fixed before launch, but it's a subtle trap anyone parsing these logs will hit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. The 5-hour billing window is real but Anthropic doesn't expose it clearly.&lt;/strong&gt; Max subscribers are billed against a rolling 5-hour window, not a daily quota. If you don't track this, you can get surprised when the window rolls over mid-session.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical decisions I'd defend
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SQLite over a JSON file.&lt;/strong&gt; JSON is fine for ccusage's "read once, compute, discard" model. For an analytics layer you want sub-100ms queries, joins, aggregations, and a schema migration story. SQLite via better-sqlite3 is the right tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local-only over cloud-first.&lt;/strong&gt; The moment you add cloud sync, you need auth, storage, privacy controls, GDPR compliance, a business model. None of that serves the primary use case of "show me where my money went." Kerf is local-first on purpose. A hosted team tier is on the roadmap but will always be optional.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ink for the terminal UI.&lt;/strong&gt; React components in the terminal feel weird at first but the composability is worth it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hooks as the enforcement mechanism.&lt;/strong&gt; Claude Code ships a native hook system. Using hooks means kerf doesn't have to intercept or proxy Claude Code's traffic — it just responds to events Claude Code already emits.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;v1 is focused on doing one thing well: Claude Code observability for individual developers. The roadmap from here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;v2.x:&lt;/strong&gt; Cursor and Codex support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;v2.x:&lt;/strong&gt; Slack/Discord alerts on budget thresholds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;v2.x:&lt;/strong&gt; GitHub Actions integration for cost gates on PRs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;v3.x (paid team tier):&lt;/strong&gt; cloud sync, team aggregation, SSO&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But none of that ships before I'm sure v1 is rock solid. The CLI will always be free and MIT licensed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; kerf-cli
kerf &lt;span class="nb"&gt;sync
&lt;/span&gt;kerf summary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/dhanushkumarsivaji/kerf-cli" rel="noopener noreferrer"&gt;github.com/dhanushkumarsivaji/kerf-cli&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Show HN discussion:&lt;/strong&gt; &lt;a href="https://news.ycombinator.com/item?id=47683060" rel="noopener noreferrer"&gt;news.ycombinator.com/item?id=47683060&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you've hit billing surprises with Claude Code, I'd love to hear about them in the comments. The more weird patterns I see, the better the analyzer gets.&lt;/p&gt;

</description>
      <category>claude</category>
      <category>ai</category>
      <category>opensource</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
