ctxprof 0.1.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.
- ctxprof-0.1.0/.gitignore +8 -0
- ctxprof-0.1.0/LICENSE +21 -0
- ctxprof-0.1.0/PKG-INFO +294 -0
- ctxprof-0.1.0/README.md +258 -0
- ctxprof-0.1.0/claude_context_tools/__init__.py +3 -0
- ctxprof-0.1.0/claude_context_tools/audit.py +697 -0
- ctxprof-0.1.0/claude_context_tools/cli.py +53 -0
- ctxprof-0.1.0/claude_context_tools/dashboard.py +1175 -0
- ctxprof-0.1.0/claude_context_tools/data/claude-statusline-heartbeat.sh +343 -0
- ctxprof-0.1.0/claude_context_tools/install.py +103 -0
- ctxprof-0.1.0/claude_context_tools/pricing.py +110 -0
- ctxprof-0.1.0/pyproject.toml +38 -0
- ctxprof-0.1.0/tests/fixtures/state/00000000-aaaa-bbbb-cccc-000000000001.json +20 -0
- ctxprof-0.1.0/tests/fixtures/state/steps/00000000-aaaa-bbbb-cccc-000000000001.jsonl +4 -0
- ctxprof-0.1.0/tests/fixtures/transcript/00000000-aaaa-bbbb-cccc-000000000001.jsonl +10 -0
- ctxprof-0.1.0/tests/make_fixtures.py +128 -0
- ctxprof-0.1.0/tests/test_claude_context_tools.py +251 -0
ctxprof-0.1.0/.gitignore
ADDED
ctxprof-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Maria Dubyaga
|
|
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.
|
ctxprof-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ctxprof
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Live dashboard and offline context/cache audit for Claude Code sessions
|
|
5
|
+
Project-URL: Homepage, https://github.com/kraftaa/claude_workflow
|
|
6
|
+
Project-URL: Source, https://github.com/kraftaa/claude_workflow/tree/main/claude-context-tools
|
|
7
|
+
Author: Maria Dubyaga
|
|
8
|
+
License: MIT License
|
|
9
|
+
|
|
10
|
+
Copyright (c) 2026 Maria Dubyaga
|
|
11
|
+
|
|
12
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
in the Software without restriction, including without limitation the rights
|
|
15
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
furnished to do so, subject to the following conditions:
|
|
18
|
+
|
|
19
|
+
The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
copies or substantial portions of the Software.
|
|
21
|
+
|
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
SOFTWARE.
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Keywords: cache,claude,claude-code,observability,statusline,tokens
|
|
31
|
+
Classifier: Environment :: Console
|
|
32
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
33
|
+
Classifier: Programming Language :: Python :: 3
|
|
34
|
+
Requires-Python: >=3.9
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# ctxprof
|
|
38
|
+
|
|
39
|
+
A **context profiler for Claude Code sessions** (package `ctxprof`, command `ctx`) — a small, dependency-free CLI
|
|
40
|
+
(`ctx`) for understanding where a session's context and cache budget actually
|
|
41
|
+
goes. Like a CPU/memory profiler, but for tokens. Several lenses:
|
|
42
|
+
|
|
43
|
+
- **Live** — `ctx dashboard` (all sessions) / `ctx watch` (one session): how much,
|
|
44
|
+
right now.
|
|
45
|
+
- **Per-turn** — `ctx steps` / `ctx digest` / `ctx rates`: what each turn cost and
|
|
46
|
+
where the money went.
|
|
47
|
+
- **Offline** — `ctx audit`: why one session burned context/cache, after the fact.
|
|
48
|
+
- **Explain** — `ctx explain`: ranks a session's biggest *avoidable* costs
|
|
49
|
+
(idle cache rebuilds, repeated reads, large inserts) with $ impact and a fix
|
|
50
|
+
for each, splitting measured (real billed rebuilds) from estimated waste.
|
|
51
|
+
|
|
52
|
+
All read the same per-session **heartbeat** files that a statusline writes. The
|
|
53
|
+
statusline is the only piece that runs inside Claude Code; everything else is an
|
|
54
|
+
ordinary command you run in any terminal (or via `!ctx …` / `/ctx` inside Claude).
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
Claude Code session
|
|
58
|
+
│ (each statusline refresh)
|
|
59
|
+
▼
|
|
60
|
+
heartbeat statusline ──writes──► ~/.claude/session-status/<session>.json
|
|
61
|
+
~/.claude/session-status/steps/<session>.jsonl
|
|
62
|
+
▲ │
|
|
63
|
+
│ one stdout line ├──► ctx dashboard (live, all sessions)
|
|
64
|
+
in-app status bar └──► ctx audit (offline, one session)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Requires `python3` (3.9+). The heartbeat statusline also needs `jq` + `awk`
|
|
68
|
+
(standard on macOS/Linux).
|
|
69
|
+
|
|
70
|
+
## Install
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Recommended: isolated global install with pipx
|
|
74
|
+
pipx install ctxprof # from PyPI
|
|
75
|
+
# or from a git checkout (replace <your-repo-url>):
|
|
76
|
+
pipx install "git+<your-repo-url>#subdirectory=claude-context-tools"
|
|
77
|
+
|
|
78
|
+
# Or plain pip (ideally in a venv)
|
|
79
|
+
pip install ctxprof
|
|
80
|
+
|
|
81
|
+
# Or straight from a clone, editable (path is the directory in the repo)
|
|
82
|
+
pip install -e claude-context-tools
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
This installs one command, `ctx`, an umbrella with subcommands:
|
|
86
|
+
`dashboard`, `watch`, `explain`, `show`, `steps`, `digest`, `rates`, `audit`,
|
|
87
|
+
`tui`, `install` (plus `statusline`/`hook` plumbing). Run `ctx` with no
|
|
88
|
+
arguments to see them all.
|
|
89
|
+
|
|
90
|
+
No clone needed after install. To run without installing, use
|
|
91
|
+
`python3 -m claude_context_tools.cli <subcommand>` from this directory.
|
|
92
|
+
|
|
93
|
+
## When to run what
|
|
94
|
+
|
|
95
|
+
| I want to… | Run |
|
|
96
|
+
|---|---|
|
|
97
|
+
| See what all my open sessions cost **right now** | `ctx dashboard` |
|
|
98
|
+
| Find **which session is the money sink** | `ctx digest` (ranks sessions by cost) |
|
|
99
|
+
| Know **why a session got expensive + how to fix it** | `ctx explain <id>` |
|
|
100
|
+
| **Watch one session live** while I work (split pane) | `ctx watch <id>` |
|
|
101
|
+
| See **per-turn** costs / catch a spike as it happens | `ctx steps` |
|
|
102
|
+
| **Deep offline** breakdown of one session's transcript | `ctx audit <id>` |
|
|
103
|
+
| Check **prices / keep-warm-vs-rebuild** math | `ctx rates` |
|
|
104
|
+
|
|
105
|
+
Typical flow: **`ctx digest`** to see which session is costing the most →
|
|
106
|
+
**`ctx explain <id>`** on that one to see the avoidable costs and the fix.
|
|
107
|
+
|
|
108
|
+
**Which session do the single-session commands act on?** With no id they pick the
|
|
109
|
+
**newest** (most recently active) session, and the **header line shows its name**
|
|
110
|
+
so you can confirm. To target another, pass a **session id or prefix** — get the
|
|
111
|
+
ids from `ctx dashboard` (the `ID` column) and run `ctx show <id>` to see a
|
|
112
|
+
session's repo/cwd. No time window: `ctx explain`/`audit` cover the **whole
|
|
113
|
+
session**; `ctx digest --since 2h` and `ctx steps --limit N` are the windowed ones.
|
|
114
|
+
|
|
115
|
+
## Use it
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# Live table of all active sessions (Ctrl-C to quit):
|
|
119
|
+
ctx dashboard
|
|
120
|
+
|
|
121
|
+
# Print once and exit (good for tmux panes / scripts):
|
|
122
|
+
ctx dashboard --refresh 0
|
|
123
|
+
|
|
124
|
+
# Include sessions idle past the stale timeout (shown dimmed, with `!`):
|
|
125
|
+
ctx dashboard --include-stale
|
|
126
|
+
|
|
127
|
+
# Details + a ready-to-run audit command for one session (or the newest):
|
|
128
|
+
ctx show
|
|
129
|
+
ctx show <session-id-or-prefix>
|
|
130
|
+
|
|
131
|
+
# Live single-session panel + recommendations (the "sidebar"; run in a split pane):
|
|
132
|
+
ctx watch
|
|
133
|
+
ctx watch <session-id>
|
|
134
|
+
|
|
135
|
+
# Rank a session's biggest avoidable costs (the "profiler" view):
|
|
136
|
+
ctx explain
|
|
137
|
+
ctx explain <session-id> --top 5
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
`ctx explain` looks like:
|
|
141
|
+
|
|
142
|
+
```text
|
|
143
|
+
ctx explain Make Claude workflow utility installable … — top avoidable costs
|
|
144
|
+
|
|
145
|
+
1. $ 51.29 Cache rebuilds after idle >5min (×15)
|
|
146
|
+
→ rebuilt ~7.2M cache tok — /clear before breaks; keep sessions short
|
|
147
|
+
2. $ 0.02 ~est Large Read result (~35.2k tok)
|
|
148
|
+
→ summarize/cap big output before it enters context
|
|
149
|
+
3. $ 0.01 ~est Re-reading dashboard.py 27×
|
|
150
|
+
→ ~12.5k redundant tok — read targeted ranges / keep a summary
|
|
151
|
+
|
|
152
|
+
Total avoidable: ~$51.32 ($51.29 measured + $0.03 estimated)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
# Recent per-turn cost feed across all sessions (catches expensive turns):
|
|
157
|
+
ctx steps
|
|
158
|
+
ctx steps --limit 40
|
|
159
|
+
ctx steps --json --limit 200 # machine-readable (feed to Claude to analyze)
|
|
160
|
+
|
|
161
|
+
# Rollup summary: totals, top-cost turns, biggest cache writes, longest gaps:
|
|
162
|
+
ctx digest --since 2h
|
|
163
|
+
|
|
164
|
+
# Price table + keep-warm-vs-rebuild economics for the current context:
|
|
165
|
+
ctx rates
|
|
166
|
+
ctx rates --context 100000 --model "Opus 4.8"
|
|
167
|
+
# (edit prices without touching code via ~/.claude/ctx-pricing.json)
|
|
168
|
+
|
|
169
|
+
# Interactive browser (scroll/filter/drill into a turn):
|
|
170
|
+
ctx tui
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Inside Claude Code
|
|
174
|
+
|
|
175
|
+
Prefix any command with `!` in the Claude prompt to run it and drop the output
|
|
176
|
+
into the conversation, or use the bundled `/ctx` slash command:
|
|
177
|
+
|
|
178
|
+
```text
|
|
179
|
+
!ctx digest --since 2h
|
|
180
|
+
/ctx steps
|
|
181
|
+
/ctx steps --json --limit 200 # then ask Claude to summarize the big turns
|
|
182
|
+
|
|
183
|
+
# Offline "why did it burn?" audit:
|
|
184
|
+
ctx audit --latest
|
|
185
|
+
ctx audit --session <session-id>
|
|
186
|
+
ctx audit --transcript ~/.claude/projects/<proj>/<session>.jsonl
|
|
187
|
+
ctx audit --latest --json # machine-readable
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Turn on the heartbeat (one-time)
|
|
191
|
+
|
|
192
|
+
The dashboard and audit need a statusline that writes heartbeat files. Claude
|
|
193
|
+
Code supports **exactly one** `statusLine`, so this never silently replaces an
|
|
194
|
+
existing one:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
ctx install --statusline # safe: refuses if a statusLine exists
|
|
198
|
+
ctx install --statusline --force # replace it (writes a timestamped backup)
|
|
199
|
+
ctx install --print-path # just print the heartbeat script path
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
If you already have a statusline you like, keep it — just make it also write the
|
|
203
|
+
`~/.claude/session-status/<session>.json` and `steps/<session>.jsonl` shapes (see
|
|
204
|
+
`claude_context_tools/data/claude-statusline-heartbeat.sh` for the exact fields)
|
|
205
|
+
and these tools will read it. To wire it by hand instead:
|
|
206
|
+
|
|
207
|
+
```json
|
|
208
|
+
{
|
|
209
|
+
"statusLine": {
|
|
210
|
+
"type": "command",
|
|
211
|
+
"command": "bash /ABSOLUTE/PATH/TO/claude-statusline-heartbeat.sh"
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Global vs per-repo
|
|
217
|
+
|
|
218
|
+
- **Global** (all sessions): set the heartbeat in `~/.claude/settings.json`.
|
|
219
|
+
This is what `ctx install --statusline` does. Usual choice.
|
|
220
|
+
- **Per-repo**: set `statusLine` in a project's `.claude/settings.json`; it wins
|
|
221
|
+
for sessions in that repo. Use an absolute path to the heartbeat script.
|
|
222
|
+
|
|
223
|
+
## What the audit reports
|
|
224
|
+
|
|
225
|
+
- **What burned context** — per-category token estimates (assistant/user text,
|
|
226
|
+
thinking, tool inputs, per-tool results, attachments).
|
|
227
|
+
- **Attachment breakdown** — system-injected context (skill listings, hook
|
|
228
|
+
output, deferred tool lists) totalled separately.
|
|
229
|
+
- **Repeated file reads / commands / duplicate output blobs** — content paid for
|
|
230
|
+
more than once.
|
|
231
|
+
- **Large pasted logs / large tool results** and **subagent report weight**.
|
|
232
|
+
- **Cache telemetry** — per-turn behavior, separating expected initial
|
|
233
|
+
**warm-up** from likely mid-session **invalidation** (cache was warm, then
|
|
234
|
+
largely rewritten).
|
|
235
|
+
- **Recommendations** derived from the findings.
|
|
236
|
+
|
|
237
|
+
### Honest limits
|
|
238
|
+
|
|
239
|
+
- Token counts in `ctx audit` are **estimates** (`~4 chars/token`, scaled by the
|
|
240
|
+
model's tokenizer — ×1.35 for Opus 4.7+; override with `--token-factor`). The
|
|
241
|
+
live per-turn numbers (statusline, `ctx steps`, COST) are Claude Code's own
|
|
242
|
+
exact usage fields, not estimates.
|
|
243
|
+
- Cache analysis is **heuristic and token-level** — the heartbeat exposes per-turn
|
|
244
|
+
token *counts*, not cache-block boundaries, so it flags *likely* invalidation,
|
|
245
|
+
not exact cache-block attribution.
|
|
246
|
+
- **Unknown is not zero.** With no step file, the audit says per-turn cache
|
|
247
|
+
behavior is unknown rather than reporting `0`.
|
|
248
|
+
|
|
249
|
+
## Dashboard notes
|
|
250
|
+
|
|
251
|
+
- **LABEL** is the session name/task (from `session_name`) so you identify
|
|
252
|
+
sessions by what they're doing, not by UUID. **ID** is a short id prefix
|
|
253
|
+
(enough for `ctx show <prefix>`).
|
|
254
|
+
- **CONTEXT** is a fill bar + percent; **RECENT** is a per-session sparkline of
|
|
255
|
+
the last few turns' token volume, colored by cache-**read** share
|
|
256
|
+
(green = cheap cache hits, cyan = fresh input / rebuild) so big-write rebuild
|
|
257
|
+
turns show as expensive, not green.
|
|
258
|
+
- The table is **responsive**: on narrow terminals lower-priority columns
|
|
259
|
+
(MODE, AGENT, 5H/7D, IN/OUT, API, DUR, CHG) drop out, always keeping REPO,
|
|
260
|
+
the CONTEXT bar, COST, RECENT and LABEL.
|
|
261
|
+
- `ctx steps` is a separate cross-session **per-turn** feed (STEP, CTX, tokens,
|
|
262
|
+
CACHE R/W, COST, APIΔ, GAP) — costs over $0.25/$1.00 a turn are highlighted.
|
|
263
|
+
- **Stale sessions** are hidden by default; the header shows `stale-hidden:N` so
|
|
264
|
+
you know they exist. `--include-stale` shows them dimmed with an `!`. The
|
|
265
|
+
cutoff is `CLAUDE_STATUS_STALE_AFTER_SECONDS` (default 900s). An open session
|
|
266
|
+
that's idle (not generating turns) stops writing heartbeats and goes stale.
|
|
267
|
+
- **COST and TIME are cumulative session totals**; **live-ctx / IN / OUT** are
|
|
268
|
+
the current context, not lifetime. An old session legitimately shows a large
|
|
269
|
+
frozen cost/time — its lifetime total at the last heartbeat, not ongoing spend.
|
|
270
|
+
|
|
271
|
+
## Files written
|
|
272
|
+
|
|
273
|
+
| Path | Written by | Used by |
|
|
274
|
+
|---|---|---|
|
|
275
|
+
| `~/.claude/session-status/<session>.json` | heartbeat statusline | dashboard, audit |
|
|
276
|
+
| `~/.claude/session-status/steps/<session>.jsonl` | heartbeat statusline | audit (cache telemetry) |
|
|
277
|
+
| Claude transcript JSONL (under `~/.claude/projects/`) | Claude Code | audit |
|
|
278
|
+
|
|
279
|
+
Override the state dir with `CLAUDE_STATUS_STATE_DIR`. Nothing is deleted
|
|
280
|
+
automatically. These tools only ever **read** your transcripts — they never
|
|
281
|
+
modify or upload them.
|
|
282
|
+
|
|
283
|
+
## Tests
|
|
284
|
+
|
|
285
|
+
Fixture-based smoke tests, stdlib only (synthetic data, generated by
|
|
286
|
+
`tests/make_fixtures.py` — no private content):
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
python3 tests/test_claude_context_tools.py
|
|
290
|
+
python3 tests/make_fixtures.py # regenerate fixtures after changing shapes
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
See [`../03-harnesses.md`](../03-harnesses.md) → "Status line" for how this fits
|
|
294
|
+
the broader harness guidance.
|
ctxprof-0.1.0/README.md
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# ctxprof
|
|
2
|
+
|
|
3
|
+
A **context profiler for Claude Code sessions** (package `ctxprof`, command `ctx`) — a small, dependency-free CLI
|
|
4
|
+
(`ctx`) for understanding where a session's context and cache budget actually
|
|
5
|
+
goes. Like a CPU/memory profiler, but for tokens. Several lenses:
|
|
6
|
+
|
|
7
|
+
- **Live** — `ctx dashboard` (all sessions) / `ctx watch` (one session): how much,
|
|
8
|
+
right now.
|
|
9
|
+
- **Per-turn** — `ctx steps` / `ctx digest` / `ctx rates`: what each turn cost and
|
|
10
|
+
where the money went.
|
|
11
|
+
- **Offline** — `ctx audit`: why one session burned context/cache, after the fact.
|
|
12
|
+
- **Explain** — `ctx explain`: ranks a session's biggest *avoidable* costs
|
|
13
|
+
(idle cache rebuilds, repeated reads, large inserts) with $ impact and a fix
|
|
14
|
+
for each, splitting measured (real billed rebuilds) from estimated waste.
|
|
15
|
+
|
|
16
|
+
All read the same per-session **heartbeat** files that a statusline writes. The
|
|
17
|
+
statusline is the only piece that runs inside Claude Code; everything else is an
|
|
18
|
+
ordinary command you run in any terminal (or via `!ctx …` / `/ctx` inside Claude).
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
Claude Code session
|
|
22
|
+
│ (each statusline refresh)
|
|
23
|
+
▼
|
|
24
|
+
heartbeat statusline ──writes──► ~/.claude/session-status/<session>.json
|
|
25
|
+
~/.claude/session-status/steps/<session>.jsonl
|
|
26
|
+
▲ │
|
|
27
|
+
│ one stdout line ├──► ctx dashboard (live, all sessions)
|
|
28
|
+
in-app status bar └──► ctx audit (offline, one session)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Requires `python3` (3.9+). The heartbeat statusline also needs `jq` + `awk`
|
|
32
|
+
(standard on macOS/Linux).
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Recommended: isolated global install with pipx
|
|
38
|
+
pipx install ctxprof # from PyPI
|
|
39
|
+
# or from a git checkout (replace <your-repo-url>):
|
|
40
|
+
pipx install "git+<your-repo-url>#subdirectory=claude-context-tools"
|
|
41
|
+
|
|
42
|
+
# Or plain pip (ideally in a venv)
|
|
43
|
+
pip install ctxprof
|
|
44
|
+
|
|
45
|
+
# Or straight from a clone, editable (path is the directory in the repo)
|
|
46
|
+
pip install -e claude-context-tools
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
This installs one command, `ctx`, an umbrella with subcommands:
|
|
50
|
+
`dashboard`, `watch`, `explain`, `show`, `steps`, `digest`, `rates`, `audit`,
|
|
51
|
+
`tui`, `install` (plus `statusline`/`hook` plumbing). Run `ctx` with no
|
|
52
|
+
arguments to see them all.
|
|
53
|
+
|
|
54
|
+
No clone needed after install. To run without installing, use
|
|
55
|
+
`python3 -m claude_context_tools.cli <subcommand>` from this directory.
|
|
56
|
+
|
|
57
|
+
## When to run what
|
|
58
|
+
|
|
59
|
+
| I want to… | Run |
|
|
60
|
+
|---|---|
|
|
61
|
+
| See what all my open sessions cost **right now** | `ctx dashboard` |
|
|
62
|
+
| Find **which session is the money sink** | `ctx digest` (ranks sessions by cost) |
|
|
63
|
+
| Know **why a session got expensive + how to fix it** | `ctx explain <id>` |
|
|
64
|
+
| **Watch one session live** while I work (split pane) | `ctx watch <id>` |
|
|
65
|
+
| See **per-turn** costs / catch a spike as it happens | `ctx steps` |
|
|
66
|
+
| **Deep offline** breakdown of one session's transcript | `ctx audit <id>` |
|
|
67
|
+
| Check **prices / keep-warm-vs-rebuild** math | `ctx rates` |
|
|
68
|
+
|
|
69
|
+
Typical flow: **`ctx digest`** to see which session is costing the most →
|
|
70
|
+
**`ctx explain <id>`** on that one to see the avoidable costs and the fix.
|
|
71
|
+
|
|
72
|
+
**Which session do the single-session commands act on?** With no id they pick the
|
|
73
|
+
**newest** (most recently active) session, and the **header line shows its name**
|
|
74
|
+
so you can confirm. To target another, pass a **session id or prefix** — get the
|
|
75
|
+
ids from `ctx dashboard` (the `ID` column) and run `ctx show <id>` to see a
|
|
76
|
+
session's repo/cwd. No time window: `ctx explain`/`audit` cover the **whole
|
|
77
|
+
session**; `ctx digest --since 2h` and `ctx steps --limit N` are the windowed ones.
|
|
78
|
+
|
|
79
|
+
## Use it
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Live table of all active sessions (Ctrl-C to quit):
|
|
83
|
+
ctx dashboard
|
|
84
|
+
|
|
85
|
+
# Print once and exit (good for tmux panes / scripts):
|
|
86
|
+
ctx dashboard --refresh 0
|
|
87
|
+
|
|
88
|
+
# Include sessions idle past the stale timeout (shown dimmed, with `!`):
|
|
89
|
+
ctx dashboard --include-stale
|
|
90
|
+
|
|
91
|
+
# Details + a ready-to-run audit command for one session (or the newest):
|
|
92
|
+
ctx show
|
|
93
|
+
ctx show <session-id-or-prefix>
|
|
94
|
+
|
|
95
|
+
# Live single-session panel + recommendations (the "sidebar"; run in a split pane):
|
|
96
|
+
ctx watch
|
|
97
|
+
ctx watch <session-id>
|
|
98
|
+
|
|
99
|
+
# Rank a session's biggest avoidable costs (the "profiler" view):
|
|
100
|
+
ctx explain
|
|
101
|
+
ctx explain <session-id> --top 5
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`ctx explain` looks like:
|
|
105
|
+
|
|
106
|
+
```text
|
|
107
|
+
ctx explain Make Claude workflow utility installable … — top avoidable costs
|
|
108
|
+
|
|
109
|
+
1. $ 51.29 Cache rebuilds after idle >5min (×15)
|
|
110
|
+
→ rebuilt ~7.2M cache tok — /clear before breaks; keep sessions short
|
|
111
|
+
2. $ 0.02 ~est Large Read result (~35.2k tok)
|
|
112
|
+
→ summarize/cap big output before it enters context
|
|
113
|
+
3. $ 0.01 ~est Re-reading dashboard.py 27×
|
|
114
|
+
→ ~12.5k redundant tok — read targeted ranges / keep a summary
|
|
115
|
+
|
|
116
|
+
Total avoidable: ~$51.32 ($51.29 measured + $0.03 estimated)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
# Recent per-turn cost feed across all sessions (catches expensive turns):
|
|
121
|
+
ctx steps
|
|
122
|
+
ctx steps --limit 40
|
|
123
|
+
ctx steps --json --limit 200 # machine-readable (feed to Claude to analyze)
|
|
124
|
+
|
|
125
|
+
# Rollup summary: totals, top-cost turns, biggest cache writes, longest gaps:
|
|
126
|
+
ctx digest --since 2h
|
|
127
|
+
|
|
128
|
+
# Price table + keep-warm-vs-rebuild economics for the current context:
|
|
129
|
+
ctx rates
|
|
130
|
+
ctx rates --context 100000 --model "Opus 4.8"
|
|
131
|
+
# (edit prices without touching code via ~/.claude/ctx-pricing.json)
|
|
132
|
+
|
|
133
|
+
# Interactive browser (scroll/filter/drill into a turn):
|
|
134
|
+
ctx tui
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Inside Claude Code
|
|
138
|
+
|
|
139
|
+
Prefix any command with `!` in the Claude prompt to run it and drop the output
|
|
140
|
+
into the conversation, or use the bundled `/ctx` slash command:
|
|
141
|
+
|
|
142
|
+
```text
|
|
143
|
+
!ctx digest --since 2h
|
|
144
|
+
/ctx steps
|
|
145
|
+
/ctx steps --json --limit 200 # then ask Claude to summarize the big turns
|
|
146
|
+
|
|
147
|
+
# Offline "why did it burn?" audit:
|
|
148
|
+
ctx audit --latest
|
|
149
|
+
ctx audit --session <session-id>
|
|
150
|
+
ctx audit --transcript ~/.claude/projects/<proj>/<session>.jsonl
|
|
151
|
+
ctx audit --latest --json # machine-readable
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Turn on the heartbeat (one-time)
|
|
155
|
+
|
|
156
|
+
The dashboard and audit need a statusline that writes heartbeat files. Claude
|
|
157
|
+
Code supports **exactly one** `statusLine`, so this never silently replaces an
|
|
158
|
+
existing one:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
ctx install --statusline # safe: refuses if a statusLine exists
|
|
162
|
+
ctx install --statusline --force # replace it (writes a timestamped backup)
|
|
163
|
+
ctx install --print-path # just print the heartbeat script path
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
If you already have a statusline you like, keep it — just make it also write the
|
|
167
|
+
`~/.claude/session-status/<session>.json` and `steps/<session>.jsonl` shapes (see
|
|
168
|
+
`claude_context_tools/data/claude-statusline-heartbeat.sh` for the exact fields)
|
|
169
|
+
and these tools will read it. To wire it by hand instead:
|
|
170
|
+
|
|
171
|
+
```json
|
|
172
|
+
{
|
|
173
|
+
"statusLine": {
|
|
174
|
+
"type": "command",
|
|
175
|
+
"command": "bash /ABSOLUTE/PATH/TO/claude-statusline-heartbeat.sh"
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Global vs per-repo
|
|
181
|
+
|
|
182
|
+
- **Global** (all sessions): set the heartbeat in `~/.claude/settings.json`.
|
|
183
|
+
This is what `ctx install --statusline` does. Usual choice.
|
|
184
|
+
- **Per-repo**: set `statusLine` in a project's `.claude/settings.json`; it wins
|
|
185
|
+
for sessions in that repo. Use an absolute path to the heartbeat script.
|
|
186
|
+
|
|
187
|
+
## What the audit reports
|
|
188
|
+
|
|
189
|
+
- **What burned context** — per-category token estimates (assistant/user text,
|
|
190
|
+
thinking, tool inputs, per-tool results, attachments).
|
|
191
|
+
- **Attachment breakdown** — system-injected context (skill listings, hook
|
|
192
|
+
output, deferred tool lists) totalled separately.
|
|
193
|
+
- **Repeated file reads / commands / duplicate output blobs** — content paid for
|
|
194
|
+
more than once.
|
|
195
|
+
- **Large pasted logs / large tool results** and **subagent report weight**.
|
|
196
|
+
- **Cache telemetry** — per-turn behavior, separating expected initial
|
|
197
|
+
**warm-up** from likely mid-session **invalidation** (cache was warm, then
|
|
198
|
+
largely rewritten).
|
|
199
|
+
- **Recommendations** derived from the findings.
|
|
200
|
+
|
|
201
|
+
### Honest limits
|
|
202
|
+
|
|
203
|
+
- Token counts in `ctx audit` are **estimates** (`~4 chars/token`, scaled by the
|
|
204
|
+
model's tokenizer — ×1.35 for Opus 4.7+; override with `--token-factor`). The
|
|
205
|
+
live per-turn numbers (statusline, `ctx steps`, COST) are Claude Code's own
|
|
206
|
+
exact usage fields, not estimates.
|
|
207
|
+
- Cache analysis is **heuristic and token-level** — the heartbeat exposes per-turn
|
|
208
|
+
token *counts*, not cache-block boundaries, so it flags *likely* invalidation,
|
|
209
|
+
not exact cache-block attribution.
|
|
210
|
+
- **Unknown is not zero.** With no step file, the audit says per-turn cache
|
|
211
|
+
behavior is unknown rather than reporting `0`.
|
|
212
|
+
|
|
213
|
+
## Dashboard notes
|
|
214
|
+
|
|
215
|
+
- **LABEL** is the session name/task (from `session_name`) so you identify
|
|
216
|
+
sessions by what they're doing, not by UUID. **ID** is a short id prefix
|
|
217
|
+
(enough for `ctx show <prefix>`).
|
|
218
|
+
- **CONTEXT** is a fill bar + percent; **RECENT** is a per-session sparkline of
|
|
219
|
+
the last few turns' token volume, colored by cache-**read** share
|
|
220
|
+
(green = cheap cache hits, cyan = fresh input / rebuild) so big-write rebuild
|
|
221
|
+
turns show as expensive, not green.
|
|
222
|
+
- The table is **responsive**: on narrow terminals lower-priority columns
|
|
223
|
+
(MODE, AGENT, 5H/7D, IN/OUT, API, DUR, CHG) drop out, always keeping REPO,
|
|
224
|
+
the CONTEXT bar, COST, RECENT and LABEL.
|
|
225
|
+
- `ctx steps` is a separate cross-session **per-turn** feed (STEP, CTX, tokens,
|
|
226
|
+
CACHE R/W, COST, APIΔ, GAP) — costs over $0.25/$1.00 a turn are highlighted.
|
|
227
|
+
- **Stale sessions** are hidden by default; the header shows `stale-hidden:N` so
|
|
228
|
+
you know they exist. `--include-stale` shows them dimmed with an `!`. The
|
|
229
|
+
cutoff is `CLAUDE_STATUS_STALE_AFTER_SECONDS` (default 900s). An open session
|
|
230
|
+
that's idle (not generating turns) stops writing heartbeats and goes stale.
|
|
231
|
+
- **COST and TIME are cumulative session totals**; **live-ctx / IN / OUT** are
|
|
232
|
+
the current context, not lifetime. An old session legitimately shows a large
|
|
233
|
+
frozen cost/time — its lifetime total at the last heartbeat, not ongoing spend.
|
|
234
|
+
|
|
235
|
+
## Files written
|
|
236
|
+
|
|
237
|
+
| Path | Written by | Used by |
|
|
238
|
+
|---|---|---|
|
|
239
|
+
| `~/.claude/session-status/<session>.json` | heartbeat statusline | dashboard, audit |
|
|
240
|
+
| `~/.claude/session-status/steps/<session>.jsonl` | heartbeat statusline | audit (cache telemetry) |
|
|
241
|
+
| Claude transcript JSONL (under `~/.claude/projects/`) | Claude Code | audit |
|
|
242
|
+
|
|
243
|
+
Override the state dir with `CLAUDE_STATUS_STATE_DIR`. Nothing is deleted
|
|
244
|
+
automatically. These tools only ever **read** your transcripts — they never
|
|
245
|
+
modify or upload them.
|
|
246
|
+
|
|
247
|
+
## Tests
|
|
248
|
+
|
|
249
|
+
Fixture-based smoke tests, stdlib only (synthetic data, generated by
|
|
250
|
+
`tests/make_fixtures.py` — no private content):
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
python3 tests/test_claude_context_tools.py
|
|
254
|
+
python3 tests/make_fixtures.py # regenerate fixtures after changing shapes
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
See [`../03-harnesses.md`](../03-harnesses.md) → "Status line" for how this fits
|
|
258
|
+
the broader harness guidance.
|