claude-usage-rzp 0.1.0
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.
- package/README.md +239 -0
- package/dist/commands/config.js +17 -0
- package/dist/commands/interactive.js +217 -0
- package/dist/commands/overview.js +98 -0
- package/dist/commands/project.js +78 -0
- package/dist/commands/projects.js +66 -0
- package/dist/commands/session.js +98 -0
- package/dist/config.js +33 -0
- package/dist/index.js +65 -0
- package/dist/loader.js +299 -0
- package/dist/pricing.js +55 -0
- package/dist/types.js +2 -0
- package/package.json +48 -0
- package/src/commands/config.ts +16 -0
- package/src/commands/interactive.ts +271 -0
- package/src/commands/overview.ts +126 -0
- package/src/commands/project.ts +85 -0
- package/src/commands/projects.ts +83 -0
- package/src/commands/session.ts +105 -0
- package/src/config.ts +34 -0
- package/src/index.ts +73 -0
- package/src/loader.ts +360 -0
- package/src/pricing.ts +68 -0
- package/src/types.ts +78 -0
- package/tsconfig.json +17 -0
package/README.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Claude Usage CLI
|
|
2
|
+
|
|
3
|
+
> View your Claude Code API usage directly in your terminal š
|
|
4
|
+
|
|
5
|
+
A beautiful command-line tool to visualize Claude Code token usage, costs, and activity across all your projects.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
⨠**Interactive Mode** - Beautiful menu-driven interface with Claude branding
|
|
10
|
+
š **Token Breakdown** - Input, Output, Cache Write, and Cache Read tokens by model
|
|
11
|
+
š **Project Analytics** - View usage breakdown by project
|
|
12
|
+
š¬ **Session Details** - Drill down to individual session messages
|
|
13
|
+
šØ **Beautiful Output** - Colorful tables with proper formatting and emojis
|
|
14
|
+
ā” **Fast & Lightweight** - No external services, reads local Claude data
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
### Using npx (Recommended)
|
|
19
|
+
|
|
20
|
+
Run instantly without installation:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npx claude-usage-rzp
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Installation
|
|
27
|
+
|
|
28
|
+
Install globally to use anywhere:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install -g claude-usage-rzp
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Then simply run:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
claude-usage
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
### Interactive Mode (Default)
|
|
43
|
+
|
|
44
|
+
Running `claude-usage` launches an interactive menu where you can explore your usage data:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
claude-usage
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The interactive mode features:
|
|
51
|
+
- š Summary with total sessions, messages, tokens, and costs
|
|
52
|
+
- š
Last 7 Days Activity
|
|
53
|
+
- š Token Usage by Model
|
|
54
|
+
- š All Projects view
|
|
55
|
+
- š Refresh Data
|
|
56
|
+
- Beautiful Claude ASCII art header in orange
|
|
57
|
+
|
|
58
|
+
### Commands
|
|
59
|
+
|
|
60
|
+
#### Overview
|
|
61
|
+
Shows total usage statistics, token breakdown by model, and recent activity:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
claude-usage overview
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
#### List Projects
|
|
68
|
+
View all projects with session counts and total costs:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
claude-usage projects
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
#### View Project
|
|
75
|
+
See all sessions within a specific project:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
claude-usage project /path/to/project
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
#### View Session
|
|
82
|
+
Get detailed message-level breakdown for a session:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
claude-usage session <session-id>
|
|
86
|
+
|
|
87
|
+
# Or specify project
|
|
88
|
+
claude-usage session <session-id> --project /path/to/project
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
#### Configuration
|
|
92
|
+
Check current settings and data directory:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
claude-usage config
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Options
|
|
99
|
+
|
|
100
|
+
#### Custom Data Directory
|
|
101
|
+
|
|
102
|
+
By default, the tool reads from `~/.claude`. To use a different location:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
claude-usage --data-dir /custom/path/to/claude/data
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Or set the `CLAUDE_DATA_DIR` environment variable:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
export CLAUDE_DATA_DIR=/custom/path/to/claude/data
|
|
112
|
+
claude-usage
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Example Output
|
|
116
|
+
|
|
117
|
+
### Interactive Mode
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
āāāāāāāāāā āāāāāā āāā āāāāāāāāāā āāāāāāāā
|
|
121
|
+
āāāāāāāāāāā āāāāāāāāāāā āāāāāāāāāāāāāāāāāāā
|
|
122
|
+
āāā āāā āāāāāāāāāāā āāāāāā āāāāāāāāā
|
|
123
|
+
āāā āāā āāāāāāāāāāā āāāāāā āāāāāāāāā
|
|
124
|
+
āāāāāāāāāāāāāāāāāāā āāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
125
|
+
āāāāāāāāāāāāāāāāāā āāā āāāāāāā āāāāāāā āāāāāāāā
|
|
126
|
+
|
|
127
|
+
Usage Visualizer ć» Track your AI costs
|
|
128
|
+
|
|
129
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
130
|
+
|
|
131
|
+
š Summary:
|
|
132
|
+
š¬ Total Sessions: 32
|
|
133
|
+
šØ Total Messages: 5,393
|
|
134
|
+
š¢ Total Tokens Used: 141,115,796
|
|
135
|
+
ā¬ļø Total Input Tokens: 54,341,054
|
|
136
|
+
ā¬ļø Total Output Tokens: 12,326
|
|
137
|
+
š° Estimated Cost: $1063.1512
|
|
138
|
+
|
|
139
|
+
? What would you like to see? (Use arrow keys)
|
|
140
|
+
⯠š
Last 7 Days Activity
|
|
141
|
+
š Token Usage by Model
|
|
142
|
+
š All Projects
|
|
143
|
+
āāāāāāāāāāāāāā
|
|
144
|
+
š Refresh Data
|
|
145
|
+
ā Exit
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Token Usage by Model
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
š Token Usage by Model:
|
|
152
|
+
|
|
153
|
+
āāāāāāāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāā¬āāāāāāāāā¬āāāāāāāāāāāāāā¬āāāāāāāāāāāāā¬āāāāāāāāāāāā
|
|
154
|
+
ā Model ā Input ā Output ā Cache Write ā Cache Read ā Cost ā
|
|
155
|
+
āāāāāāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāā¼āāāāāāāāā¼āāāāāāāāāāāāāā¼āāāāāāāāāāāāā¼āāāāāāāāāāāā¤
|
|
156
|
+
ā opus-4-6 ā 54,272,618 ā 4,678 ā 428,133 ā 1,735,051 ā $825.0702 ā
|
|
157
|
+
ā sonnet-4-5 (20250929) ā 17,478 ā 568 ā 1,852,448 ā 21,403,174 ā $13.4286 ā
|
|
158
|
+
āāāāāāāāāāāāāāāāāāāāāāāāā“āāāāāāāāāāāāā“āāāāāāāāā“āāāāāāāāāāāāāā“āāāāāāāāāāāāā“āāāāāāāāāāāā
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## How It Works
|
|
162
|
+
|
|
163
|
+
Claude Usage CLI reads your local Claude Code data files:
|
|
164
|
+
- `stats-cache.json` - Aggregated statistics
|
|
165
|
+
- `history.jsonl` - Session metadata and names
|
|
166
|
+
- `projects/` - Individual session message logs
|
|
167
|
+
|
|
168
|
+
All data stays local. No external API calls or data transmission.
|
|
169
|
+
|
|
170
|
+
## Development
|
|
171
|
+
|
|
172
|
+
### Local Development
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# Clone the repository
|
|
176
|
+
git clone https://github.com/hamid-miran/claude-usage-cli.git
|
|
177
|
+
cd claude-usage-cli
|
|
178
|
+
|
|
179
|
+
# Install dependencies
|
|
180
|
+
npm install
|
|
181
|
+
|
|
182
|
+
# Build TypeScript
|
|
183
|
+
npm run build
|
|
184
|
+
|
|
185
|
+
# Run locally
|
|
186
|
+
node dist/index.js
|
|
187
|
+
|
|
188
|
+
# Or use tsx for development
|
|
189
|
+
npm run dev
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Project Structure
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
src/
|
|
196
|
+
āāā index.ts # Main CLI entry point
|
|
197
|
+
āāā types.ts # TypeScript type definitions
|
|
198
|
+
āāā config.ts # Configuration and data directory
|
|
199
|
+
āāā loader.ts # Data loading from Claude files
|
|
200
|
+
āāā pricing.ts # Token pricing and cost calculation
|
|
201
|
+
āāā commands/ # CLI command implementations
|
|
202
|
+
āāā interactive.ts # Interactive menu mode
|
|
203
|
+
āāā overview.ts
|
|
204
|
+
āāā projects.ts
|
|
205
|
+
āāā project.ts
|
|
206
|
+
āāā session.ts
|
|
207
|
+
āāā config.ts
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Requirements
|
|
211
|
+
|
|
212
|
+
- Node.js >= 18.0.0
|
|
213
|
+
- Claude Code data directory (usually `~/.claude`)
|
|
214
|
+
|
|
215
|
+
## Publishing to npm
|
|
216
|
+
|
|
217
|
+
To make this available via `npx` for everyone:
|
|
218
|
+
|
|
219
|
+
1. Create an npm account at [npmjs.com](https://www.npmjs.com)
|
|
220
|
+
2. Login locally: `npm login`
|
|
221
|
+
3. Update package name to be unique (if needed)
|
|
222
|
+
4. Publish: `npm publish`
|
|
223
|
+
|
|
224
|
+
Then users can run:
|
|
225
|
+
```bash
|
|
226
|
+
npx claude-usage-rzp
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## License
|
|
230
|
+
|
|
231
|
+
MIT
|
|
232
|
+
|
|
233
|
+
## Contributing
|
|
234
|
+
|
|
235
|
+
Contributions welcome! Please open an issue or PR.
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
Created by Hamid š
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.configCommand = configCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const config_1 = require("../config");
|
|
9
|
+
function configCommand() {
|
|
10
|
+
console.log(chalk_1.default.cyan.bold('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
11
|
+
console.log(chalk_1.default.cyan.bold(' Configuration'));
|
|
12
|
+
console.log(chalk_1.default.cyan.bold('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n'));
|
|
13
|
+
console.log(` Data Directory: ${chalk_1.default.green((0, config_1.getDataDir)())}`);
|
|
14
|
+
console.log(` Has stats-cache.json: ${(0, config_1.hasStatsCache)() ? chalk_1.default.green('ā') : chalk_1.default.red('ā')}`);
|
|
15
|
+
console.log(` Has projects/: ${(0, config_1.hasProjects)() ? chalk_1.default.green('ā') : chalk_1.default.red('ā')}`);
|
|
16
|
+
console.log(chalk_1.default.dim('\nš” Set CLAUDE_DATA_DIR environment variable or use --data-dir option\n'));
|
|
17
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.interactiveCommand = interactiveCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
9
|
+
const cli_table3_1 = __importDefault(require("cli-table3"));
|
|
10
|
+
const loader_1 = require("../loader");
|
|
11
|
+
const pricing_1 = require("../pricing");
|
|
12
|
+
const config_1 = require("../config");
|
|
13
|
+
async function showSummary() {
|
|
14
|
+
const stats = (0, loader_1.loadStatsCache)();
|
|
15
|
+
if (!stats)
|
|
16
|
+
return false;
|
|
17
|
+
// Calculate total cost
|
|
18
|
+
const totalCost = Object.entries(stats.modelStats).reduce((sum, [modelId, model]) => {
|
|
19
|
+
return (sum +
|
|
20
|
+
(0, pricing_1.estimateCost)(modelId, model.inputTokens, model.outputTokens, model.cacheCreationInputTokens, model.cacheReadInputTokens));
|
|
21
|
+
}, 0);
|
|
22
|
+
// Calculate total cache tokens across all models
|
|
23
|
+
let totalCacheWrite = 0;
|
|
24
|
+
let totalCacheRead = 0;
|
|
25
|
+
for (const model of Object.values(stats.modelStats)) {
|
|
26
|
+
totalCacheWrite += model.cacheCreationInputTokens;
|
|
27
|
+
totalCacheRead += model.cacheReadInputTokens;
|
|
28
|
+
}
|
|
29
|
+
const totalTokens = stats.totalInputTokens + stats.totalOutputTokens + totalCacheWrite + totalCacheRead;
|
|
30
|
+
console.log(chalk_1.default.bold('\nš Summary:'));
|
|
31
|
+
console.log(` š¬ Total Sessions: ${chalk_1.default.green(stats.totalSessions.toLocaleString('en-US'))}`);
|
|
32
|
+
console.log(` šØ Total Messages: ${chalk_1.default.green(stats.totalMessages.toLocaleString('en-US'))}`);
|
|
33
|
+
console.log(` š¢ Total Tokens Used: ${chalk_1.default.green.bold((0, pricing_1.formatTokens)(totalTokens))}`);
|
|
34
|
+
console.log(` ā¬ļø Total Input Tokens: ${chalk_1.default.cyan((0, pricing_1.formatTokens)(stats.totalInputTokens))}`);
|
|
35
|
+
console.log(` ā¬ļø Total Output Tokens: ${chalk_1.default.green((0, pricing_1.formatTokens)(stats.totalOutputTokens))}`);
|
|
36
|
+
console.log(` š° Estimated Cost: ${chalk_1.default.hex('#FF6B35').bold((0, pricing_1.formatCost)(totalCost))}`);
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
async function showLast7Days() {
|
|
40
|
+
const stats = (0, loader_1.loadStatsCache)();
|
|
41
|
+
if (!stats)
|
|
42
|
+
return;
|
|
43
|
+
const recent = stats.dailyActivity.slice(-7);
|
|
44
|
+
console.log(chalk_1.default.bold('\nš
Recent Activity (Last 7 Days):'));
|
|
45
|
+
const activityTable = new cli_table3_1.default({
|
|
46
|
+
head: [
|
|
47
|
+
chalk_1.default.cyan('Date'),
|
|
48
|
+
chalk_1.default.white('Sessions'),
|
|
49
|
+
chalk_1.default.white('Messages'),
|
|
50
|
+
chalk_1.default.white('Tool Calls'),
|
|
51
|
+
],
|
|
52
|
+
colAligns: ['left', 'right', 'right', 'right'],
|
|
53
|
+
});
|
|
54
|
+
for (const day of recent) {
|
|
55
|
+
activityTable.push([
|
|
56
|
+
day.date,
|
|
57
|
+
day.sessionCount.toString(),
|
|
58
|
+
day.messageCount.toString(),
|
|
59
|
+
day.toolCallCount.toString(),
|
|
60
|
+
]);
|
|
61
|
+
}
|
|
62
|
+
console.log(activityTable.toString());
|
|
63
|
+
}
|
|
64
|
+
async function showTokensByModel() {
|
|
65
|
+
const stats = (0, loader_1.loadStatsCache)();
|
|
66
|
+
if (!stats)
|
|
67
|
+
return;
|
|
68
|
+
console.log(chalk_1.default.bold('\nš Token Usage by Model:'));
|
|
69
|
+
const table = new cli_table3_1.default({
|
|
70
|
+
head: [
|
|
71
|
+
chalk_1.default.cyan('Model'),
|
|
72
|
+
chalk_1.default.blue('Input'),
|
|
73
|
+
chalk_1.default.green('Output'),
|
|
74
|
+
chalk_1.default.yellow('Cache Write'),
|
|
75
|
+
chalk_1.default.magenta('Cache Read'),
|
|
76
|
+
chalk_1.default.green.bold('Cost'),
|
|
77
|
+
],
|
|
78
|
+
colAligns: ['left', 'right', 'right', 'right', 'right', 'right'],
|
|
79
|
+
});
|
|
80
|
+
for (const [modelId, model] of Object.entries(stats.modelStats)) {
|
|
81
|
+
const cost = (0, pricing_1.estimateCost)(modelId, model.inputTokens, model.outputTokens, model.cacheCreationInputTokens, model.cacheReadInputTokens);
|
|
82
|
+
let displayName = modelId.replace('claude-', '').replace(/-20\d{6}/, (match) => ` (${match.substring(1)})`);
|
|
83
|
+
table.push([
|
|
84
|
+
displayName,
|
|
85
|
+
(0, pricing_1.formatTokens)(model.inputTokens),
|
|
86
|
+
(0, pricing_1.formatTokens)(model.outputTokens),
|
|
87
|
+
(0, pricing_1.formatTokens)(model.cacheCreationInputTokens),
|
|
88
|
+
(0, pricing_1.formatTokens)(model.cacheReadInputTokens),
|
|
89
|
+
(0, pricing_1.formatCost)(cost),
|
|
90
|
+
]);
|
|
91
|
+
}
|
|
92
|
+
console.log(table.toString());
|
|
93
|
+
}
|
|
94
|
+
async function showProjects() {
|
|
95
|
+
const projectSummaries = (0, loader_1.buildProjectSummaries)();
|
|
96
|
+
if (Object.keys(projectSummaries).length === 0) {
|
|
97
|
+
console.log(chalk_1.default.yellow('\nNo projects found'));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const projectData = [];
|
|
101
|
+
for (const [encodedPath, sessions] of Object.entries(projectSummaries)) {
|
|
102
|
+
const displayPath = decodeURIComponent(encodedPath);
|
|
103
|
+
const sessionCount = sessions.length;
|
|
104
|
+
const totalInput = sessions.reduce((sum, s) => sum + s.totalInputTokens, 0);
|
|
105
|
+
const totalOutput = sessions.reduce((sum, s) => sum + s.totalOutputTokens, 0);
|
|
106
|
+
const totalCost = sessions.reduce((sum, s) => sum + s.totalCostUsd, 0);
|
|
107
|
+
projectData.push({
|
|
108
|
+
path: displayPath,
|
|
109
|
+
encoded: encodedPath,
|
|
110
|
+
sessions: sessionCount,
|
|
111
|
+
input: totalInput,
|
|
112
|
+
output: totalOutput,
|
|
113
|
+
cost: totalCost,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
projectData.sort((a, b) => b.cost - a.cost);
|
|
117
|
+
console.log(chalk_1.default.bold(`\nš Projects (${projectData.length} total):`));
|
|
118
|
+
const table = new cli_table3_1.default({
|
|
119
|
+
head: [
|
|
120
|
+
chalk_1.default.cyan('Project Path'),
|
|
121
|
+
chalk_1.default.white('Sessions'),
|
|
122
|
+
chalk_1.default.blue('Input Tokens'),
|
|
123
|
+
chalk_1.default.green('Output Tokens'),
|
|
124
|
+
chalk_1.default.green.bold('Total Cost'),
|
|
125
|
+
],
|
|
126
|
+
colAligns: ['left', 'right', 'right', 'right', 'right'],
|
|
127
|
+
colWidths: [40, 10, 15, 15, 12],
|
|
128
|
+
wordWrap: true,
|
|
129
|
+
});
|
|
130
|
+
for (const proj of projectData) {
|
|
131
|
+
table.push([
|
|
132
|
+
proj.path,
|
|
133
|
+
proj.sessions.toString(),
|
|
134
|
+
(0, pricing_1.formatTokens)(proj.input),
|
|
135
|
+
(0, pricing_1.formatTokens)(proj.output),
|
|
136
|
+
(0, pricing_1.formatCost)(proj.cost),
|
|
137
|
+
]);
|
|
138
|
+
}
|
|
139
|
+
console.log(table.toString());
|
|
140
|
+
}
|
|
141
|
+
async function interactiveCommand() {
|
|
142
|
+
if (!(0, config_1.hasStatsCache)()) {
|
|
143
|
+
console.log(chalk_1.default.red(`\nā No stats-cache.json found in ${(0, config_1.getDataDir)()}`));
|
|
144
|
+
console.log(chalk_1.default.dim('Make sure CLAUDE_DATA_DIR is set correctly or use --data-dir option.\n'));
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
// Clear screen and show header
|
|
148
|
+
console.clear();
|
|
149
|
+
// ASCII Art Header - Claude orange color
|
|
150
|
+
console.log(chalk_1.default.hex('#FF6B35').bold(`
|
|
151
|
+
āāāāāāāāāā āāāāāā āāā āāāāāāāāāā āāāāāāāā
|
|
152
|
+
āāāāāāāāāāā āāāāāāāāāāā āāāāāāāāāāāāāāāāāāā
|
|
153
|
+
āāā āāā āāāāāāāāāāā āāāāāā āāāāāāāāā
|
|
154
|
+
āāā āāā āāāāāāāāāāā āāāāāā āāāāāāāāā
|
|
155
|
+
āāāāāāāāāāāāāāāāāāā āāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
156
|
+
āāāāāāāāāāāāāāāāāā āāā āāāāāāā āāāāāāā āāāāāāāā
|
|
157
|
+
`));
|
|
158
|
+
console.log(chalk_1.default.hex('#FF6B35')(' Usage Visualizer') + chalk_1.default.dim(' ć» Track your AI costs\n'));
|
|
159
|
+
console.log(chalk_1.default.dim('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
160
|
+
// Show summary first
|
|
161
|
+
const hasData = await showSummary();
|
|
162
|
+
if (!hasData) {
|
|
163
|
+
console.log(chalk_1.default.red('\nā Failed to load stats cache\n'));
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
// Interactive menu loop
|
|
167
|
+
let running = true;
|
|
168
|
+
while (running) {
|
|
169
|
+
console.log(); // spacing
|
|
170
|
+
const { action } = await inquirer_1.default.prompt([
|
|
171
|
+
{
|
|
172
|
+
type: 'list',
|
|
173
|
+
name: 'action',
|
|
174
|
+
message: 'What would you like to see?',
|
|
175
|
+
choices: [
|
|
176
|
+
{ name: 'š
Last 7 Days Activity', value: 'last7days' },
|
|
177
|
+
{ name: 'š Token Usage by Model', value: 'tokens' },
|
|
178
|
+
{ name: 'š All Projects', value: 'projects' },
|
|
179
|
+
new inquirer_1.default.Separator(),
|
|
180
|
+
{ name: 'š Refresh Data', value: 'refresh' },
|
|
181
|
+
{ name: 'ā Exit', value: 'exit' },
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
]);
|
|
185
|
+
console.log(); // spacing
|
|
186
|
+
switch (action) {
|
|
187
|
+
case 'last7days':
|
|
188
|
+
await showLast7Days();
|
|
189
|
+
break;
|
|
190
|
+
case 'tokens':
|
|
191
|
+
await showTokensByModel();
|
|
192
|
+
break;
|
|
193
|
+
case 'projects':
|
|
194
|
+
await showProjects();
|
|
195
|
+
break;
|
|
196
|
+
case 'refresh':
|
|
197
|
+
console.clear();
|
|
198
|
+
// ASCII Art Header - Claude orange color
|
|
199
|
+
console.log(chalk_1.default.hex('#FF6B35').bold(`
|
|
200
|
+
āāāāāāāāāā āāāāāā āāā āāāāāāāāāā āāāāāāāā
|
|
201
|
+
āāāāāāāāāāā āāāāāāāāāāā āāāāāāāāāāāāāāāāāāā
|
|
202
|
+
āāā āāā āāāāāāāāāāā āāāāāā āāāāāāāāā
|
|
203
|
+
āāā āāā āāāāāāāāāāā āāāāāā āāāāāāāāā
|
|
204
|
+
āāāāāāāāāāāāāāāāāāā āāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
205
|
+
āāāāāāāāāāāāāāāāāā āāā āāāāāāā āāāāāāā āāāāāāāā
|
|
206
|
+
`));
|
|
207
|
+
console.log(chalk_1.default.hex('#FF6B35')(' Usage Visualizer') + chalk_1.default.dim(' ć» Track your AI costs\n'));
|
|
208
|
+
console.log(chalk_1.default.dim('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
209
|
+
await showSummary();
|
|
210
|
+
break;
|
|
211
|
+
case 'exit':
|
|
212
|
+
console.log(chalk_1.default.dim('\nHamid said Goodbye! š\n'));
|
|
213
|
+
running = false;
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.overviewCommand = overviewCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const cli_table3_1 = __importDefault(require("cli-table3"));
|
|
9
|
+
const loader_1 = require("../loader");
|
|
10
|
+
const pricing_1 = require("../pricing");
|
|
11
|
+
const config_1 = require("../config");
|
|
12
|
+
function overviewCommand() {
|
|
13
|
+
if (!(0, config_1.hasStatsCache)()) {
|
|
14
|
+
console.log(chalk_1.default.red(`ā No stats-cache.json found in ${(0, config_1.getDataDir)()}`));
|
|
15
|
+
console.log(chalk_1.default.dim('\nMake sure CLAUDE_DATA_DIR is set correctly or use --data-dir option.'));
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
const stats = (0, loader_1.loadStatsCache)();
|
|
19
|
+
if (!stats) {
|
|
20
|
+
console.log(chalk_1.default.red('ā Failed to load stats cache'));
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
// Header
|
|
24
|
+
console.log(chalk_1.default.cyan.bold('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
25
|
+
console.log(chalk_1.default.cyan.bold(' Claude Code Usage Overview'));
|
|
26
|
+
console.log(chalk_1.default.cyan.bold('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n'));
|
|
27
|
+
// Calculate total cost
|
|
28
|
+
const totalCost = Object.entries(stats.modelStats).reduce((sum, [modelId, model]) => {
|
|
29
|
+
return (sum +
|
|
30
|
+
(0, pricing_1.estimateCost)(modelId, model.inputTokens, model.outputTokens, model.cacheCreationInputTokens, model.cacheReadInputTokens));
|
|
31
|
+
}, 0);
|
|
32
|
+
// Summary stats
|
|
33
|
+
console.log(chalk_1.default.bold('Summary:'));
|
|
34
|
+
console.log(` Total Sessions: ${chalk_1.default.green(stats.totalSessions.toLocaleString('en-US'))}`);
|
|
35
|
+
console.log(` Total Messages: ${chalk_1.default.green(stats.totalMessages.toLocaleString('en-US'))}`);
|
|
36
|
+
console.log(` Total Input Tokens: ${chalk_1.default.blue((0, pricing_1.formatTokens)(stats.totalInputTokens))}`);
|
|
37
|
+
console.log(` Total Output Tokens: ${chalk_1.default.green((0, pricing_1.formatTokens)(stats.totalOutputTokens))}`);
|
|
38
|
+
console.log(` Estimated Cost: ${chalk_1.default.green.bold((0, pricing_1.formatCost)(totalCost))}`);
|
|
39
|
+
if (stats.firstSessionDate) {
|
|
40
|
+
console.log(` First Session: ${chalk_1.default.dim(stats.firstSessionDate)}`);
|
|
41
|
+
}
|
|
42
|
+
if (stats.lastComputedDate) {
|
|
43
|
+
console.log(` Last Computed: ${chalk_1.default.dim(stats.lastComputedDate)}`);
|
|
44
|
+
}
|
|
45
|
+
// Token breakdown by model
|
|
46
|
+
if (Object.keys(stats.modelStats).length > 0) {
|
|
47
|
+
console.log(chalk_1.default.bold('\nToken Usage by Model:'));
|
|
48
|
+
const table = new cli_table3_1.default({
|
|
49
|
+
head: [
|
|
50
|
+
chalk_1.default.cyan('Model'),
|
|
51
|
+
chalk_1.default.blue('Input'),
|
|
52
|
+
chalk_1.default.green('Output'),
|
|
53
|
+
chalk_1.default.yellow('Cache Write'),
|
|
54
|
+
chalk_1.default.magenta('Cache Read'),
|
|
55
|
+
chalk_1.default.green.bold('Cost'),
|
|
56
|
+
],
|
|
57
|
+
colAligns: ['left', 'right', 'right', 'right', 'right', 'right'],
|
|
58
|
+
});
|
|
59
|
+
for (const [modelId, model] of Object.entries(stats.modelStats)) {
|
|
60
|
+
const cost = (0, pricing_1.estimateCost)(modelId, model.inputTokens, model.outputTokens, model.cacheCreationInputTokens, model.cacheReadInputTokens);
|
|
61
|
+
// Simplify model name
|
|
62
|
+
let displayName = modelId.replace('claude-', '').replace(/-20\d{6}/, (match) => ` (${match.substring(1)})`);
|
|
63
|
+
table.push([
|
|
64
|
+
displayName,
|
|
65
|
+
(0, pricing_1.formatTokens)(model.inputTokens),
|
|
66
|
+
(0, pricing_1.formatTokens)(model.outputTokens),
|
|
67
|
+
(0, pricing_1.formatTokens)(model.cacheCreationInputTokens),
|
|
68
|
+
(0, pricing_1.formatTokens)(model.cacheReadInputTokens),
|
|
69
|
+
(0, pricing_1.formatCost)(cost),
|
|
70
|
+
]);
|
|
71
|
+
}
|
|
72
|
+
console.log(table.toString());
|
|
73
|
+
}
|
|
74
|
+
// Recent activity
|
|
75
|
+
if (stats.dailyActivity.length > 0) {
|
|
76
|
+
const recent = stats.dailyActivity.slice(-7); // Last 7 days
|
|
77
|
+
console.log(chalk_1.default.bold('\nRecent Activity (Last 7 Days):'));
|
|
78
|
+
const activityTable = new cli_table3_1.default({
|
|
79
|
+
head: [
|
|
80
|
+
chalk_1.default.cyan('Date'),
|
|
81
|
+
chalk_1.default.white('Sessions'),
|
|
82
|
+
chalk_1.default.white('Messages'),
|
|
83
|
+
chalk_1.default.white('Tool Calls'),
|
|
84
|
+
],
|
|
85
|
+
colAligns: ['left', 'right', 'right', 'right'],
|
|
86
|
+
});
|
|
87
|
+
for (const day of recent) {
|
|
88
|
+
activityTable.push([
|
|
89
|
+
day.date,
|
|
90
|
+
day.sessionCount.toString(),
|
|
91
|
+
day.messageCount.toString(),
|
|
92
|
+
day.toolCallCount.toString(),
|
|
93
|
+
]);
|
|
94
|
+
}
|
|
95
|
+
console.log(activityTable.toString());
|
|
96
|
+
}
|
|
97
|
+
console.log(chalk_1.default.dim('\nš” Tip: Use "claude-usage projects" to see all projects\n'));
|
|
98
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.projectCommand = projectCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const cli_table3_1 = __importDefault(require("cli-table3"));
|
|
9
|
+
const loader_1 = require("../loader");
|
|
10
|
+
const pricing_1 = require("../pricing");
|
|
11
|
+
function projectCommand(projectPath) {
|
|
12
|
+
const projectSummaries = (0, loader_1.buildProjectSummaries)();
|
|
13
|
+
// Try to find the project (handle both encoded and unencoded paths)
|
|
14
|
+
const encodedPath = encodeURIComponent(projectPath);
|
|
15
|
+
let sessions;
|
|
16
|
+
let foundPath = '';
|
|
17
|
+
if (encodedPath in projectSummaries) {
|
|
18
|
+
sessions = projectSummaries[encodedPath];
|
|
19
|
+
foundPath = encodedPath;
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
// Try to find by partial match
|
|
23
|
+
for (const [path, sess] of Object.entries(projectSummaries)) {
|
|
24
|
+
if (projectPath.includes(decodeURIComponent(path)) || decodeURIComponent(path).includes(projectPath)) {
|
|
25
|
+
sessions = sess;
|
|
26
|
+
foundPath = path;
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (!sessions) {
|
|
32
|
+
console.log(chalk_1.default.red(`ā Project not found: ${projectPath}`));
|
|
33
|
+
console.log(chalk_1.default.dim('\nAvailable projects:'));
|
|
34
|
+
for (const path of Object.keys(projectSummaries)) {
|
|
35
|
+
console.log(` - ${decodeURIComponent(path)}`);
|
|
36
|
+
}
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
// Sort sessions by timestamp descending
|
|
40
|
+
sessions = sessions.slice().sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
|
41
|
+
console.log(chalk_1.default.cyan.bold('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
42
|
+
console.log(chalk_1.default.cyan.bold(` Project: ${decodeURIComponent(foundPath)}`));
|
|
43
|
+
console.log(chalk_1.default.dim(` ${sessions.length} sessions`));
|
|
44
|
+
console.log(chalk_1.default.cyan.bold('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n'));
|
|
45
|
+
const table = new cli_table3_1.default({
|
|
46
|
+
head: [
|
|
47
|
+
chalk_1.default.cyan('Session'),
|
|
48
|
+
chalk_1.default.dim('Timestamp'),
|
|
49
|
+
chalk_1.default.white('Messages'),
|
|
50
|
+
chalk_1.default.blue('Input'),
|
|
51
|
+
chalk_1.default.green('Output'),
|
|
52
|
+
chalk_1.default.green.bold('Cost'),
|
|
53
|
+
chalk_1.default.yellow('Models'),
|
|
54
|
+
],
|
|
55
|
+
colAligns: ['left', 'left', 'right', 'right', 'right', 'right', 'left'],
|
|
56
|
+
colWidths: [30, 20, 10, 12, 12, 10, 20],
|
|
57
|
+
wordWrap: true,
|
|
58
|
+
});
|
|
59
|
+
for (const session of sessions) {
|
|
60
|
+
// Truncate slug if too long
|
|
61
|
+
const slugDisplay = session.slug.length > 60 ? session.slug.substring(0, 57) + '...' : session.slug;
|
|
62
|
+
// Format timestamp
|
|
63
|
+
const timestamp = session.timestamp ? new Date(session.timestamp).toLocaleString('en-US') : 'N/A';
|
|
64
|
+
table.push([
|
|
65
|
+
slugDisplay,
|
|
66
|
+
timestamp,
|
|
67
|
+
session.messageCount.toString(),
|
|
68
|
+
(0, pricing_1.formatTokens)(session.totalInputTokens),
|
|
69
|
+
(0, pricing_1.formatTokens)(session.totalOutputTokens),
|
|
70
|
+
(0, pricing_1.formatCost)(session.totalCostUsd),
|
|
71
|
+
Array.from(session.modelsUsed)
|
|
72
|
+
.map(m => m.replace('claude-', ''))
|
|
73
|
+
.join(', '),
|
|
74
|
+
]);
|
|
75
|
+
}
|
|
76
|
+
console.log(table.toString());
|
|
77
|
+
console.log(chalk_1.default.dim('\nš” Tip: Use "claude-usage session <session-id>" to see message details\n'));
|
|
78
|
+
}
|