Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 58 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,13 @@ troubleshooting, see the [User guide](specs/USER_GUIDE.md).
| **Review history and spend** | Find expensive or slow work across sessions, projects, runtimes, platforms, and calendar ranges. |
| **Compare models and execution** | Compare input, output, pace, wait, and workload shape without presenting weak matches as meaningful results. |
| **Investigate tools and skills** | Find high-output, failing, repeated, unobserved, or deferred capabilities while keeping incomplete evidence explicit. |
| **Improve with Tok** | Ask Tok, Token Meter's token coach, questions from any page, turn a natural-language intention into one measurable goal, and opt into evidence-bounded weekly reviews through your signed-in Codex CLI. |
| **Manage usage** | Check provider-reported limits, allocate a monthly budget, receive threshold notifications, and let Codex or Claude query bounded evidence through the local MCP. |

## Coverage

**Runtimes:** Claude Code and Desktop Agent/Cowork, Codex CLI and desktop,
Cursor Agent/Composer, OpenCode, Kiro, and Pi.
Cursor Agent/Composer, OpenCode, Kiro, and Pi coding-agent sessions.

| Platform | Status | Experience |
| --- | --- | --- |
Expand All @@ -79,13 +80,20 @@ Token Meter works when the agent keeps session evidence on your machine in a
supported local store. Sessions that exist only in a cloud-hosted service may
not be available to Token Meter.

For Pi coding-agent sessions, recorded token/cost and structural tool evidence
remain local and content-free. Wait time
is inferred between user and assistant events. Pi leaves semantic token classification
and context-window size unavailable when its records do not provide them.

## First Five Minutes

1. Open **Sessions → Current sessions** and select an active run.
2. Under **Run**, check cost, context pressure, Output/$, and Reasoning ratio.
Add a session budget if the run needs an attention limit.
3. After more sessions accumulate, use **Spend**, **Models**, **Tools**,
**Efficiency**, and **Git** to review longer-term patterns.
4. Open **Tok** from the header to ask about the current page or draft one
measurable improvement goal.

## Product Tour

Expand Down Expand Up @@ -139,6 +147,43 @@ evidence stays labelled beside the numbers.
<img src="images/efficiency.png" alt="Token Meter Efficiency page with output per dollar, reasoning ratio, context load, and output per execution" width="900">
</p>

### Set a measurable goal with Tok

Tok is one shared right-side conversation across the dashboard, not a set of
tabs. It can use the current page name and bounded, content-free Token Meter
evidence; messages stay in browser memory and disappear on refresh. A reply
starts with one content-free live line carrying a spinning indicator, the stage,
and a visual-only timer. The stage reads **Starting Tok**, then **Opening
Codex**, the Token Meter reading actually in progress such as **Reading your
usage history**, or **Checking the evidence**—only when those observed
boundaries occur. Once a Token Meter reading completes the line counts it, and a
run past roughly twenty-five seconds adds that Tok is still working—an
elapsed-time statement that claims no cause for the delay. After eight
seconds the send control becomes **Stop**, which ends only that active Tok turn;
the sent message remains and the panel offers **Retry**. Provider response time
is variable, and Tok never exposes model reasoning, raw events, or fabricated
progress.

Failures are visible, not silent. A request that cannot be delivered becomes a
clearly marked error entry with **Retry** rather than words in Tok's voice, and a
missing local prerequisite—such as a Codex CLI that is not installed—appears as a
notice naming what to do, with a quiet attention dot on the closed **Tok**
control.

Describe an improvement in ordinary language, review the structured metric,
target, evidence window, runtime, and weekday that Codex drafts, then explicitly
activate it. One active goal appears as a compact rail below the composer; open
it for progress, weekly controls, and the saved review. Token Meter stores that
contract and numeric snapshots—not your wording or Codex's prose. Answers show
one collapsed source line with at most three content-free measurements, rather
than an expanded evidence table.

Weekly reviews are opt-in. When enabled, the signed-in local Codex CLI compares
the saved baseline with current covered evidence once on the selected weekday.
You can pause weekly reviews, run one manually, or clear the goal from the same
panel. Progress stays unavailable while a baseline is being collected or when
required evidence is missing.

### Git

**Git** pairs successful local pushes with covered spend, so you can see code
Expand All @@ -160,7 +205,7 @@ and automatic installation are separate settings; both are on by default.
<img src="images/mcp.png" alt="Token Meter Settings view for local read-only agent connections" width="900">
</p>

The local MCP exposes seven read-only tools:
The local MCP exposes eight read-only tools:

| Tool | Use |
| --- | --- |
Expand All @@ -170,6 +215,7 @@ The local MCP exposes seven read-only tools:
| `sessions` | Select content-free session IDs using runtime, client, model, state, or time filters. |
| `trace` | Read a standardized trace or sanitized runtime-native structure for one session. |
| `stats` | Aggregate selected token, cost, timing, context, attempt, model-call, or tool metrics. |
| `goal` | Read the active structured Tok goal, progress, or latest numeric weekly review. |
| `schema` | Discover fields, dimensions, units, limits, and availability semantics. |

A comparison harness can call `sessions`, pass one returned ID to `trace`, and
Expand Down Expand Up @@ -214,6 +260,16 @@ provider under its own terms. See the [User guide](specs/USER_GUIDE.md) for the
full evidence semantics and [Security policy](specs/SECURITY.md) for the
canonical boundary.

Tok uses the user's existing Codex CLI sign-in. Each turn runs in an
ephemeral, read-only workspace with general shell, file, browser, app, plugin,
memory, and sub-agent tools disabled; only allowlisted read-only Token Meter MCP
tools are exposed. Coach-launched MCP calls can reuse the running local server's
bounded warm evidence path and fall back once to the existing local read when
that optimization is unavailable; the result contract and allowlists do not
expand. The message you send and the bounded MCP results selected for the answer
may be processed by OpenAI under that account's terms. Tok never receives raw
trace content, and Token Meter never stores the conversation or model reasoning.

## Documentation

| Document | Use it for |
Expand Down
402 changes: 402 additions & 0 deletions page.html

Large diffs are not rendered by default.

10 changes: 8 additions & 2 deletions performance.html
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
}
function formatStat(stat){
if(!stat||stat.available!==true||!Number.isFinite(Number(stat.current)))return '—';
if(stat.id==='lines_pushed')return Math.round(Number(stat.current)).toLocaleString('en-US');
if(stat.id==='lines_pushed'||stat.id==='commits_pushed')return Math.round(Number(stat.current)).toLocaleString('en-US');
const unit=String(stat.unit||'').toLowerCase();
if(unit==='seconds')return durationLabel(stat.current);
return compactNumber(stat.current);
Expand Down Expand Up @@ -209,6 +209,7 @@
const name=sanitizeBuilderName(options.name),range=[7,30,90].includes(Number(payload&&payload.range_days))?Number(payload.range_days):30;
const lineStat=safeArray(payload&&payload.spotlights).find(stat=>stat&&stat.id==='lines_pushed');
const lead=lineStat||{id:'lines_pushed',label:'Lines pushed',family:'delivery',available:false,current:null,previous:null,delta_pct:null,unit:'lines'};
const commitStat=safeArray(payload&&payload.spotlights).find(stat=>stat&&stat.id==='commits_pushed')||{id:'commits_pushed',label:'Commits pushed',family:'delivery',available:false,current:null,previous:null,delta_pct:null,unit:'commits'};
const logo=options.logo,hasLogo=Boolean(logo&&logo.complete&&Number(logo.naturalWidth)>0);if(hasLogo)ctx.drawImage(logo,72,44,148,59);
const brandX=hasLogo?258:72;
if(hasLogo){ctx.fillStyle=gradientFill(ctx,0,46,0,102,[[0,'rgba(127,219,242,.08)'],[.5,'rgba(127,219,242,.5)'],[1,'rgba(127,219,242,.08)']]);ctx.fillRect(236,46,2,56);}
Expand All @@ -229,6 +230,11 @@
const heroSize=fitText(ctx,heroValue,790,176,100);ctx.fillStyle=gradientFill(ctx,58,340,58,486,[[0,'#ffffff'],[.5,'#dcf2fa'],[1,'#5cc4e6']]);ctx.font=`900 ${heroSize}px ${condensed}`;ctx.fillText(heroValue,58,486);
const currentSummary=hasCurrent?`Current lines pushed ${leadValue} lines.`:'Current lines pushed unavailable.';
if(!hasCurrent){ctx.fillStyle='#82949c';ctx.font='900 15px -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif';ctx.fillText('GIT EVIDENCE UNAVAILABLE',72,528);}
const commitValue=formatStat(commitStat),hasCommits=commitStat.available===true&&Number.isFinite(Number(commitStat.current));
ctx.fillStyle=gradientFill(ctx,72,0,1008,0,[[0,'rgba(0,188,235,.62)'],[.45,'rgba(127,219,242,.24)'],[1,'rgba(143,108,230,0)']]);ctx.fillRect(72,540,936,1);
ctx.fillStyle='#9fb4bd';setTracking(ctx,'2.6px');ctx.font='900 15px -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif';ctx.fillText('COMMITS',72,582);setTracking(ctx,'0px');
ctx.fillStyle=gradientFill(ctx,232,552,232,590,[[0,'#ffffff'],[1,'#8ee9f2']]);ctx.font=`900 44px ${condensed}`;ctx.fillText(ellipsize(ctx,commitValue,300),232,582);
const commitSummary=hasCommits?`Commits pushed ${commitValue}.`:'Commits pushed unavailable.';
const activity=safeArray(payload&&payload.activity_days),active=activity.filter(day=>day&&day.recorded_session===true).length;
const groupSize=Math.max(1,Math.ceil(activity.length/30)),rhythm=activity.length>30?Array.from({length:30},(_,index)=>({recorded_session:activity.slice(index*groupSize,index*groupSize+groupSize).some(day=>day&&day.recorded_session===true)})):activity.slice(0,30),gap=7,cellWidth=(936-gap*Math.max(0,rhythm.length-1))/Math.max(1,rhythm.length);
rhythm.forEach((day,index)=>{
Expand Down Expand Up @@ -270,7 +276,7 @@
const sessions=features.find(stat=>stat.id==='sessions'),sessionsSummary=sessions&&sessions.available===true?`AI sessions ${formatStat(sessions)}.`:'AI sessions unavailable.';
const spendSummary=spend&&spend.available===true?`Covered equivalent spend ${formatSpend(spend)}, an estimate.`:'Covered equivalent spend unavailable.';
const rhythmSummary=groupSize>1?`Rhythm chart groups ${groupSize} days per bar.`:'Rhythm chart shows one day per bar.';
const identitySummary=name?`Name ${name}. `:'',summary=`${range}-day Builder Field Sheet. ${identitySummary}${currentSummary} ${active} active days. ${rhythmSummary} ${sessionsSummary} ${spendSummary}`;
const identitySummary=name?`Name ${name}. `:'',summary=`${range}-day Builder Field Sheet. ${identitySummary}${currentSummary} ${commitSummary} ${active} active days. ${rhythmSummary} ${sessionsSummary} ${spendSummary}`;
const usageSummary=options.includeUsage===false?'Usage charts excluded.':!hasUsage?'Usage charts unavailable.':`Agent usage: ${agentNames.join(', ')||'unavailable'}. Models: ${modelNames.join(', ')||'unavailable'}; runtime-scoped model usage.`;
canvas.setAttribute('aria-label',`${summary} ${usageSummary}`);
return {summary};
Expand Down
1 change: 1 addition & 0 deletions runtime-manifest.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ required scripts/update-linux
optional README.md
optional LICENSE
python-tree token_meter
tree token_meter/coach/workspace
tree menubar
tree scripts
tree assets
Loading