Claude Code usage-per-project status line and tracker
Project description
claude-code-usage-per-project (ccupp)
A Claude Code status line and usage tracker that shows per-project efficiency metrics — prompts, tokens, cost, and API time — across all sessions for the current working directory.
What it does
Three modes across three scripts:
1. Status line (piped from Claude Code)
When Claude Code invokes it as a statusLine command, reads JSON from stdin and prints a 2-line HUD:
Opus 4.7 [xhigh] ctx ███░░░░░░░ 38%
myproject [12 msg] [84.2k tok] [$1.83] [6m12s]
- Line 1: Model name (cyan) — effort level as a dim-bracket tag — context-window bar (green/yellow/red at 70%/90%)
- Line 2: Project name anchor — cumulative user messages, unique tokens, cost, total API response time, each in a dim-bracket tag
2. Usage report (run directly in terminal)
$ ccupp
Prints a boxed per-session table for the project in the current directory:
myproject — Claude Code usage (per project)
╭──────────────┬─────────────────┬──────────┬────────┬───────┬─────────╮
│ SESSION │ DATE │ USER_MSG │ TOKENS │ COST │ TIME │
├──────────────┼─────────────────┼──────────┼────────┼───────┼─────────┤
│ 9beb422a │ 2026-05-30 12:00│ 12 │ 84.2k │ $1.83 │ 6m12s │
│ 0ec212f4~ │ 2026-05-29 09:15│ 8 │ 51.0k │ $0.94 │ 4m30s │
├──────────────┼─────────────────┼──────────┼────────┼───────┼─────────┤
│ TOTAL │ │ 20 │ 135.2k │ $2.77 │ 10m42s │
╰──────────────┴─────────────────┴──────────┴────────┴───────┴─────────╯
~ = backfill estimate (cost·time approximate)
(In the real terminal, the border characters render dim so the data stands out.)
Sessions before installation are lazily backfilled on first run (~ marker).
3. Prompt export (ccupp --export)
$ ccupp --export
✅ ./PROMPTS.md
3 sessions · 47 prompts
Exports every human prompt you typed (filtering out tool results, system messages, UI slash commands like /usage or /model, interrupt markers) to PROMPTS.md as a chronological Markdown document — useful as a portfolio artifact.
Installation
Requirements: Python 3.9+ (stdlib only, no dependencies)
Option A — pip (recommended)
pip install git+https://github.com/jwchoi-kr/claude-code-usage-per-project.git
This installs the ccupp command to your PATH. Use which ccupp to find the installed path for the next step.
Wire up the status line in ~/.claude/settings.json:
{
"statusLine": {
"type": "command",
"command": "/path/to/ccupp",
"padding": 0
}
}
Use the full path returned by which ccupp (e.g. /Users/you/.pyenv/shims/ccupp) to avoid PATH issues when Claude Code invokes the hook.
Option B — clone manually
git clone https://github.com/jwchoi-kr/claude-code-usage-per-project.git ~/tools/ccupp
Wire up the status line in ~/.claude/settings.json:
{
"statusLine": {
"type": "command",
"command": "/path/to/python3 /path/to/ccupp/ccupp.py",
"padding": 0
}
}
Add a shell alias for the report and export commands:
alias ccupp='python3 ~/tools/ccupp/ccupp.py'
How it works
Snapshot files
The status line fires after every assistant response (potentially dozens of times per session). Rather than accumulating into a single counter, each session writes its own snapshot file:
~/.claude/projects/<project-dir>/.ccupp/sessions/<session_id>.json
{"tokens": 84200, "utterances": 12, "cost_usd": 1.83, "api_ms": 372000, "estimated": false}
- Current session: rewritten each render with exact values from stdin (
cost.total_cost_usd,cost.total_api_duration_ms) - Past sessions: loaded from their snapshot; backfilled on first encounter if missing
- Project total: sum of all snapshot files
This design is idempotent (re-running never double-counts) and safe for concurrent sessions.
Token counting
Tokens are summed as input + cache_creation + cache_read + output — all four types — matching ccusage. Cache reads inflate the displayed number, but they're real tokens you pay for, so the figure stays comparable across tools.
Deduplication uses (message.id, requestId) as the primary key with a sidechain fallback on message.id alone (subagent copies of the same message collapse to one entry). On key collisions, non-sidechain wins over sidechain, then larger token total wins, then the entry with speed info wins — same tie-breaking as ccusage.
Subagent (sidechain) tokens are included; they represent real work done by the model on your behalf.
Human prompt filter
An utterance counts only if it is:
type: "user"- Not
isMetaorisSidechain - Not a tool result block
- Not a UI slash command (
/usage,/model,/clear, etc.) or local-command wrapper - Not an interrupt marker
Deduplicated by promptId (Claude Code can emit the same prompt ID multiple times for streaming).
Backfill estimation
Sessions from before installation have no exact cost/time data. Time is estimated from message timestamps. Cost uses ccusage-style Auto mode:
- If a transcript entry has a
costUSDfield, that value wins. - Otherwise cost = tokens × per-model rate from the LiteLLM pricing table, with the 200K-tier surcharges and the Fast-mode multiplier applied.
The LiteLLM JSON is fetched on first use and cached at ~/.ccupp/litellm-pricing.json for 24h. If the network is unreachable on first run, ccupp falls back to a built-in per-family table (opus/sonnet/haiku). The live session always uses the exact cost.total_cost_usd Claude Code provides via stdin — pricing-table inaccuracies only affect backfilled sessions.
Backfilled sessions are marked with ~ and "estimated": true.
Running tests
python3 -m unittest test_ccupp test_ccupp_core test_ccupp_report test_ccupp_export -v
All tests use stdlib unittest, tempfile, and shutil — no external dependencies and no network (pricing is injected via core._PRICING_CACHE in _PricingIsolation).
Files
| File | Purpose |
|---|---|
ccupp.py |
Status line rendering and main() dispatcher |
ccupp_core.py |
Shared utilities, data analysis, and persistence |
ccupp_report.py |
Terminal usage report (ccupp command) |
ccupp_export.py |
Prompt extraction and Markdown rendering (ccupp --export) |
test_ccupp.py |
Tests for ccupp.py |
test_ccupp_core.py |
Tests for ccupp_core.py |
test_ccupp_report.py |
Tests for ccupp_report.py |
test_ccupp_export.py |
Tests for ccupp_export.py |
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ccupp-0.1.0.tar.gz.
File metadata
- Download URL: ccupp-0.1.0.tar.gz
- Upload date:
- Size: 14.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.25
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d48ab832e64d8cc6a7a8d12b28dae8b5579f5660bf21ad80a84bdfe45b7278b0
|
|
| MD5 |
03b5c92412857f02ae74fcba27f1c99c
|
|
| BLAKE2b-256 |
64d8d6904c5a2507316c1801293324a8f3a95749bdc775dc39a6fa875b741aff
|
File details
Details for the file ccupp-0.1.0-py3-none-any.whl.
File metadata
- Download URL: ccupp-0.1.0-py3-none-any.whl
- Upload date:
- Size: 16.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.25
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b3bbd3f74ae052e5ad2eb3f1aaee775f9ab4fdf548a7ee326e430715f0fc6ea6
|
|
| MD5 |
12ea0f22d7623cb5b569c1f304986f94
|
|
| BLAKE2b-256 |
931cd7d109172577a0b6c59cc4b4c48a29e2630da4a624790a4a2ff836657b04
|