Skip to main content

Rotor — Install & Scan Guide

Rotor is the Meterhouse metering agent: the part that sits on each machine and turns as work happens. It scans Claude Code's local transcript files, stores usage in a local SQLite database, and (optionally) syncs it to the central server. Scanning works fully offline — no server required.

Installed as meterhouse-rotor; the command it provides is meterhouse.

Tracked activity ≠ official quota. All numbers are token counts parsed from local transcripts — an estimate, not your Claude Max/Pro billing or quota.


1. Prerequisites

  • Python 3.10+ (check with python --version)
  • Claude Code installed and used at least once on this machine, so transcripts exist under ~/.claude/projects/ (Windows: %USERPROFILE%\.claude\projects\).

The agent uses only the Python standard library — there is nothing to pip install for scanning.

For central mode (sending usage to the dashboard) you install the meterhouse command from the repo, which needs git on PATH — see §7.


2. Install

Clone the repository and move into the agent folder:

git clone <your-repo-url> meterhouse
cd meterhouse\agent

You can run it three ways:

A. Install from PyPI (public release) — once published, users can install:

pip install meterhouse-rotor

B. No install (simplest) — run it as a module from the agent/ folder:

python -m meterhouse --help

C. Install the meterhouse command locally (so you can run it from anywhere):

pip install -e .
meterhouse --help

Both install options are equivalent for command usage; the rest of this guide uses python -m meterhouse.

The package is now public-ready as meterhouse-rotor. If the package has already been published on PyPI, use the pip install meterhouse-rotor command above.


3. Use the agent as a Python SDK

The package now exposes a simple SDK interface via meterhouse.Agent.

Install the package

pip install -e .

Example usage

from meterhouse import Agent

agent = Agent(display_name="PC-01")
agent.register(
    server_url="http://127.0.0.1:8000",
    api_key="cfk_...",
    display_name="PC-01",
)
print(agent.scan())
print(agent.sync())
print(agent.health())

The SDK also supports running the daemon programmatically:

agent = Agent(display_name="PC-01")
agent.daemon()

4. Run a scan

Give this machine a name (once), then scan:

python -m meterhouse identity --display-name PC-01
python -m meterhouse scan

Example output:

  [NEW] C:\Users\you\.claude\projects\my-proj\<uuid>.jsonl  (+266 events)
  ...
scan complete: new=18 updated=0 skipped=0 events+=1787

The scan is incremental and idempotent:

  • Unchanged files are skipped; changed files are read only from where they left off.
  • Running it again processes nothing new (events+=0) — it never double-counts.

Run scan whenever you want fresh data (or schedule it — see §6).


4. View your usage

python -m meterhouse today     # today's tokens by model
python -m meterhouse week      # last 7 days
python -m meterhouse stats     # all-time totals, by model, top projects

stats example:

  Meterhouse - all-time tracked usage
  Sessions:       13
  Input tokens:   6.2M   Output tokens: 1.1M   Cache read: 280M ...
  By model:  claude-opus-4-8  ...  claude-sonnet-5 ...
  Top projects:  Github/hotel-demo  ...
  (estimate only - not official Max/Pro quota)

5. Where your data lives

What Default location Override with
Usage database ~/.claude/meterhouse/usage.db METERHOUSE_DB
Machine identity/config ~/.claude/meterhouse/agent.json METERHOUSE_CONFIG
Transcripts it reads (read-only) ~/.claude/projects/**/*.jsonl auto-discovered

Example with a custom DB path (PowerShell):

$env:METERHOUSE_DB = "D:\data\usage.db"
python -m meterhouse scan

The agent never modifies Claude Code's files and never stores prompts, responses, or source code — only token counts and metadata.


6. Running in the background (event-driven)

The agent runs only while you have a Claude Code session open. There is no service, no logon task, and no process at all on an idle machine. Claude Code's own session hooks start it and stop it:

python -m meterhouse install-hooks

That writes three entries into ~/.claude/settings.json (your existing hooks are preserved, and uninstall-hooks removes exactly these three):

Hook What it does
SessionStart Records the session and starts the agent if it isn't already running.
SessionEnd Removes the session. The last one out lets the agent shut down.
UserPromptSubmit Refreshes liveness, and restarts the agent if it had crashed.

All three are registered async, so they never make you wait, and they never print anything — a hook that wrote to stdout would inject text into Claude's context on every session.

The "Connect PC" flow (§7) does this for you and adds one Meterhouse Daily Catch-up task: a single meterhouse once per day, purely as a floor on data loss if hooks ever fail to install. Scanning is incremental and idempotent, so even a machine a day behind reports its full history.

The lifecycle

open Claude Code  ->  SessionStart hook  ->  agent starts
                      scans every 60s, and instantly whenever a transcript grows
close the last    ->  SessionEnd hook    ->  grace period
session               final scan + sync  ->  reports "stopped"  ->  process exits

Within a session the scan interval is a floor, not a fixed cadence: the agent watches ~/.claude/projects every few seconds and scans the moment a transcript is written, so work shows up on the dashboard in seconds. The final scan on shutdown is what guarantees the last turn of a session — usually the largest — is never left behind.

Claude Code that is killed outright never fires SessionEnd. The agent covers that with an idle timeout (session_idle_timeout_seconds, default 300) measured against transcript writes as well as hook events, so a long agent turn with no prompt still counts as active.

Several Claude Code windows share one agent, and each OS user on a shared machine gets their own — everything lives under ~/.claude/meterhouse/. Launching the agent while one is already running is safe: it takes a single-instance lock (~/.claude/meterhouse/daemon.lock) and a duplicate exits immediately, which is what lets every hook simply try.

Checking on it

python -m meterhouse sessions   # live sessions, hook state, is the agent running
python -m meterhouse health     # last scan, active sessions, clean-stop time

The agent reports each step to the dashboard as it happens (scanningscannedsyncingidlestopped). A machine showing Idle on the Systems page has stopped on purpose; that is the normal resting state, not a fault. Its log is at ~/.claude/meterhouse/agent.log (rotating) — the agent runs windowless, so this is where its output goes.

Turning it off

python -m meterhouse uninstall-hooks
schtasks /Delete /TN "Meterhouse Daily Catch-up" /F

Machines that cannot use hooks

Where ~/.claude/settings.json is locked down by policy, run the old always-running behaviour explicitly:

python -m meterhouse daemon --always-on

Equivalent config: always_on: true in runtime.json, or METERHOUSE_ALWAYS_ON=true. This never exits on its own, so it needs a logon scheduled task the way earlier versions did.


7. Send data to the central server (optional)

Local scanning is enough for one machine. To feed a central dashboard:

  1. Sign in to the web app and open Connect PC (or ask your admin for an API key).
  2. The web app generates a one-line setup command for the machine you want to track. This is the command you run in PowerShell on that PC.
  3. The command installs the agent, registers the machine with the server, scans local Claude Code transcripts, and syncs the results back to the dashboard.

If you only have the website link, that is enough. The site does not scan your PC from the browser; it only generates the install/connect command and gives you the server URL and API key to use.

pip install meterhouse-rotor
meterhouse register --server https://YOUR-API-URL --api-key cfk_... --display-name PC-01
meterhouse scan
meterhouse sync

Or use the Windows installer from the repo (deploy/install.ps1), which does all four steps above and sets up the recurring scan+sync task automatically.

--server must be the API server (e.g. http://localhost:8000 in dev), not the dashboard's frontend URL (http://localhost:5173/...). Pointing it at the frontend URL will fail to register.

The agent connects by calling the server at the given API URL and authenticating with the supplied API key. After registration, it reads local transcript files, aggregates token events, and sends only usage metadata to the dashboard.

An administrator can also create an API key under Admin → Agent API keys, then share the key and API URL with each user.

sync only sends events the server hasn't seen; if the server is down it simply retries next time (nothing is lost, nothing is double-counted). The dashboard shows a system as "Never synced" until the first successful sync call — registering alone, or running scan without sync, is not enough.


8. Troubleshooting

Symptom Fix
scan complete: new=0 ... events+=0 on first run No transcripts found. Confirm %USERPROFILE%\.claude\projects\ exists and you've used Claude Code.
python not found Install Python 3.10+ and ensure it's on PATH (py -3 also works on Windows).
Want a clean re-scan Delete the usage DB (%USERPROFILE%\.claude\meterhouse\usage.db) and run scan again.
sync says "Central mode not configured" Run register first with --server and --api-key.
sync fails / offline Expected when the server is unreachable; it retries on the next run.

Command reference

python -m meterhouse scan       [--display-name NAME] [--quiet]
python -m meterhouse today | week | stats
python -m meterhouse identity   [--display-name NAME] [--set-display-name NAME]
python -m meterhouse register   --server URL --api-key KEY [--display-name NAME]
python -m meterhouse sync       [--quiet]
python -m meterhouse once       [--quiet]   # scan + sync + run queued commands
python -m meterhouse install-hooks          # start/stop with your Claude Code sessions
python -m meterhouse uninstall-hooks
python -m meterhouse sessions               # live sessions + hook/agent state
python -m meterhouse daemon     [--display-name NAME] [--always-on]
python -m meterhouse health
python -m meterhouse heartbeat
python -m meterhouse account   [show | enable | disable]
python -m meterhouse --version

Claude account reporting (optional, off by default)

account controls whether this machine also reports which Claude subscription it is signed into, so an admin can see who is on which plan and how much of its rate limit is used.

python -m meterhouse account show      # print the exact payload - sends nothing
python -m meterhouse account enable
python -m meterhouse account disable

Enabled, the agent reads a fixed allowlist of fields from ~/.claude.json: account UUID, email, display name, organisation, plan tier, and the cached rate-limit percentages. OAuth tokens and credentials are never read, and .credentials.json is never opened. See meterhouse/account.py for the allowlist and tests/test_account.py for the tests that enforce it.

Equivalent env var: METERHOUSE_ACCOUNT_REPORTING=true.

Run the test suite with pip install pytest && python -m pytest.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

meterhouse_rotor-0.3.0.tar.gz (86.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

meterhouse_rotor-0.3.0-py3-none-any.whl (67.9 kB view details)

Uploaded Python 3

File details

Details for the file meterhouse_rotor-0.3.0.tar.gz.

File metadata

  • Download URL: meterhouse_rotor-0.3.0.tar.gz
  • Upload date:
  • Size: 86.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for meterhouse_rotor-0.3.0.tar.gz
Algorithm Hash digest
SHA256 cd685403da235f8f5e591c87e783ec6b6e0c8b4c176308117eddb9223b653469
MD5 0fd1d4cb6f980cb8cfa48105704fe055
BLAKE2b-256 70b0c4d239e7dacc45742011044423b30dfe63e2e8bc456a8bacf83780a21c17

See more details on using hashes here.

Provenance

The following attestation bundles were made for meterhouse_rotor-0.3.0.tar.gz:

Publisher: publish-agent.yml on Aniruth-Sakthivel/claude-code-uusage

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file meterhouse_rotor-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for meterhouse_rotor-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 73e28f6891942d624991be69f7008f14bc328642e5dfe39ecab0e9e3f7e08f93
MD5 9bc16dd2e412ba56a6d74737a11a1d93
BLAKE2b-256 827fbb6810bf2c313118cc7f0969d005d58bab80ec9475834a1e826f838cac44

See more details on using hashes here.

Provenance

The following attestation bundles were made for meterhouse_rotor-0.3.0-py3-none-any.whl:

Publisher: publish-agent.yml on Aniruth-Sakthivel/claude-code-uusage

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page