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