<?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: 대표아이브엑스</title>
    <description>The latest articles on DEV Community by 대표아이브엑스 (@_b344376ac1d97b9ee).</description>
    <link>https://dev.to/_b344376ac1d97b9ee</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4199640%2F4898ed81-75b8-4241-86b6-89c90fdf361f.png</url>
      <title>DEV Community: 대표아이브엑스</title>
      <link>https://dev.to/_b344376ac1d97b9ee</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/_b344376ac1d97b9ee"/>
    <language>en</language>
    <item>
      <title>Estimating Claude Code's "5-hour window" from local logs — what I learned building an always-on usage gauge</title>
      <dc:creator>대표아이브엑스</dc:creator>
      <pubDate>Thu, 22 Oct 2026 05:47:38 +0000</pubDate>
      <link>https://dev.to/_b344376ac1d97b9ee/estimating-claude-codes-5-hour-window-from-local-logs-what-i-learned-building-an-always-on-1hi7</link>
      <guid>https://dev.to/_b344376ac1d97b9ee/estimating-claude-codes-5-hour-window-from-local-logs-what-i-learned-building-an-always-on-1hi7</guid>
      <description>&lt;p&gt;If you use Claude Code all day, you've probably been stopped mid-task by "usage limit reached". &lt;code&gt;/usage&lt;/code&gt; tells you the official number, but only when you stop and ask. I wanted the answer to sit in the corner of my screen while I work, so I built a small Windows gauge that estimates what's left from the logs Claude Code already writes. This post is about how the estimate works.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F48b1byamg1zpsmb9o5vx.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F48b1byamg1zpsmb9o5vx.gif" alt="AI Usage Gauge demo" width="461" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. What to read
&lt;/h2&gt;

&lt;p&gt;Claude Code writes one JSONL file per session under &lt;code&gt;~/.claude/projects/&amp;lt;project&amp;gt;/&amp;lt;session&amp;gt;.jsonl&lt;/code&gt;. Assistant lines carry &lt;code&gt;message.usage&lt;/code&gt;:&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="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"message"&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="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"usage"&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;span class="nl"&gt;"input_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"output_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;840&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cache_creation_input_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3021&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"cache_read_input_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;98000&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;p&gt;The files are append-only, so I remember the last byte offset per file and read only what's new. Even with gigabytes of logs, each refresh costs almost nothing. It never reads login data or tokens.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. What counts as "usage"
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;cache_read_input_tokens&lt;/code&gt; is huge (a long chat easily reads 100k+ per turn). Adding it in wrecks the estimate. I count &lt;strong&gt;input + output + cache_creation&lt;/strong&gt; as usage and use cache_read only to judge how long a chat's context has grown.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Cutting the 5-hour windows
&lt;/h2&gt;

&lt;p&gt;A window starts at the first request after the previous window ended, floored to the hour, and lasts 5 hours (an estimate that matches observed behaviour):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;BLOCK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;usage_blocks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_cr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_m&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;minute&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;microsecond&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;BLOCK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;cc&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Where does the limit come from?
&lt;/h2&gt;

&lt;p&gt;Plan limits aren't published and change over time, so the gauge learns &lt;em&gt;your&lt;/em&gt; limit:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;If you've ever hit the limit&lt;/strong&gt;, the log has a "limit reached / resets …" line. The total of the window containing that moment is your measured limit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Otherwise&lt;/strong&gt;, it uses your busiest 5-hour window in the last 31 days as a provisional ceiling ("headroom"). The 7-day limit works the same with a sliding window (the 7 days in progress are excluded).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Spend-limit lines are money-based, so they're ignored for learning.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. "At this pace you'll run out around 6:30 PM"
&lt;/h2&gt;

&lt;p&gt;From the last 30 minutes of usage it projects when you'll run out, and shows a yellow badge when you're going faster than your usual peak. It's not a perfect prediction, but knowing "about an hour left" before starting a big refactor has changed how I plan work.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Tkinter details
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Per-Monitor DPI v2, so it stays sharp on 125%/150% displays&lt;/li&gt;
&lt;li&gt;Tk's Canvas has no anti-aliasing, so rounded corners and bar ends are small images with per-pixel coverage&lt;/li&gt;
&lt;li&gt;Shipped as a single exe with PyInstaller&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. Other tools
&lt;/h2&gt;

&lt;p&gt;The same approach reads Codex, Gemini CLI and Antigravity logs. Cursor doesn't store usage locally, so its tab only estimates activity from its chat database (opened read-only).&lt;/p&gt;

&lt;h2&gt;
  
  
  The tool
&lt;/h2&gt;

&lt;p&gt;All of this is packaged as &lt;strong&gt;AI Usage Gauge&lt;/strong&gt; (Windows, EN/JA/ZH/KO UI). 7-day free trial, then a one-time purchase.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/aivex2024-spec/usagegauge" rel="noopener noreferrer"&gt;https://github.com/aivex2024-spec/usagegauge&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unofficial, independent tool — not affiliated with Anthropic. Numbers are estimates. Feedback on the estimation method is very welcome.&lt;/p&gt;

</description>
      <category>claude</category>
      <category>python</category>
      <category>windows</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
