claude-top 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.
@@ -0,0 +1,59 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+
8
+ jobs:
9
+ verify:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - name: Set up Python ${{ matrix.python-version }}
18
+ uses: actions/setup-python@v5
19
+ with:
20
+ python-version: ${{ matrix.python-version }}
21
+
22
+ - name: Install build dependencies
23
+ run: |
24
+ python -m pip install --upgrade pip
25
+ pip install build twine
26
+
27
+ - name: Build package
28
+ run: python -m build
29
+
30
+ - name: Check package
31
+ run: twine check dist/*
32
+
33
+ publish:
34
+ needs: verify
35
+ runs-on: ubuntu-latest
36
+ environment:
37
+ name: pypi
38
+ url: https://pypi.org/p/claude-top
39
+ permissions:
40
+ id-token: write
41
+
42
+ steps:
43
+ - uses: actions/checkout@v4
44
+
45
+ - name: Set up Python
46
+ uses: actions/setup-python@v5
47
+ with:
48
+ python-version: '3.13'
49
+
50
+ - name: Install build dependencies
51
+ run: |
52
+ python -m pip install --upgrade pip
53
+ pip install build
54
+
55
+ - name: Build package
56
+ run: python -m build
57
+
58
+ - name: Publish to PyPI
59
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,61 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [ main, develop ]
6
+ pull_request:
7
+ branches: [ main, develop ]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ${{ matrix.os }}
12
+ strategy:
13
+ matrix:
14
+ os: [ubuntu-latest, windows-latest, macos-latest]
15
+ python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14']
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Set up Python ${{ matrix.python-version }}
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+
25
+ - name: Install dependencies
26
+ run: |
27
+ python -m pip install --upgrade pip
28
+ pip install -e ".[dev]"
29
+
30
+ - name: Run tests
31
+ run: |
32
+ pytest tests/ -v --cov=claude_top --cov-report=term-missing
33
+
34
+ - name: Upload coverage
35
+ if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.14'
36
+ uses: codecov/codecov-action@v4
37
+ with:
38
+ fail_ci_if_error: false
39
+
40
+ lint:
41
+ runs-on: ubuntu-latest
42
+ steps:
43
+ - uses: actions/checkout@v4
44
+
45
+ - name: Set up Python
46
+ uses: actions/setup-python@v5
47
+ with:
48
+ python-version: '3.14'
49
+
50
+ - name: Install dependencies
51
+ run: |
52
+ python -m pip install --upgrade pip
53
+ pip install ruff black
54
+
55
+ - name: Lint with ruff
56
+ run: |
57
+ ruff check src/
58
+
59
+ - name: Check formatting with black
60
+ run: |
61
+ black --check src/
@@ -0,0 +1,49 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual environments
24
+ venv/
25
+ env/
26
+ ENV/
27
+ .venv/
28
+
29
+ # IDEs
30
+ .vscode/
31
+ .idea/
32
+ *.swp
33
+ *.swo
34
+ *~
35
+
36
+ # OS
37
+ .DS_Store
38
+ Thumbs.db
39
+
40
+ # Testing
41
+ .pytest_cache/
42
+ .coverage
43
+ htmlcov/
44
+
45
+ # Build
46
+ *.whl
47
+
48
+ # Local History
49
+ .lh/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 claude-top contributors
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,176 @@
1
+ Metadata-Version: 2.4
2
+ Name: claude-top
3
+ Version: 0.1.0
4
+ Summary: CLI + TUI for viewing Claude Code usage statistics from local session files
5
+ Project-URL: Homepage, https://github.com/xpodev/claude-top
6
+ Project-URL: Repository, https://github.com/xpodev/claude-top
7
+ Project-URL: Issues, https://github.com/xpodev/claude-top/issues
8
+ Author-email: Xpo Development <dev@xpo.dev>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: anthropic,claude,cli,monitoring,tui,usage
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Topic :: Utilities
22
+ Requires-Python: >=3.9
23
+ Requires-Dist: python-dateutil>=2.8.0
24
+ Requires-Dist: requests>=2.31.0
25
+ Requires-Dist: rich>=13.0.0
26
+ Requires-Dist: textual>=0.47.0
27
+ Requires-Dist: typer>=0.9.0
28
+ Provides-Extra: dev
29
+ Requires-Dist: black>=23.0.0; extra == 'dev'
30
+ Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
31
+ Requires-Dist: pytest>=7.4.0; extra == 'dev'
32
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
33
+ Description-Content-Type: text/markdown
34
+
35
+ # claude-top
36
+
37
+ ![Claude Top screenshot](images/claude-top.png)
38
+
39
+ A CLI + TUI utility for inspecting Claude Code usage from local session files.
40
+
41
+ `claude-top` reads your local Claude history (`~/.claude/projects/**/*.jsonl`), aggregates token/request metrics, and presents them in either:
42
+
43
+ - an interactive Textual dashboard (default),
44
+ - a Rich terminal table (`--no-ui`), or
45
+ - JSON (`--json`).
46
+
47
+ ## What it shows
48
+
49
+ - Total input/output tokens and request count
50
+ - Per-model usage breakdown
51
+ - Optional detailed stats (`--detailed`):
52
+ - cache reads/writes and hit rate
53
+ - average tokens per request
54
+ - estimated cost (informational)
55
+ - 7-day trend sparkline
56
+ - week-over-week comparison
57
+ - top projects by token usage
58
+ - Tier-aware usage bars and warnings (80%/90%) when tier metadata is available
59
+
60
+ ## Requirements
61
+
62
+ - Python 3.9+
63
+ - Existing Claude Code data in `~/.claude/projects`
64
+
65
+ Notes:
66
+ - No setup is required to read local usage files.
67
+ - If `~/.claude/.credentials.json` is present with a valid OAuth token, `claude-top` also tries to fetch usage window reset metadata from Anthropic OAuth usage API for better countdowns.
68
+
69
+ ## Installation
70
+
71
+ ### Run without install (uvx)
72
+
73
+ ```bash
74
+ uvx claude-top
75
+ ```
76
+
77
+ ### Install globally (uv)
78
+
79
+ ```bash
80
+ uv tool install claude-top
81
+ ```
82
+
83
+ ### Install with pip
84
+
85
+ ```bash
86
+ pip install claude-top
87
+ ```
88
+
89
+ ### From source
90
+
91
+ ```bash
92
+ git clone https://github.com/xpodev/claude-top.git
93
+ cd claude-top
94
+ uv pip install -e .
95
+ ```
96
+
97
+ ## Usage
98
+
99
+ ```bash
100
+ # Launch TUI (auto-refresh by default)
101
+ claude-top
102
+
103
+ # Print terminal table once and exit
104
+ claude-top --no-ui --once
105
+
106
+ # Print terminal table with refresh
107
+ claude-top --no-ui --watch 5
108
+
109
+ # Print JSON once and exit
110
+ claude-top --json
111
+
112
+ # Include detailed analytics
113
+ claude-top --detailed
114
+ ```
115
+
116
+ ### CLI options
117
+
118
+ - `--once`: Display once and exit.
119
+ - `--no-ui`: Use table output instead of the TUI.
120
+ - `--json`: Print JSON output and exit.
121
+ - `--detailed`: Include detailed analytics.
122
+ - `--watch N`: Refresh interval in seconds (default: 1 when not using `--once`).
123
+
124
+ ### TUI keybindings
125
+
126
+ - `r`: Refresh now
127
+ - `q`: Quit
128
+
129
+ ## How data is calculated
130
+
131
+ - Scans all JSONL events in `~/.claude/projects` recursively.
132
+ - Counts each `assistant` event as one request.
133
+ - Aggregates token fields from each assistant message usage payload:
134
+ - `input_tokens`
135
+ - `output_tokens`
136
+ - `cache_creation_input_tokens`
137
+ - `cache_read_input_tokens`
138
+ - Derives project names from common event path/project fields.
139
+ - Builds last-7-day trend and week-over-week token comparison from timestamps.
140
+
141
+ ## Troubleshooting
142
+
143
+ ### "No Claude Code session data found"
144
+
145
+ `claude-top` only reads local Claude session files. Make sure:
146
+
147
+ 1. Claude Code has been used on this machine.
148
+ 2. `~/.claude/projects` exists and contains `.jsonl` files.
149
+
150
+ ### Tier/reset countdown is missing
151
+
152
+ Tier and reset countdown information depends on OAuth metadata. If unavailable:
153
+
154
+ - ensure `~/.claude/.credentials.json` exists,
155
+ - ensure the OAuth token is valid,
156
+ - check network access to Anthropic API.
157
+
158
+ The tool still works with local usage data even if tier/reset metadata cannot be fetched.
159
+
160
+ ## Development
161
+
162
+ ```bash
163
+ # Clone repository
164
+ git clone https://github.com/xpodev/claude-top.git
165
+ cd claude-top
166
+
167
+ # Install with development dependencies
168
+ uv pip install -e ".[dev]"
169
+
170
+ # Run tests
171
+ uv run pytest -q
172
+ ```
173
+
174
+ ## License
175
+
176
+ MIT
@@ -0,0 +1,142 @@
1
+ # claude-top
2
+
3
+ ![Claude Top screenshot](images/claude-top.png)
4
+
5
+ A CLI + TUI utility for inspecting Claude Code usage from local session files.
6
+
7
+ `claude-top` reads your local Claude history (`~/.claude/projects/**/*.jsonl`), aggregates token/request metrics, and presents them in either:
8
+
9
+ - an interactive Textual dashboard (default),
10
+ - a Rich terminal table (`--no-ui`), or
11
+ - JSON (`--json`).
12
+
13
+ ## What it shows
14
+
15
+ - Total input/output tokens and request count
16
+ - Per-model usage breakdown
17
+ - Optional detailed stats (`--detailed`):
18
+ - cache reads/writes and hit rate
19
+ - average tokens per request
20
+ - estimated cost (informational)
21
+ - 7-day trend sparkline
22
+ - week-over-week comparison
23
+ - top projects by token usage
24
+ - Tier-aware usage bars and warnings (80%/90%) when tier metadata is available
25
+
26
+ ## Requirements
27
+
28
+ - Python 3.9+
29
+ - Existing Claude Code data in `~/.claude/projects`
30
+
31
+ Notes:
32
+ - No setup is required to read local usage files.
33
+ - If `~/.claude/.credentials.json` is present with a valid OAuth token, `claude-top` also tries to fetch usage window reset metadata from Anthropic OAuth usage API for better countdowns.
34
+
35
+ ## Installation
36
+
37
+ ### Run without install (uvx)
38
+
39
+ ```bash
40
+ uvx claude-top
41
+ ```
42
+
43
+ ### Install globally (uv)
44
+
45
+ ```bash
46
+ uv tool install claude-top
47
+ ```
48
+
49
+ ### Install with pip
50
+
51
+ ```bash
52
+ pip install claude-top
53
+ ```
54
+
55
+ ### From source
56
+
57
+ ```bash
58
+ git clone https://github.com/xpodev/claude-top.git
59
+ cd claude-top
60
+ uv pip install -e .
61
+ ```
62
+
63
+ ## Usage
64
+
65
+ ```bash
66
+ # Launch TUI (auto-refresh by default)
67
+ claude-top
68
+
69
+ # Print terminal table once and exit
70
+ claude-top --no-ui --once
71
+
72
+ # Print terminal table with refresh
73
+ claude-top --no-ui --watch 5
74
+
75
+ # Print JSON once and exit
76
+ claude-top --json
77
+
78
+ # Include detailed analytics
79
+ claude-top --detailed
80
+ ```
81
+
82
+ ### CLI options
83
+
84
+ - `--once`: Display once and exit.
85
+ - `--no-ui`: Use table output instead of the TUI.
86
+ - `--json`: Print JSON output and exit.
87
+ - `--detailed`: Include detailed analytics.
88
+ - `--watch N`: Refresh interval in seconds (default: 1 when not using `--once`).
89
+
90
+ ### TUI keybindings
91
+
92
+ - `r`: Refresh now
93
+ - `q`: Quit
94
+
95
+ ## How data is calculated
96
+
97
+ - Scans all JSONL events in `~/.claude/projects` recursively.
98
+ - Counts each `assistant` event as one request.
99
+ - Aggregates token fields from each assistant message usage payload:
100
+ - `input_tokens`
101
+ - `output_tokens`
102
+ - `cache_creation_input_tokens`
103
+ - `cache_read_input_tokens`
104
+ - Derives project names from common event path/project fields.
105
+ - Builds last-7-day trend and week-over-week token comparison from timestamps.
106
+
107
+ ## Troubleshooting
108
+
109
+ ### "No Claude Code session data found"
110
+
111
+ `claude-top` only reads local Claude session files. Make sure:
112
+
113
+ 1. Claude Code has been used on this machine.
114
+ 2. `~/.claude/projects` exists and contains `.jsonl` files.
115
+
116
+ ### Tier/reset countdown is missing
117
+
118
+ Tier and reset countdown information depends on OAuth metadata. If unavailable:
119
+
120
+ - ensure `~/.claude/.credentials.json` exists,
121
+ - ensure the OAuth token is valid,
122
+ - check network access to Anthropic API.
123
+
124
+ The tool still works with local usage data even if tier/reset metadata cannot be fetched.
125
+
126
+ ## Development
127
+
128
+ ```bash
129
+ # Clone repository
130
+ git clone https://github.com/xpodev/claude-top.git
131
+ cd claude-top
132
+
133
+ # Install with development dependencies
134
+ uv pip install -e ".[dev]"
135
+
136
+ # Run tests
137
+ uv run pytest -q
138
+ ```
139
+
140
+ ## License
141
+
142
+ MIT
Binary file
@@ -0,0 +1,77 @@
1
+ [project]
2
+ name = "claude-top"
3
+ version = "0.1.0"
4
+ description = "CLI + TUI for viewing Claude Code usage statistics from local session files"
5
+ readme = "README.md"
6
+ requires-python = ">=3.9"
7
+ license = {text = "MIT"}
8
+ authors = [
9
+ {name = "Xpo Development", email = "dev@xpo.dev"}
10
+ ]
11
+ keywords = ["claude", "anthropic", "usage", "cli", "tui", "monitoring"]
12
+ classifiers = [
13
+ "Development Status :: 4 - Beta",
14
+ "Intended Audience :: Developers",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.9",
18
+ "Programming Language :: Python :: 3.10",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Topic :: Software Development :: Libraries :: Python Modules",
22
+ "Topic :: Utilities",
23
+ ]
24
+ dependencies = [
25
+ "textual>=0.47.0",
26
+ "rich>=13.0.0",
27
+ "typer>=0.9.0",
28
+ "python-dateutil>=2.8.0",
29
+ "requests>=2.31.0",
30
+ ]
31
+
32
+ [project.urls]
33
+ Homepage = "https://github.com/xpodev/claude-top"
34
+ Repository = "https://github.com/xpodev/claude-top"
35
+ Issues = "https://github.com/xpodev/claude-top/issues"
36
+
37
+ [project.optional-dependencies]
38
+ dev = [
39
+ "pytest>=7.4.0",
40
+ "pytest-cov>=4.1.0",
41
+ "ruff>=0.1.0",
42
+ "black>=23.0.0",
43
+ ]
44
+
45
+ [project.scripts]
46
+ claude-top = "claude_top.cli:cli"
47
+
48
+ [build-system]
49
+ requires = ["hatchling"]
50
+ build-backend = "hatchling.build"
51
+
52
+ [tool.hatch.build.targets.wheel]
53
+ packages = ["src/claude_top"]
54
+
55
+ [tool.pytest.ini_options]
56
+ testpaths = ["tests"]
57
+ python_files = ["test_*.py"]
58
+ python_classes = ["Test*"]
59
+ python_functions = ["test_*"]
60
+ addopts = [
61
+ "--strict-markers",
62
+ "--strict-config",
63
+ "-ra",
64
+ ]
65
+
66
+ [tool.ruff]
67
+ line-length = 100
68
+ target-version = "py39"
69
+ src = ["src"]
70
+
71
+ [tool.ruff.lint]
72
+ select = ["E", "F", "W", "I", "N", "UP"]
73
+ ignore = ["E501"] # Line too long
74
+
75
+ [tool.black]
76
+ line-length = 100
77
+ target-version = ["py39"]
@@ -0,0 +1,5 @@
1
+ [pytest]
2
+ minversion = 7.0
3
+ testpaths = tests
4
+ addopts = -q
5
+ python_files = test_*.py
@@ -0,0 +1,3 @@
1
+ """Claude Top - View Claude Code API usage statistics."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,101 @@
1
+ """Authentication and credential management using system keyring."""
2
+
3
+ import json
4
+ from datetime import datetime
5
+ from pathlib import Path
6
+ from typing import Any, Optional
7
+
8
+ import keyring
9
+
10
+ SERVICE_NAME = "claude-top"
11
+ USERNAME = "api-key"
12
+
13
+
14
+ def get_claude_code_credentials() -> Optional[dict[str, Any]]:
15
+ """
16
+ Read credentials from Claude Code's .credentials.json file.
17
+
18
+ Returns:
19
+ Dictionary with OAuth credentials if found, None otherwise
20
+ """
21
+ # Try common locations for .claude directory
22
+ possible_paths = [
23
+ Path.home() / ".claude" / ".credentials.json",
24
+ Path("~/.claude/.credentials.json").expanduser(),
25
+ ]
26
+
27
+ for creds_path in possible_paths:
28
+ if creds_path.exists():
29
+ try:
30
+ with open(creds_path) as f:
31
+ data = json.load(f)
32
+
33
+ if "claudeAiOauth" in data:
34
+ oauth = data["claudeAiOauth"]
35
+
36
+ # Check if token is expired
37
+ expires_at = oauth.get("expiresAt", 0)
38
+ if expires_at > datetime.now().timestamp() * 1000:
39
+ return {
40
+ "access_token": oauth.get("accessToken"),
41
+ "refresh_token": oauth.get("refreshToken"),
42
+ "expires_at": expires_at,
43
+ "source": "claude_code",
44
+ }
45
+ except (OSError, json.JSONDecodeError):
46
+ continue
47
+
48
+ return None
49
+
50
+
51
+ def get_api_key() -> Optional[str]:
52
+ """
53
+ Retrieve API key/token with the following priority:
54
+ 1. Claude Code OAuth token (if available and not expired)
55
+ 2. Manually stored API key in keyring
56
+
57
+ Returns:
58
+ API key or OAuth access token
59
+ """
60
+ # First, try Claude Code credentials
61
+ claude_creds = get_claude_code_credentials()
62
+ if claude_creds and claude_creds.get("access_token"):
63
+ return claude_creds["access_token"]
64
+
65
+ # Fall back to keyring
66
+ return keyring.get_password(SERVICE_NAME, USERNAME)
67
+
68
+
69
+ def save_api_key(api_key: str) -> None:
70
+ """Save API key to system keyring."""
71
+ keyring.set_password(SERVICE_NAME, USERNAME, api_key)
72
+
73
+
74
+ def delete_api_key() -> None:
75
+ """Remove API key from system keyring."""
76
+ try:
77
+ keyring.delete_password(SERVICE_NAME, USERNAME)
78
+ except keyring.errors.PasswordDeleteError:
79
+ pass
80
+
81
+
82
+ def is_authenticated() -> bool:
83
+ """Check if user is authenticated (via Claude Code or keyring)."""
84
+ return get_api_key() is not None
85
+
86
+
87
+ def get_auth_source() -> str:
88
+ """
89
+ Determine the source of authentication.
90
+
91
+ Returns:
92
+ "claude_code", "keyring", or "none"
93
+ """
94
+ claude_creds = get_claude_code_credentials()
95
+ if claude_creds and claude_creds.get("access_token"):
96
+ return "claude_code"
97
+
98
+ if keyring.get_password(SERVICE_NAME, USERNAME):
99
+ return "keyring"
100
+
101
+ return "none"