<?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: Dr. Farhan</title>
    <description>The latest articles on DEV Community by Dr. Farhan (@cytostack).</description>
    <link>https://dev.to/cytostack</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%2F3836080%2Fe8e5a489-00b7-4099-9ea3-6feab08a1c02.png</url>
      <title>DEV Community: Dr. Farhan</title>
      <link>https://dev.to/cytostack</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cytostack"/>
    <language>en</language>
    <item>
      <title>Claude Code used 2.5M tokens on my project. I got it down to 425K with 6 hook scripts.</title>
      <dc:creator>Dr. Farhan</dc:creator>
      <pubDate>Fri, 20 Mar 2026 20:43:47 +0000</pubDate>
      <link>https://dev.to/cytostack/claude-code-used-25m-tokens-on-my-project-i-got-it-down-to-425k-with-6-hook-scripts-d40</link>
      <guid>https://dev.to/cytostack/claude-code-used-25m-tokens-on-my-project-i-got-it-down-to-425k-with-6-hook-scripts-d40</guid>
      <description>&lt;p&gt;My team and I have been using Claude Code as our primary coding agent for about six months now. Somewhere around last month, I started hitting limits on my MAX plans. My prompts weren't unusually long and my projects aren't massive, so I couldn't figure out where the tokens were going.&lt;/p&gt;

&lt;p&gt;So I did what any developer would do when something doesn't make sense - I started logging.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;Claude Code supports lifecycle hooks - &lt;code&gt;PreToolUse&lt;/code&gt;, &lt;code&gt;PostToolUse&lt;/code&gt;, &lt;code&gt;SessionStart&lt;/code&gt;, &lt;code&gt;Stop&lt;/code&gt;. I wrote a simple &lt;code&gt;PreToolUse&lt;/code&gt; hook on the Read tool that appends every file read to a JSON log with a timestamp, file path, and a rough token estimate based on file size.&lt;/p&gt;

&lt;p&gt;Nothing fancy. Just a log.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Found
&lt;/h2&gt;

&lt;p&gt;After 132 sessions across 20 projects, the numbers were consistent:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;71% of all file reads were files Claude had already opened in that same session.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In one project, Claude read &lt;code&gt;server.ts&lt;/code&gt; four times in a single session. It read &lt;code&gt;package.json&lt;/code&gt; three times. Not because the files changed - because it had no awareness of what it had already read.&lt;/p&gt;

&lt;p&gt;The deeper issue: Claude doesn't know what a file contains until it opens it. It can't tell the difference between a 50-token config file and a 2,000-token module. It has no map of your project. When it needs to find a function, it scans directories. When it needs to understand your architecture, it opens files one by one.&lt;/p&gt;

&lt;p&gt;It works blind.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cost
&lt;/h2&gt;

&lt;p&gt;I ran a comparison on a large active project. Same codebase, same prompts, three different setups:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OpenClaw + Claude          ~3.4M tokens
Claude CLI (no middleware)  ~2.5M tokens
With my hooks              ~425K tokens
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's roughly 80% fewer tokens for the same work. The savings come from two places: Claude skipping full file reads when a description is enough, and Claude not re-reading files it already has in context.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Those logging hooks evolved into a proper tool. Six Node.js scripts that sit in a &lt;code&gt;.wolf/&lt;/code&gt; directory in your project:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. anatomy.md - the project index
&lt;/h3&gt;

&lt;p&gt;Every file in your project gets a one-line description and a token estimate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## src/api/&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="sb"&gt;`auth.ts`&lt;/span&gt; - JWT validation middleware. Reads from env.JWT_SECRET. (~340 tok)
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`users.ts`&lt;/span&gt; - CRUD endpoints for /api/users. Pagination via query params. (~890 tok)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before Claude reads a file, the &lt;code&gt;PreToolUse&lt;/code&gt; hook injects this info. Claude sees "this file is your Express config, ~520 tokens" and can decide whether the description is enough or whether it needs the full content. This alone eliminates a significant chunk of unnecessary reads.&lt;/p&gt;

&lt;p&gt;The index auto-updates after every write. You never maintain it manually.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. cerebrum.md - the learning memory
&lt;/h3&gt;

&lt;p&gt;This is the file that makes sessions compound. When you correct Claude - "don't use &lt;code&gt;var&lt;/code&gt;, always use &lt;code&gt;const&lt;/code&gt;" - it logs the correction here. When you express a preference - "named exports, never default exports" - it goes here. When you make an architecture decision, it goes here.&lt;/p&gt;

&lt;p&gt;The critical part is the Do-Not-Repeat list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Do-Not-Repeat&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; 2026-03-10: Never use &lt;span class="sb"&gt;`var`&lt;/span&gt; - always &lt;span class="sb"&gt;`const`&lt;/span&gt; or &lt;span class="sb"&gt;`let`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; 2026-03-11: Don't mock the database in integration tests
&lt;span class="p"&gt;-&lt;/span&gt; 2026-03-14: The auth middleware reads from &lt;span class="sb"&gt;`cfg.talk`&lt;/span&gt;, not &lt;span class="sb"&gt;`cfg.tts`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;PreToolUse&lt;/code&gt; hook on Write checks new code against this list before Claude writes it. It's not perfect - compliance is around 85-90% - but it catches the repeat offenders.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. buglog.json - the fix memory
&lt;/h3&gt;

&lt;p&gt;Every bug fix gets logged with the error message, root cause, fix, and tags. Before Claude starts debugging anything, the hook checks if that error is already in the log. Instead of spending 15 minutes rediscovering a solution, it finds it in the log and applies it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"error_message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TypeError: Cannot read properties of undefined (reading 'map')"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"root_cause"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"API response was null when users array was expected"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Added optional chaining: data?.users?.map() and fallback empty array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"null-check"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"api-response"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. token-ledger.json - the receipt
&lt;/h3&gt;

&lt;p&gt;Every session gets a line item. Lifetime totals track reads, writes, anatomy hits vs misses, and repeated reads blocked. This is how I got the numbers above - and how you can verify the savings for yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the hooks work
&lt;/h2&gt;

&lt;p&gt;The architecture is simple. Six hooks, three lifecycle events:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;Hook&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SessionStart&lt;/td&gt;
&lt;td&gt;session-start.js&lt;/td&gt;
&lt;td&gt;Creates session tracker, logs to memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PreToolUse (Read)&lt;/td&gt;
&lt;td&gt;pre-read.js&lt;/td&gt;
&lt;td&gt;Shows anatomy info, warns on repeated reads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PreToolUse (Write)&lt;/td&gt;
&lt;td&gt;pre-write.js&lt;/td&gt;
&lt;td&gt;Checks cerebrum Do-Not-Repeat list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PostToolUse (Read)&lt;/td&gt;
&lt;td&gt;post-read.js&lt;/td&gt;
&lt;td&gt;Estimates and records token usage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PostToolUse (Write)&lt;/td&gt;
&lt;td&gt;post-write.js&lt;/td&gt;
&lt;td&gt;Updates anatomy, appends to memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stop&lt;/td&gt;
&lt;td&gt;stop.js&lt;/td&gt;
&lt;td&gt;Writes session summary to token ledger&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All hooks are pure Node.js file I/O. No API calls, no network requests, no external services. The only AI calls are optional scheduled tasks (anatomy rescans, memory consolidation) that use your existing Claude subscription.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Claude Code hooks are still relatively new. &lt;code&gt;PreToolUse&lt;/code&gt;/&lt;code&gt;PostToolUse&lt;/code&gt; have had reliability issues in some versions. The tool falls back to &lt;code&gt;CLAUDE.md&lt;/code&gt; instructions when hooks don't fire.&lt;/li&gt;
&lt;li&gt;Token tracking is estimation-based (character-to-token ratio), not exact API counts. Accurate to within ~15%.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cerebrum.md&lt;/code&gt; depends on Claude actually following instructions to update it. Compliance is ~85-90%, not 100%.&lt;/li&gt;
&lt;li&gt;The 80% reduction was on one large project. The average across 20 projects is 65.8%. Your results will depend on project size and how you use Claude.&lt;/li&gt;
&lt;/ul&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; openwolf
&lt;span class="nb"&gt;cd &lt;/span&gt;your-project
openwolf init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then use &lt;code&gt;claude&lt;/code&gt; normally. The hooks fire invisibly. Run &lt;code&gt;openwolf status&lt;/code&gt; to see your numbers.&lt;/p&gt;

&lt;p&gt;It's called OpenWolf. Open source, AGPL-3.0.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/cytostack/openwolf" rel="noopener noreferrer"&gt;github.com/cytostack/openwolf&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://openwolf.com" rel="noopener noreferrer"&gt;openwolf.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have questions about the hook architecture, the token estimation approach, or anything else, I'm happy to go deeper in the comments.&lt;/p&gt;

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