tokenfishing 0.9.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tokenfishing-0.9.0/LICENSE +21 -0
- tokenfishing-0.9.0/PKG-INFO +523 -0
- tokenfishing-0.9.0/README.md +502 -0
- tokenfishing-0.9.0/pyproject.toml +41 -0
- tokenfishing-0.9.0/setup.cfg +4 -0
- tokenfishing-0.9.0/tests/test_aggregate.py +523 -0
- tokenfishing-0.9.0/tests/test_i18n.py +96 -0
- tokenfishing-0.9.0/tests/test_parser.py +157 -0
- tokenfishing-0.9.0/tests/test_plan_usage.py +212 -0
- tokenfishing-0.9.0/tests/test_state.py +234 -0
- tokenfishing-0.9.0/tests/test_statusline.py +127 -0
- tokenfishing-0.9.0/tests/test_themes.py +240 -0
- tokenfishing-0.9.0/tokenfishing/__init__.py +18 -0
- tokenfishing-0.9.0/tokenfishing/__main__.py +8 -0
- tokenfishing-0.9.0/tokenfishing/aggregate.py +568 -0
- tokenfishing-0.9.0/tokenfishing/config.py +97 -0
- tokenfishing-0.9.0/tokenfishing/i18n.py +148 -0
- tokenfishing-0.9.0/tokenfishing/parser.py +208 -0
- tokenfishing-0.9.0/tokenfishing/paths.py +90 -0
- tokenfishing-0.9.0/tokenfishing/plan_usage.py +298 -0
- tokenfishing-0.9.0/tokenfishing/popup.py +566 -0
- tokenfishing-0.9.0/tokenfishing/render.py +242 -0
- tokenfishing-0.9.0/tokenfishing/state.py +225 -0
- tokenfishing-0.9.0/tokenfishing/statusline.py +331 -0
- tokenfishing-0.9.0/tokenfishing/themes.py +1336 -0
- tokenfishing-0.9.0/tokenfishing.egg-info/PKG-INFO +523 -0
- tokenfishing-0.9.0/tokenfishing.egg-info/SOURCES.txt +28 -0
- tokenfishing-0.9.0/tokenfishing.egg-info/dependency_links.txt +1 -0
- tokenfishing-0.9.0/tokenfishing.egg-info/entry_points.txt +3 -0
- tokenfishing-0.9.0/tokenfishing.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 sozerong
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tokenfishing
|
|
3
|
+
Version: 0.9.0
|
|
4
|
+
Summary: An always-on-top pixel-art window showing your Claude Code usage
|
|
5
|
+
Author: sozerong
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/sozerong/token_fishing
|
|
8
|
+
Project-URL: Source, https://github.com/sozerong/token_fishing
|
|
9
|
+
Keywords: claude-code,token,usage,monitor,pixel-art
|
|
10
|
+
Classifier: Environment :: X11 Applications
|
|
11
|
+
Classifier: Environment :: Win32 (MS Windows)
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Utilities
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
# token fishing
|
|
23
|
+
|
|
24
|
+
*An always-on-top pixel-art window that shows how much Claude Code you have used.*
|
|
25
|
+
|
|
26
|
+
<img src="https://raw.githubusercontent.com/sozerong/token_fishing/main/docs/demo-fishing.gif" width="350" alt="Fishing theme: fish accumulate as tokens are spent">
|
|
27
|
+
|
|
28
|
+
[](https://www.python.org/downloads/)
|
|
29
|
+
[](LICENSE)
|
|
30
|
+
[](pyproject.toml)
|
|
31
|
+
|
|
32
|
+
How much have you spent in this 5-hour window, how fast are you burning it, and when
|
|
33
|
+
does it reset? `tokenfishing` answers all three in a small window that stays on top of
|
|
34
|
+
your editor. No dashboard to open, no browser tab to keep around.
|
|
35
|
+
|
|
36
|
+
| What you see | What it means |
|
|
37
|
+
|---|---|
|
|
38
|
+
| Number of things moving around | Tokens spent in the current 5-hour window |
|
|
39
|
+
| How fast they move | Burn rate (tokens per minute) |
|
|
40
|
+
| Height of the sun | Time left until the window resets — high at noon, low at dusk |
|
|
41
|
+
| Things gathered near the base | In *depletion* mode, how much of the window is already gone |
|
|
42
|
+
|
|
43
|
+
The tier climbs through five steps as the window fills. In the fishing theme that is
|
|
44
|
+
**empty basket → small fry → half basket → full basket → full boat**.
|
|
45
|
+
|
|
46
|
+
- **No runtime dependencies** — standard library only
|
|
47
|
+
- **Nothing leaves your machine** — every byte is read and rendered locally
|
|
48
|
+
- **Official numbers when available** — usage percentage and reset time come straight
|
|
49
|
+
from what Claude reports, not from a guess
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Table of contents
|
|
54
|
+
|
|
55
|
+
- [Themes](#themes)
|
|
56
|
+
- [Fishing spots](#fishing-spots)
|
|
57
|
+
- [Display modes](#display-modes)
|
|
58
|
+
- [Language](#language)
|
|
59
|
+
- [Requirements](#requirements)
|
|
60
|
+
- [Installation](#installation)
|
|
61
|
+
- [Updating](#updating)
|
|
62
|
+
- [Uninstalling](#uninstalling)
|
|
63
|
+
- [Usage](#usage)
|
|
64
|
+
- [Accuracy: where the numbers come from](#accuracy-where-the-numbers-come-from)
|
|
65
|
+
- [Configuration](#configuration)
|
|
66
|
+
- [Troubleshooting](#troubleshooting)
|
|
67
|
+
- [What it shows, and what it refuses to show](#what-it-shows-and-what-it-refuses-to-show)
|
|
68
|
+
- [How it works](#how-it-works)
|
|
69
|
+
- [Development](#development)
|
|
70
|
+
- [License](#license)
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Themes
|
|
75
|
+
|
|
76
|
+
The left button at the bottom of the window cycles through eight themes. Your choice is
|
|
77
|
+
remembered.
|
|
78
|
+
|
|
79
|
+

|
|
80
|
+
|
|
81
|
+
<img src="https://raw.githubusercontent.com/sozerong/token_fishing/main/docs/demo-themes.gif" width="350" alt="Cycling through the eight themes">
|
|
82
|
+
|
|
83
|
+
| Theme | Sky | Ground | Landmark | What is counted | When the window drains |
|
|
84
|
+
|---|---|---|---|---|---|
|
|
85
|
+
| Fishing | Sun | Rolling waves | Boat / pier / breakwater … | Fish | Catch fills a landing net |
|
|
86
|
+
| Village | Sun and clouds | Meadow | House | Villagers | New houses go up beside it |
|
|
87
|
+
| Ranch | Sun and clouds | Meadow | Barn | Animals | A round corral fills up |
|
|
88
|
+
| Space | Starfield and moon | *(none — open galaxy)* | Rocket | Stars | The rocket's flame grows |
|
|
89
|
+
| Garden | Sun and clouds | Meadow | Greenhouse | Flowers | A round flower bed fills up |
|
|
90
|
+
| Mine | Rock ceiling and lamp | Sleepers and rails | Mine shaft | Ore | An ore cart fills up |
|
|
91
|
+
| City | Scrolling skyline | Two-lane road | Tower | Cars | More buildings light up |
|
|
92
|
+
| Apiary | Sun and clouds | Flower meadow | Two hives and a smoker | Bees | *(fill only — see below)* |
|
|
93
|
+
|
|
94
|
+
**A theme only changes pictures and wording.** Every theme shares the same tier
|
|
95
|
+
thresholds, so the same usage always maps to the same tier no matter which one you pick.
|
|
96
|
+
This is enforced by `tests/test_themes.py`.
|
|
97
|
+
|
|
98
|
+
## Fishing spots
|
|
99
|
+
|
|
100
|
+
The fishing theme has its own background selector — a third button appears while the
|
|
101
|
+
fishing theme is active. Each spot changes the water colour, the sky, the props, and
|
|
102
|
+
which fish you catch.
|
|
103
|
+
|
|
104
|
+
| Spot | Setting | Props |
|
|
105
|
+
|---|---|---|
|
|
106
|
+
| Open sea | Deep blue water | Mast and flag, life ring, passing ship, gulls |
|
|
107
|
+
| Pier | Green harbour water | Lamp post, crates, coiled rope, gulls |
|
|
108
|
+
| Rocky shore | Cold, deep water | Seaweed, breaking spray, gulls |
|
|
109
|
+
| Breakwater | Steel-blue water | Red-striped lighthouse, tetrapods, gulls |
|
|
110
|
+
| Island | Tropical turquoise | Palm tree with coconuts, beach ball, shells, gulls |
|
|
111
|
+
| Car camping | Lakeside — land on the left | Open tailgate, camping chair, hanging lantern |
|
|
112
|
+
| Tent camping | Lakeside — land on the left | Tent, campfire with a pot, hanging lantern |
|
|
113
|
+
|
|
114
|
+
## Display modes
|
|
115
|
+
|
|
116
|
+
The right button switches between the two.
|
|
117
|
+
|
|
118
|
+
| Mode | Behaviour |
|
|
119
|
+
|---|---|
|
|
120
|
+
| Accumulate | The screen fills up as you spend. *"How much have I used?"* |
|
|
121
|
+
| Depletion | Starts full and empties as you spend. *"How much is left?"* |
|
|
122
|
+
|
|
123
|
+
The apiary has no depletion mode — a hive is something you fill, not something you empty
|
|
124
|
+
— so the button hides while that theme is on screen. Your choice is kept, and comes back
|
|
125
|
+
as soon as you move to another theme.
|
|
126
|
+
|
|
127
|
+
## Language
|
|
128
|
+
|
|
129
|
+
The window speaks English or Korean. It follows your system language on first run, and
|
|
130
|
+
you can pin it:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
tokenfishing --lang en
|
|
134
|
+
tokenfishing --lang ko
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The choice is remembered, so you only pass it once. It covers everything on screen — theme
|
|
138
|
+
names, tier labels, the buttons, the window title — and the statusline the hook
|
|
139
|
+
draws inside Claude Code.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Requirements
|
|
144
|
+
|
|
145
|
+
- Python 3.11 or newer
|
|
146
|
+
- tkinter (the standard-library GUI module)
|
|
147
|
+
- Claude Code history in `~/.claude/projects/`
|
|
148
|
+
|
|
149
|
+
Check what you have first:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
python3 -c "import sys, tkinter; print(sys.version)"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
If that prints `ModuleNotFoundError: No module named 'tkinter'`, install it:
|
|
156
|
+
|
|
157
|
+
| Platform | Command |
|
|
158
|
+
|---|---|
|
|
159
|
+
| Debian / Ubuntu | `sudo apt install python3-tk` |
|
|
160
|
+
| Fedora / RHEL | `sudo dnf install python3-tkinter` |
|
|
161
|
+
| Arch | `sudo pacman -S tk` |
|
|
162
|
+
| macOS (Homebrew Python) | `brew install python-tk` |
|
|
163
|
+
| macOS (python.org build) | already included |
|
|
164
|
+
| Windows | already included |
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Installation
|
|
169
|
+
|
|
170
|
+
### With pip
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
pip3 install tokenfishing
|
|
174
|
+
tokenfishing --install-statusline
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Two commands land on your `PATH`: `tokenfishing` (the window) and `tokenfishing-console`
|
|
178
|
+
(plain text). The second line registers the Claude Code statusline hook — without it the
|
|
179
|
+
window falls back to an estimate, so do not skip it.
|
|
180
|
+
|
|
181
|
+
Prefer an isolated install, or want to run it without installing at all:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
pipx install tokenfishing # isolated from the rest of your Python
|
|
185
|
+
uvx tokenfishing # run it once, install nothing
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
To track `main` instead of the last release, install
|
|
189
|
+
`git+https://github.com/sozerong/token_fishing.git` under any of the commands above.
|
|
190
|
+
|
|
191
|
+
> If your shell answers `command not found: tokenfishing` after a `pip3 install --user`,
|
|
192
|
+
> the user script directory is not on your `PATH`. Either add
|
|
193
|
+
> `python3 -m site --user-base`'s `bin` (Windows: `Scripts`) to it, use `pipx`, or run the
|
|
194
|
+
> installer script below, which does that part for you.
|
|
195
|
+
|
|
196
|
+
### With the installer script
|
|
197
|
+
|
|
198
|
+
Use this if you would rather not touch `PATH` yourself. It picks a Python 3.11+
|
|
199
|
+
interpreter, warns you if tkinter is missing (with the exact command for your platform),
|
|
200
|
+
installs through `pipx` if you have it and `pip --user` otherwise, **adds the install
|
|
201
|
+
directory to your `PATH`** (in your shell rc file on macOS/Linux, in your user environment
|
|
202
|
+
on Windows, and in the current session so it works immediately), and registers the
|
|
203
|
+
statusline hook.
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
git clone https://github.com/sozerong/token_fishing.git
|
|
207
|
+
cd token_fishing
|
|
208
|
+
bash install.sh
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
```powershell
|
|
212
|
+
git clone https://github.com/sozerong/token_fishing.git
|
|
213
|
+
cd token_fishing
|
|
214
|
+
powershell -ExecutionPolicy Bypass -File install.ps1
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
> **Do not run the installer with `sudo`** (or from an elevated PowerShell). Everything
|
|
218
|
+
> this tool touches lives in your own home directory — `~/.claude`. Installing as root
|
|
219
|
+
> would register the statusline hook in *root's* home, and the tool would never find your
|
|
220
|
+
> usage data. The script refuses to run as root for exactly this reason.
|
|
221
|
+
|
|
222
|
+
For hacking on it, install the checkout in place with `pip3 install -e .`, or just run
|
|
223
|
+
`python3 -m tokenfishing` from the repository root.
|
|
224
|
+
|
|
225
|
+
## Updating
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
pip3 install --upgrade tokenfishing
|
|
229
|
+
tokenfishing --install-statusline
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
With pipx it is `pipx upgrade tokenfishing`. If you installed from the git URL, add
|
|
233
|
+
`--force-reinstall` — a git URL carries no version for pip to compare, so `--upgrade`
|
|
234
|
+
alone would decide you are already up to date.
|
|
235
|
+
|
|
236
|
+
Re-register the hook after every update: it stores an **absolute path** to the installed
|
|
237
|
+
file, and an update that moves the install location would otherwise leave Claude Code
|
|
238
|
+
running a file that no longer exists. If you installed with the script, `bash install.sh
|
|
239
|
+
update` (Windows: `powershell -File install.ps1 update`) pulls, reinstalls and re-registers
|
|
240
|
+
in one step.
|
|
241
|
+
|
|
242
|
+
## Uninstalling
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
tokenfishing --uninstall-statusline
|
|
246
|
+
pip3 uninstall tokenfishing # or: pipx uninstall tokenfishing
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
The first line unregisters the statusline hook from `~/.claude/settings.json`, leaving the
|
|
250
|
+
rest of your settings alone. Run it **before** removing the package — afterwards the
|
|
251
|
+
command is gone. Two files are left behind for you to delete if you want them gone:
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
rm ~/.claude/tokenfishing-config.json ~/.claude/tokenfishing-limits.json
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
`bash install.sh uninstall` (Windows: `powershell -File install.ps1 uninstall`) does all of
|
|
258
|
+
that, plus taking the `PATH` entry back out.
|
|
259
|
+
|
|
260
|
+
Your Claude Code transcripts under `~/.claude/projects` are never touched.
|
|
261
|
+
|
|
262
|
+
### Using it on more than one machine
|
|
263
|
+
|
|
264
|
+
Install on each machine — the transcripts it reads are local, so there is nothing to
|
|
265
|
+
sync. See [Accuracy](#accuracy-where-the-numbers-come-from) for what does and
|
|
266
|
+
does not match across machines.
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## Usage
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
tokenfishing
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
An always-on-top window opens and refreshes every 10 seconds. Close the window to quit.
|
|
277
|
+
|
|
278
|
+
### Command-line options
|
|
279
|
+
|
|
280
|
+
```
|
|
281
|
+
tokenfishing [options]
|
|
282
|
+
|
|
283
|
+
-d, --detach run in the background and return the shell immediately
|
|
284
|
+
--lang ko|en language for the window (remembered; defaults to your system)
|
|
285
|
+
--debug print diagnostics to stderr
|
|
286
|
+
--doctor diagnose the usage data sources and exit
|
|
287
|
+
--install-statusline
|
|
288
|
+
register the Claude Code statusline hook
|
|
289
|
+
-V, --version print the version
|
|
290
|
+
-h, --help print this help
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Use `-d` to keep your terminal free. On Windows it re-launches through `pythonw` so no
|
|
294
|
+
console window tags along; on macOS and Linux it detaches into a new session and survives
|
|
295
|
+
closing the terminal.
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
tokenfishing -d
|
|
299
|
+
# running in background (PID 18556)
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
To stop it, close the window or kill the process:
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
pkill -f "tokenfishing" # macOS / Linux
|
|
306
|
+
taskkill /F /IM pythonw.exe # Windows
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Console output
|
|
310
|
+
|
|
311
|
+
For a terminal-only summary:
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
tokenfishing-console
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## Accuracy: where the numbers come from
|
|
320
|
+
|
|
321
|
+
Everything is derived from the JSONL transcripts Claude Code writes under
|
|
322
|
+
`~/.claude/projects/`. Parsing them correctly is the whole point of this project, and the
|
|
323
|
+
parser is covered item-by-item (input / output / cache-write / cache-read) by the tests in
|
|
324
|
+
`tests/`, on hand-written fixtures that pin down each of the ways a naive reader gets the
|
|
325
|
+
number wrong.
|
|
326
|
+
|
|
327
|
+
The **usage percentage and reset time** can come from three places, and the window title
|
|
328
|
+
always tells you which one is in play:
|
|
329
|
+
|
|
330
|
+
| Source | Title shows | How exact |
|
|
331
|
+
|---|---|---|
|
|
332
|
+
| Statusline hook | `official·hook` | **Exact**, as of the last time Claude Code drew its statusline |
|
|
333
|
+
| Desktop app history | `official·app` | Exact as of the app's last write (roughly every 15 minutes) |
|
|
334
|
+
| Neither | `estimate (no official numbers)` | Estimated from a learned limit — may drift |
|
|
335
|
+
|
|
336
|
+
Whichever of the two was captured **more recently** wins, and the 5-hour and weekly figures
|
|
337
|
+
always come from the same capture so they never describe two different moments. If the
|
|
338
|
+
number is more than 15 minutes old, the title says how old — `official·hook 47분 전` — so a
|
|
339
|
+
figure that looks out of date is visibly out of date rather than quietly wrong.
|
|
340
|
+
|
|
341
|
+
Run `tokenfishing --install-statusline` once to register the hook. This is the
|
|
342
|
+
recommended setup: **numbers taken through the Claude Code CLI hook are exact.**
|
|
343
|
+
|
|
344
|
+
Claude Code has only one statusline slot. If something else already owns it — a plugin,
|
|
345
|
+
your own script — the installer does not take it: it **chains**, so your existing
|
|
346
|
+
statusline keeps drawing exactly what it drew before while this tool reads the official
|
|
347
|
+
numbers off the same input. Uninstalling puts the original command back. If the original
|
|
348
|
+
ever disappears (plugin paths are per-session), the chain falls back to printing this
|
|
349
|
+
tool's own line rather than an empty statusline.
|
|
350
|
+
|
|
351
|
+
### Known sources of drift
|
|
352
|
+
|
|
353
|
+
- **Usage from the Claude desktop app, claude.ai on the web, or mobile counts against the
|
|
354
|
+
same 5-hour limit but leaves no trace in the local JSONL.** If a session was opened by
|
|
355
|
+
usage this tool cannot see, the estimated window start — and therefore the reset time —
|
|
356
|
+
can be off. Pin it with `TOKENFISHING_RESET_AT` when that matters.
|
|
357
|
+
- Estimated values are never presented as certain. They are prefixed with `~` and drawn
|
|
358
|
+
in a different colour.
|
|
359
|
+
|
|
360
|
+
### Across several machines
|
|
361
|
+
|
|
362
|
+
Transcripts are written by the Claude Code CLI on the machine that ran it, and nothing
|
|
363
|
+
syncs them. So if you use the same account on a laptop and a desktop, each window shows a
|
|
364
|
+
different slice — but not of everything:
|
|
365
|
+
|
|
366
|
+
| Value | Same on every machine? |
|
|
367
|
+
|---|---|
|
|
368
|
+
| Usage percentage, time until reset, weekly percentage | **Yes** — reported per account |
|
|
369
|
+
| Token count, request count, burn rate, weekly tokens | **No** — only what that machine did |
|
|
370
|
+
| Theme and mode selection | **No** — the config file is local too |
|
|
371
|
+
|
|
372
|
+
There is no combined view. Install the statusline hook on **every** machine: without it a
|
|
373
|
+
machine falls back to estimating the limit from its own partial token count, which comes
|
|
374
|
+
out too low.
|
|
375
|
+
|
|
376
|
+
The percentages agree only as far as each machine's last capture goes. The hook updates
|
|
377
|
+
when Claude Code draws its statusline, so a machine you have not touched for hours is
|
|
378
|
+
still holding the figure from back then — correct for the account, but stale. That is why
|
|
379
|
+
the title prints the age once it passes 15 minutes: when two machines disagree, the one
|
|
380
|
+
with the fresher capture is the one to believe.
|
|
381
|
+
|
|
382
|
+
### Does it work outside Korea?
|
|
383
|
+
|
|
384
|
+
Yes. Concretely:
|
|
385
|
+
|
|
386
|
+
- **Time zones.** The 5-hour window is computed entirely in UTC from UTC timestamps, so it
|
|
387
|
+
is correct in any time zone. The weekly figure uses your *local* midnight.
|
|
388
|
+
- **Locales and encodings.** Transcripts are read as UTF-8 with replacement, and console
|
|
389
|
+
output is forced to UTF-8, so a non-UTF-8 terminal code page will not crash it.
|
|
390
|
+
- **Paths.** `~/.claude` on macOS, Linux and Windows, plus `CLAUDE_CONFIG_DIR`, plus the
|
|
391
|
+
MSIX-redirected location the Windows Store build of the Claude desktop app writes to.
|
|
392
|
+
- **Weekly reset day.** Defaults to Tuesday, which is what one observed account used. This
|
|
393
|
+
varies per account rather than per country — set `TOKENFISHING_WEEKLY_RESET_DAY` if your
|
|
394
|
+
official screen says a different day.
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## Configuration
|
|
399
|
+
|
|
400
|
+
Three settings are toggled in the window itself (theme, fishing spot, display mode) and
|
|
401
|
+
stored in `~/.claude/tokenfishing-config.json`. Everything else is an environment
|
|
402
|
+
variable:
|
|
403
|
+
|
|
404
|
+
| Variable | Effect |
|
|
405
|
+
|---|---|
|
|
406
|
+
| `TOKENFISHING_RESET_AT` | Pin the 5-hour reset time (e.g. `20:17`, or an ISO timestamp) when the estimate is off |
|
|
407
|
+
| `TOKENFISHING_WEEKLY_RESET_DAY` | Weekly reset weekday, `0` = Monday … `6` = Sunday. Default `1` (Tuesday) |
|
|
408
|
+
| `CLAUDE_CONFIG_DIR` | Look for Claude Code data somewhere other than `~/.claude` |
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## Troubleshooting
|
|
413
|
+
|
|
414
|
+
**The title says `estimate (no official numbers)`.**
|
|
415
|
+
Neither source was found. Register the hook with `tokenfishing --install-statusline`, or
|
|
416
|
+
open the Claude desktop app once so it writes its usage history. Then run
|
|
417
|
+
`tokenfishing --doctor` to see exactly which files were found.
|
|
418
|
+
|
|
419
|
+
**The title says `official·app` and the reset time is marked `~`.**
|
|
420
|
+
Working as intended: the desktop app records percentages but not window boundaries, so the
|
|
421
|
+
reset time has to be estimated from that machine's own transcripts. The percentages are
|
|
422
|
+
account-wide and will agree with your other machines; the reset time will not. Install the
|
|
423
|
+
statusline hook on that machine to make it exact.
|
|
424
|
+
|
|
425
|
+
**Nothing appears / the window opens empty.**
|
|
426
|
+
There is no active 5-hour window — nothing has been sent recently. The window will say so.
|
|
427
|
+
|
|
428
|
+
**The reset time looks wrong.**
|
|
429
|
+
Most likely you used Claude somewhere this tool cannot see (web, mobile, desktop app).
|
|
430
|
+
Read the reset time off the official usage screen and pin it:
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
TOKENFISHING_RESET_AT=20:17 tokenfishing
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
**The weekly total looks wrong.**
|
|
437
|
+
Your account resets on a different weekday. Set `TOKENFISHING_WEEKLY_RESET_DAY`.
|
|
438
|
+
|
|
439
|
+
**Diagnostics.**
|
|
440
|
+
|
|
441
|
+
```bash
|
|
442
|
+
tokenfishing --debug
|
|
443
|
+
# [tokenfishing] start fill=official official=app pct=64.0 left=270 mode=depletion
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
## What it shows, and what it refuses to show
|
|
449
|
+
|
|
450
|
+
Included, because it is plain aggregation:
|
|
451
|
+
|
|
452
|
+
- Tokens used in the current 5-hour window, request count, burn rate
|
|
453
|
+
- Per-model breakdown, weekly total, all-time total
|
|
454
|
+
- The official usage percentage and reset time, when they are available
|
|
455
|
+
|
|
456
|
+
Deliberately **not** included, because it would require guessing:
|
|
457
|
+
|
|
458
|
+
- **Percent-of-limit derived from an estimated limit.** A P90-estimated limit was measured
|
|
459
|
+
at 82.6% against an official screen showing 35% — off by more than double.
|
|
460
|
+
- **Pace and forecast**, which are built on that estimate.
|
|
461
|
+
- **Dollar cost.** It would mean hard-coding a price table that silently goes stale, and
|
|
462
|
+
subscription users do not pay per token in the first place.
|
|
463
|
+
|
|
464
|
+
---
|
|
465
|
+
|
|
466
|
+
## How it works
|
|
467
|
+
|
|
468
|
+
```
|
|
469
|
+
~/.claude/projects/**/*.jsonl
|
|
470
|
+
│ streamed line by line, never read whole
|
|
471
|
+
▼
|
|
472
|
+
[ parser ] normalises one usage record per request
|
|
473
|
+
│ · de-duplicates by requestId (one turn spans several lines)
|
|
474
|
+
│ · takes max(output_tokens) per request (sub-agent files grow)
|
|
475
|
+
│ · includes subagents/agent-*.jsonl (separate files, no double count)
|
|
476
|
+
▼
|
|
477
|
+
UsageEntry
|
|
478
|
+
│
|
|
479
|
+
▼
|
|
480
|
+
[ aggregate ] 5-hour rolling window, burn rate, weekly totals
|
|
481
|
+
│
|
|
482
|
+
▼
|
|
483
|
+
GameState ──► [ themes ] ──► pixels
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
The parser is deliberately a thin layer: nothing above it knows what a JSONL line looks
|
|
487
|
+
like, and the drawing layer never invents a number — it only translates what `aggregate`
|
|
488
|
+
produced into a metaphor.
|
|
489
|
+
|
|
490
|
+
The transcript format is an undocumented internal detail with no version field, so the
|
|
491
|
+
parser ignores unknown fields rather than rejecting records, and keeps a counter of
|
|
492
|
+
never-before-seen keys as an early warning that the format changed.
|
|
493
|
+
|
|
494
|
+
---
|
|
495
|
+
|
|
496
|
+
## Development
|
|
497
|
+
|
|
498
|
+
```bash
|
|
499
|
+
git clone https://github.com/sozerong/token_fishing.git
|
|
500
|
+
cd token_fishing
|
|
501
|
+
python3 -m pytest tests -q
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
Self-checks for the drawing layers run standalone:
|
|
505
|
+
|
|
506
|
+
```bash
|
|
507
|
+
python3 -m tokenfishing.themes # 8 themes, 7 fishing spots
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
The token tests must keep passing **item by item** — input, output, cache-write and
|
|
511
|
+
cache-read each on their own. The two ways of misreading the transcripts push the total in
|
|
512
|
+
opposite directions, so a test that only compares the sum can pass while both halves are
|
|
513
|
+
wrong. A screen that looks nice but reports the wrong number is a regression, not a
|
|
514
|
+
feature.
|
|
515
|
+
|
|
516
|
+
All test fixtures are hand-written synthetic data. Real transcripts contain real
|
|
517
|
+
conversations and are never committed.
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
## License
|
|
522
|
+
|
|
523
|
+
MIT — see [LICENSE](LICENSE).
|