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.
- claude_top-0.1.0/.github/workflows/publish.yml +59 -0
- claude_top-0.1.0/.github/workflows/tests.yml +61 -0
- claude_top-0.1.0/.gitignore +49 -0
- claude_top-0.1.0/LICENSE +21 -0
- claude_top-0.1.0/PKG-INFO +176 -0
- claude_top-0.1.0/README.md +142 -0
- claude_top-0.1.0/images/claude-top.png +0 -0
- claude_top-0.1.0/pyproject.toml +77 -0
- claude_top-0.1.0/pytest.ini +5 -0
- claude_top-0.1.0/src/claude_top/__init__.py +3 -0
- claude_top-0.1.0/src/claude_top/auth.py +101 -0
- claude_top-0.1.0/src/claude_top/cli.py +330 -0
- claude_top-0.1.0/src/claude_top/data.py +469 -0
- claude_top-0.1.0/src/claude_top/limits.py +152 -0
- claude_top-0.1.0/src/claude_top/oauth.py +130 -0
- claude_top-0.1.0/src/claude_top/ui.py +476 -0
- claude_top-0.1.0/tests/__init__.py +1 -0
- claude_top-0.1.0/tests/test_data.py +77 -0
- claude_top-0.1.0/tests/test_limits.py +52 -0
- claude_top-0.1.0/uv.lock +1148 -0
|
@@ -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/
|
claude_top-0.1.0/LICENSE
ADDED
|
@@ -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
|
+

|
|
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
|
+

|
|
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,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"
|