ccusage 13.0.1 → 14.0.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/LICENSE CHANGED
@@ -2,8 +2,6 @@ MIT License
2
2
 
3
3
  Copyright (c) 2025 ryoppippi
4
4
 
5
- Logo and brand assets created by nyatinte are also licensed under MIT License.
6
-
7
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
8
6
  of this software and associated documentation files (the "Software"), to deal
9
7
  in the Software without restriction, including without limitation the rights
package/README.md CHANGED
@@ -1,16 +1,18 @@
1
1
  <div align="center">
2
- <img src="https://cdn.jsdelivr.net/gh/ryoppippi/ccusage@main/docs/logo.svg" alt="ccusage logo" width="256" height="256">
3
- <h1>ccusage</h1>
2
+ <img src="https://cdn.jsdelivr.net/gh/ryoppippi/ccusage@main/docs/logo.svg" alt="ccusage logo" width="256" height="256">
3
+ <h1>ccusage</h1>
4
4
  </div>
5
5
 
6
- [![npm version](https://img.shields.io/npm/v/ccusage?color=yellow)](https://npmjs.com/package/ccusage)
7
- [![NPM Downloads](https://img.shields.io/npm/dy/ccusage)](https://tanstack.com/stats/npm?packageGroups=%5B%7B%22packages%22:%5B%7B%22name%22:%22ccusage%22%7D%5D%7D%5D&range=30-days&transform=none&binType=daily&showDataMode=all&height=400)
8
- [![install size](https://packagephobia.com/badge?p=ccusage)](https://packagephobia.com/result?p=ccusage)
9
- [![DeepWiki](https://img.shields.io/badge/DeepWiki-ryoppippi%2Fccusage-blue.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAyCAYAAAAnWDnqAAAAAXNSR0IArs4c6QAAA05JREFUaEPtmUtyEzEQhtWTQyQLHNak2AB7ZnyXZMEjXMGeK/AIi+QuHrMnbChYY7MIh8g01fJoopFb0uhhEqqcbWTp06/uv1saEDv4O3n3dV60RfP947Mm9/SQc0ICFQgzfc4CYZoTPAswgSJCCUJUnAAoRHOAUOcATwbmVLWdGoH//PB8mnKqScAhsD0kYP3j/Yt5LPQe2KvcXmGvRHcDnpxfL2zOYJ1mFwrryWTz0advv1Ut4CJgf5uhDuDj5eUcAUoahrdY/56ebRWeraTjMt/00Sh3UDtjgHtQNHwcRGOC98BJEAEymycmYcWwOprTgcB6VZ5JK5TAJ+fXGLBm3FDAmn6oPPjR4rKCAoJCal2eAiQp2x0vxTPB3ALO2CRkwmDy5WohzBDwSEFKRwPbknEggCPB/imwrycgxX2NzoMCHhPkDwqYMr9tRcP5qNrMZHkVnOjRMWwLCcr8ohBVb1OMjxLwGCvjTikrsBOiA6fNyCrm8V1rP93iVPpwaE+gO0SsWmPiXB+jikdf6SizrT5qKasx5j8ABbHpFTx+vFXp9EnYQmLx02h1QTTrl6eDqxLnGjporxl3NL3agEvXdT0WmEost648sQOYAeJS9Q7bfUVoMGnjo4AZdUMQku50McDcMWcBPvr0SzbTAFDfvJqwLzgxwATnCgnp4wDl6Aa+Ax283gghmj+vj7feE2KBBRMW3FzOpLOADl0Isb5587h/U4gGvkt5v60Z1VLG8BhYjbzRwyQZemwAd6cCR5/XFWLYZRIMpX39AR0tjaGGiGzLVyhse5C9RKC6ai42ppWPKiBagOvaYk8lO7DajerabOZP46Lby5wKjw1HCRx7p9sVMOWGzb/vA1hwiWc6jm3MvQDTogQkiqIhJV0nBQBTU+3okKCFDy9WwferkHjtxib7t3xIUQtHxnIwtx4mpg26/HfwVNVDb4oI9RHmx5WGelRVlrtiw43zboCLaxv46AZeB3IlTkwouebTr1y2NjSpHz68WNFjHvupy3q8TFn3Hos2IAk4Ju5dCo8B3wP7VPr/FGaKiG+T+v+TQqIrOqMTL1VdWV1DdmcbO8KXBz6esmYWYKPwDL5b5FA1a0hwapHiom0r/cKaoqr+27/XcrS5UwSMbQAAAABJRU5ErkJggg==)](https://deepwiki.com/ryoppippi/ccusage) <!-- DeepWiki badge generated by https://deepwiki.ryoppippi.com/ -->
10
- [![Mentioned in Awesome Claude Code](https://awesome.re/mentioned-badge.svg)](https://github.com/hesreallyhim/awesome-claude-code)
6
+ <p align="center">
7
+ <a href="https://npmjs.com/package/ccusage"><img src="https://img.shields.io/npm/v/ccusage?color=yellow" alt="npm version" /></a>
8
+ <a href="https://tanstack.com/stats/npm?packageGroups=%5B%7B%22packages%22:%5B%7B%22name%22:%22ccusage%22%7D%5D%7D%5D&range=30-days&transform=none&binType=daily&showDataMode=all&height=400"><img src="https://img.shields.io/npm/dy/ccusage" alt="NPM Downloads" /></a>
9
+ <a href="https://packagephobia.com/result?p=ccusage"><img src="https://packagephobia.com/badge?p=ccusage" alt="install size" /></a>
10
+ <a href="https://deepwiki.com/ryoppippi/ccusage"><img src="https://img.shields.io/badge/DeepWiki-ryoppippi%2Fccusage-blue.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAyCAYAAAAnWDnqAAAAAXNSR0IArs4c6QAAA05JREFUaEPtmUtyEzEQhtWTQyQLHNak2AB7ZnyXZMEjXMGeK/AIi+QuHrMnbChYY7MIh8g01fJoopFb0uhhEqqcbWTp06/uv1saEDv4O3n3dV60RfP947Mm9/SQc0ICFQgzfc4CYZoTPAswgSJCCUJUnAAoRHOAUOcATwbmVLWdGoH//PB8mnKqScAhsD0kYP3j/Yt5LPQe2KvcXmGvRHcDnpxfL2zOYJ1mFwrryWTz0advv1Ut4CJgf5uhDuDj5eUcAUoahrdY/56ebRWeraTjMt/00Sh3UDtjgHtQNHwcRGOC98BJEAEymycmYcWwOprTgcB6VZ5JK5TAJ+fXGLBm3FDAmn6oPPjR4rKCAoJCal2eAiQp2x0vxTPB3ALO2CRkwmDy5WohzBDwSEFKRwPbknEggCPB/imwrycgxX2NzoMCHhPkDwqYMr9tRcP5qNrMZHkVnOjRMWwLCcr8ohBVb1OMjxLwGCvjTikrsBOiA6fNyCrm8V1rP93iVPpwaE+gO0SsWmPiXB+jikdf6SizrT5qKasx5j8ABbHpFTx+vFXp9EnYQmLx02h1QTTrl6eDqxLnGjporxl3NL3agEvXdT0WmEost648sQOYAeJS9Q7bfUVoMGnjo4AZdUMQku50McDcMWcBPvr0SzbTAFDfvJqwLzgxwATnCgnp4wDl6Aa+Ax283gghmj+vj7feE2KBBRMW3FzOpLOADl0Isb5587h/U4gGvkt5v60Z1VLG8BhYjbzRwyQZemwAd6cCR5/XFWLYZRIMpX39AR0tjaGGiGzLVyhse5C9RKC6ai42ppWPKiBagOvaYk8lO7DajerabOZP46Lby5wKjw1HCRx7p9sVMOWGzb/vA1hwiWc6jm3MvQDTogQkiqIhJV0nBQBTU+3okKCFDy9WwferkHjtxib7t3xIUQtHxnIwtx4mpg26/HfwVNVDb4oI9RHmx5WGelRVlrtiw43zboCLaxv46AZeB3IlTkwouebTr1y2NjSpHz68WNFjHvupy3q8TFn3Hos2IAk4Ju5dCo8B3wP7VPr/FGaKiG+T+v+TQqIrOqMTL1VdWV1DdmcbO8KXBz6esmYWYKPwDL5b5FA1a0hwapHiom0r/cKaoqr+27/XcrS5UwSMbQAAAABJRU5ErkJggg==" alt="DeepWiki" /></a>
11
+ <a href="https://github.com/hesreallyhim/awesome-claude-code"><img src="https://awesome.re/mentioned-badge.svg" alt="Mentioned in Awesome Claude Code" /></a>
12
+ </p>
11
13
 
12
14
  <div align="center">
13
- <img src="https://cdn.jsdelivr.net/gh/ryoppippi/ccusage@main/docs/screenshot.png">
15
+ <img src="https://cdn.jsdelivr.net/gh/ryoppippi/ccusage@main/docs/screenshot.png">
14
16
  </div>
15
17
 
16
18
  > **ccusage(claude-code-usage)**
@@ -22,7 +24,7 @@ Inspired by [this article](https://note.com/milliondev/n/n1d018da2d769) about tr
22
24
  ## What is `ccusage` (by NotebookLM)
23
25
 
24
26
  <details>
25
- <summary>Podcast</summary>
27
+ <summary>Podcast</summary>
26
28
 
27
29
  # English
28
30
 
@@ -50,12 +52,13 @@ This tool helps you understand the value you're getting from your subscription b
50
52
  - 📊 **Model Breakdown**: View per-model cost breakdown with `--breakdown` flag
51
53
  - 📅 **Date Filtering**: Filter reports by date range using `--since` and `--until`
52
54
  - 📁 **Custom Path**: Support for custom Claude data directory locations
53
- - 🎨 **Beautiful Output**: Colorful table-formatted display with responsive width adjustment
55
+ - 🎨 **Beautiful Output**: Colorful table-formatted display with automatic responsive layout
56
+ - 📱 **Smart Tables**: Automatic compact mode for narrow terminals (< 100 characters) with essential columns
57
+ - 📋 **Enhanced Model Display**: Model names shown as bulleted lists for better readability
54
58
  - 📄 **JSON Output**: Export data in structured JSON format with `--json`
55
59
  - 💰 **Cost Tracking**: Shows costs in USD for each day/month/session
56
60
  - 🔄 **Cache Token Support**: Tracks and displays cache creation and cache read tokens separately
57
61
  - 🌐 **Offline Mode**: Use pre-cached pricing data without network connectivity with `--offline` (Claude models only)
58
- - 📏 **Responsive Tables**: Automatic table width adjustment for narrow terminals with intelligent word wrapping
59
62
  - 🔌 **MCP Integration**: Built-in Model Context Protocol server for integration with other tools
60
63
 
61
64
  ## Important Disclaimer
@@ -121,6 +124,31 @@ bun install
121
124
  bun run start [subcommand] [options]
122
125
  ```
123
126
 
127
+ ## Configuration
128
+
129
+ ### Claude Data Directory Support
130
+
131
+ ccusage automatically detects and aggregates usage data from multiple Claude Code installation directories:
132
+
133
+ - **`~/.config/claude/projects/`** - New default location (Claude Code v1.0.30+)
134
+ - **`~/.claude/projects/`** - Legacy location (pre-v1.0.30)
135
+
136
+ > **Note**: The directory change from `~/.claude` to `~/.config/claude` in Claude Code v1.0.30 was an undocumented breaking change. ccusage handles both locations automatically to ensure compatibility across different Claude Code versions.
137
+
138
+ ### Custom Paths
139
+
140
+ You can specify custom Claude data directories using the `CLAUDE_CONFIG_DIR` environment variable:
141
+
142
+ ```bash
143
+ # Single custom path
144
+ export CLAUDE_CONFIG_DIR="/path/to/your/claude/data"
145
+
146
+ # Multiple paths (comma-separated)
147
+ export CLAUDE_CONFIG_DIR="/path/to/claude1,/path/to/claude2"
148
+ ```
149
+
150
+ When `CLAUDE_CONFIG_DIR` is set, ccusage will use those paths instead of the default locations.
151
+
124
152
  ## Usage
125
153
 
126
154
  ### Daily Report (Default)
@@ -352,7 +380,7 @@ Available MCP tools:
352
380
  #### Claude Desktop Configuration Example
353
381
 
354
382
  <div align="center">
355
- <img src="https://cdn.jsdelivr.net/gh/ryoppippi/ccusage@main/docs/mcp-claude-desktop.avif">
383
+ <img src="https://cdn.jsdelivr.net/gh/ryoppippi/ccusage@main/docs/mcp-claude-desktop.avif">
356
384
  </div>
357
385
 
358
386
  To use ccusage MCP with Claude Desktop, add this to your Claude Desktop configuration file:
@@ -408,9 +436,19 @@ The MCP Inspector provides a web-based interface to:
408
436
  - Debug server responses
409
437
  - Export server configurations
410
438
 
439
+ ## Responsive Display
440
+
441
+ ccusage automatically adapts its table layout based on your terminal width:
442
+
443
+ - **Wide terminals (≥100 characters)**: Shows all columns with bulleted model lists
444
+ - **Narrow terminals (<100 characters)**: Compact mode with essential columns only (Date, Models, Input, Output, Cost)
445
+ - **Smart formatting**: Model names displayed as clean bulleted lists (• opus-4, • sonnet-4) instead of comma-separated text
446
+
447
+ When in compact mode, ccusage displays a helpful message showing how to see the full data.
448
+
411
449
  ## Output Example
412
450
 
413
- ### Daily Report
451
+ ### Daily Report (Wide Terminal)
414
452
 
415
453
  ```
416
454
  ╭──────────────────────────────────────────╮
@@ -422,14 +460,41 @@ The MCP Inspector provides a web-based interface to:
422
460
  ┌──────────────┬──────────────────┬────────┬─────────┬──────────────┬────────────┬──────────────┬────────────┐
423
461
  │ Date │ Models │ Input │ Output │ Cache Create │ Cache Read │ Total Tokens │ Cost (USD) │
424
462
  ├──────────────┼──────────────────┼────────┼─────────┼──────────────┼────────────┼──────────────┼────────────┤
425
- │ 2025-05-30 │ opus-4, sonnet-4 │ 277 │ 31,456 │ 512 │ 1,024 │ 33,269 │ $17.58 │
426
- 2025-05-29 │ sonnet-4 959 39,662 256 768 41,645 $16.42
427
- │ 2025-05-28opus-4 15521,69312851222,488 $8.36
463
+ │ 2025-05-30 │ opus-4 │ 277 │ 31,456 │ 512 │ 1,024 │ 33,269 │ $17.58 │
464
+ sonnet-4
465
+ │ 2025-05-29• sonnet-4 95939,66225676841,645 $16.42
466
+ │ 2025-05-28 │ • opus-4 │ 155 │ 21,693 │ 128 │ 512 │ 22,488 │ $8.36 │
428
467
  ├──────────────┼──────────────────┼────────┼─────────┼──────────────┼────────────┼──────────────┼────────────┤
429
468
  │ Total │ │ 11,174 │ 720,366 │ 896 │ 2,304 │ 734,740 │ $336.47 │
430
469
  └──────────────┴──────────────────┴────────┴─────────┴──────────────┴────────────┴──────────────┴────────────┘
431
470
  ```
432
471
 
472
+ ### Daily Report (Compact Mode - Narrow Terminal)
473
+
474
+ ```
475
+ ╭──────────────────────────────────────────╮
476
+ │ │
477
+ │ Claude Code Token Usage Report - Daily │
478
+ │ │
479
+ ╰──────────────────────────────────────────╯
480
+
481
+ ┌───────────┬──────────────────┬────────┬─────────┬────────────┐
482
+ │ Date │ Models │ Input │ Output │ Cost (USD) │
483
+ ├───────────┼──────────────────┼────────┼─────────┼────────────┤
484
+ │ 2025 │ • opus-4 │ 277 │ 31,456 │ $17.58 │
485
+ │ 05-30 │ • sonnet-4 │ │ │ │
486
+ │ 2025 │ • sonnet-4 │ 959 │ 39,662 │ $16.42 │
487
+ │ 05-29 │ │ │ │ │
488
+ │ 2025 │ • opus-4 │ 155 │ 21,693 │ $8.36 │
489
+ │ 05-28 │ │ │ │ │
490
+ ├───────────┼──────────────────┼────────┼─────────┼────────────┤
491
+ │ Total │ │ 11,174 │ 720,366 │ $336.47 │
492
+ └───────────┴──────────────────┴────────┴─────────┴────────────┘
493
+
494
+ Running in Compact Mode
495
+ Expand terminal width to see cache metrics and total tokens
496
+ ```
497
+
433
498
  With `--breakdown` flag:
434
499
 
435
500
  ```
@@ -508,17 +573,17 @@ Thanks to [@milliondev](https://note.com/milliondev) for the original concept an
508
573
  ## Sponsors
509
574
 
510
575
  <p align="center">
511
- <a href="https://github.com/sponsors/ryoppippi">
512
- <img src="https://cdn.jsdelivr.net/gh/ryoppippi/sponsors/sponsors.svg">
513
- </a>
576
+ <a href="https://github.com/sponsors/ryoppippi">
577
+ <img src="https://cdn.jsdelivr.net/gh/ryoppippi/sponsors/sponsors.svg">
578
+ </a>
514
579
  </p>
515
580
 
516
581
  ## Star History
517
582
 
518
583
  <a href="https://www.star-history.com/#ryoppippi/ccusage&Date">
519
- <picture>
520
- <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=ryoppippi/ccusage&type=Date&theme=dark" />
521
- <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=ryoppippi/ccusage&type=Date" />
522
- <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=ryoppippi/ccusage&type=Date" />
523
- </picture>
584
+ <picture>
585
+ <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=ryoppippi/ccusage&type=Date&theme=dark" />
586
+ <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=ryoppippi/ccusage&type=Date" />
587
+ <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=ryoppippi/ccusage&type=Date" />
588
+ </picture>
524
589
  </a>
@@ -1,5 +1,5 @@
1
- import "./pricing-fetcher-A6FC8DhM.js";
2
- import { DailyUsage, MonthlyUsage, SessionUsage } from "./data-loader-BrxITdVN.js";
1
+ import "./pricing-fetcher-CXnYw4TA.js";
2
+ import { DailyUsage, MonthlyUsage, SessionUsage } from "./data-loader-C1n0ww95.js";
3
3
 
4
4
  //#region src/calculate-cost.d.ts
5
5
  /**
@@ -1,3 +1,3 @@
1
- import "./types.internal-CX7kpidj.js";
1
+ import "./_types-Cr2YEzKm.js";
2
2
  import { calculateTotals, createTotalsObject, getTotalTokens } from "./calculate-cost-CoS7we68.js";
3
3
  export { calculateTotals, createTotalsObject, getTotalTokens };
@@ -1,7 +1,7 @@
1
- import { CostMode, PricingFetcher, SortOrder } from "./pricing-fetcher-A6FC8DhM.js";
1
+ import { CostMode, PricingFetcher, SortOrder } from "./pricing-fetcher-CXnYw4TA.js";
2
2
  import { z } from "zod";
3
3
 
4
- //#region src/session-blocks.internal.d.ts
4
+ //#region src/_session-blocks.d.ts
5
5
 
6
6
  /**
7
7
  * Represents a single usage data entry loaded from JSONL files
@@ -47,9 +47,16 @@ type SessionBlock = {
47
47
  */
48
48
  //#endregion
49
49
  //#region src/data-loader.d.ts
50
+ /**
51
+ * Get all Claude data directories to search for usage data
52
+ * Supports multiple paths: environment variable (comma-separated), new default, and old default
53
+ * @returns Array of valid Claude data directory paths
54
+ */
55
+ declare function getClaudePaths(): string[];
50
56
  /**
51
57
  * Default path for Claude data directory
52
58
  * Uses environment variable CLAUDE_CONFIG_DIR if set, otherwise defaults to ~/.claude
59
+ * @deprecated Use getClaudePaths() instead for multiple path support
53
60
  */
54
61
  declare function getDefaultClaudePath(): string;
55
62
  /**
@@ -461,4 +468,4 @@ declare function loadMonthlyUsageData(options?: LoadOptions): Promise<MonthlyUsa
461
468
  */
462
469
  declare function loadSessionBlockData(options?: LoadOptions): Promise<SessionBlock[]>;
463
470
  //#endregion
464
- export { DailyUsage, DateFilter, LoadOptions, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData, calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema };
471
+ export { DailyUsage, DateFilter, LoadOptions, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData, calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema };
@@ -1,11 +1,16 @@
1
- import { CLAUDE_PROJECTS_DIR_NAME, DEFAULT_CLAUDE_CODE_PATH, DEFAULT_RECENT_DAYS, PricingFetcher, USAGE_DATA_GLOB_PATTERN, USER_HOME_DIR, __commonJSMin, __require, __toESM, require_usingCtx } from "./pricing-fetcher-Dc_QCa7C.js";
2
- import { activityDateSchema, arrayType, createDailyDate, createMonthlyDate, createProjectPath, createSessionId, dailyDateSchema, isoTimestampSchema, messageIdSchema, modelNameSchema, monthlyDateSchema, numberType, objectType, projectPathSchema, requestIdSchema, sessionIdSchema, versionSchema } from "./types.internal-CX7kpidj.js";
3
- import { logger } from "./logger-Pv_1kl_H.js";
1
+ import { CLAUDE_CONFIG_DIR_ENV, CLAUDE_PROJECTS_DIR_NAME, DEFAULT_CLAUDE_CODE_PATH, DEFAULT_CLAUDE_CONFIG_PATH, DEFAULT_RECENT_DAYS, PricingFetcher, USAGE_DATA_GLOB_PATTERN, USER_HOME_DIR, __commonJSMin, __require, __toESM, require_usingCtx } from "./pricing-fetcher-Dup-4216.js";
2
+ import { activityDateSchema, arrayType, createDailyDate, createMonthlyDate, createProjectPath, createSessionId, dailyDateSchema, isoTimestampSchema, messageIdSchema, modelNameSchema, monthlyDateSchema, numberType, objectType, projectPathSchema, requestIdSchema, sessionIdSchema, versionSchema } from "./_types-Cr2YEzKm.js";
3
+ import { logger } from "./logger-fRKbFGRA.js";
4
4
  import a, { readFile } from "node:fs/promises";
5
5
  import F, { homedir } from "node:os";
6
6
  import path, { posix } from "node:path";
7
7
  import process$1 from "node:process";
8
8
  import b from "node:fs";
9
+ function toArray(array) {
10
+ array = array ?? [];
11
+ return Array.isArray(array) ? array : [array];
12
+ }
13
+ const VOID = Symbol("p-void");
9
14
  /**
10
15
  * Return `true` if the type of `x` is `string`.
11
16
  *
@@ -3233,20 +3238,67 @@ function filterRecentBlocks(blocks, days = DEFAULT_RECENT_DAYS) {
3233
3238
  }
3234
3239
  var import_usingCtx = __toESM(require_usingCtx(), 1);
3235
3240
  /**
3241
+ * Get all Claude data directories to search for usage data
3242
+ * Supports multiple paths: environment variable (comma-separated), new default, and old default
3243
+ * @returns Array of valid Claude data directory paths
3244
+ */
3245
+ function getClaudePaths() {
3246
+ const paths = [];
3247
+ const normalizedPaths = /* @__PURE__ */ new Set();
3248
+ const envPaths = (process$1.env[CLAUDE_CONFIG_DIR_ENV] ?? "").trim();
3249
+ if (envPaths !== "") {
3250
+ const envPathList = envPaths.split(",").map((p) => p.trim()).filter((p) => p !== "");
3251
+ for (const envPath of envPathList) {
3252
+ const normalizedPath = path.resolve(envPath);
3253
+ if (isDirectorySync(normalizedPath)) {
3254
+ const projectsPath = path.join(normalizedPath, CLAUDE_PROJECTS_DIR_NAME);
3255
+ if (isDirectorySync(projectsPath)) {
3256
+ if (!normalizedPaths.has(normalizedPath)) {
3257
+ normalizedPaths.add(normalizedPath);
3258
+ paths.push(normalizedPath);
3259
+ }
3260
+ }
3261
+ }
3262
+ }
3263
+ }
3264
+ const defaultPaths = [path.join(USER_HOME_DIR, DEFAULT_CLAUDE_CONFIG_PATH), path.join(USER_HOME_DIR, DEFAULT_CLAUDE_CODE_PATH)];
3265
+ for (const defaultPath of defaultPaths) {
3266
+ const normalizedPath = path.resolve(defaultPath);
3267
+ if (isDirectorySync(normalizedPath)) {
3268
+ const projectsPath = path.join(normalizedPath, CLAUDE_PROJECTS_DIR_NAME);
3269
+ if (isDirectorySync(projectsPath)) {
3270
+ if (!normalizedPaths.has(normalizedPath)) {
3271
+ normalizedPaths.add(normalizedPath);
3272
+ paths.push(normalizedPath);
3273
+ }
3274
+ }
3275
+ }
3276
+ }
3277
+ if (paths.length === 0) throw new Error(`No valid Claude data directories found. Please ensure at least one of the following exists:
3278
+ - ${path.join(USER_HOME_DIR, DEFAULT_CLAUDE_CONFIG_PATH, CLAUDE_PROJECTS_DIR_NAME)}
3279
+ - ${path.join(USER_HOME_DIR, DEFAULT_CLAUDE_CODE_PATH, CLAUDE_PROJECTS_DIR_NAME)}
3280
+ - Or set ${CLAUDE_CONFIG_DIR_ENV} environment variable to valid directory path(s) containing a '${CLAUDE_PROJECTS_DIR_NAME}' subdirectory`.trim());
3281
+ return paths;
3282
+ }
3283
+ /**
3236
3284
  * Default path for Claude data directory
3237
3285
  * Uses environment variable CLAUDE_CONFIG_DIR if set, otherwise defaults to ~/.claude
3286
+ * @deprecated Use getClaudePaths() instead for multiple path support
3238
3287
  */
3239
3288
  function getDefaultClaudePath() {
3240
- const envClaudeCodePath = (process$1.env.CLAUDE_CONFIG_DIR ?? "").trim();
3241
- if (envClaudeCodePath === "") return path.join(USER_HOME_DIR, DEFAULT_CLAUDE_CODE_PATH);
3242
- if (!isDirectorySync(envClaudeCodePath)) throw new Error(`CLAUDE_CONFIG_DIR path is not a valid directory: ${envClaudeCodePath}.
3243
- Please set CLAUDE_CONFIG_DIR to a valid directory path, or ensure ${path.join(USER_HOME_DIR, DEFAULT_CLAUDE_CODE_PATH)} exists.
3244
- `.trim());
3245
- const claudeCodeProjectsPath = path.join(envClaudeCodePath, CLAUDE_PROJECTS_DIR_NAME);
3246
- if (!isDirectorySync(claudeCodeProjectsPath)) throw new Error(`Claude data directory does not exist: ${claudeCodeProjectsPath}.
3247
- Please set CLAUDE_CONFIG_DIR to a valid path, or ensure ${path.join(USER_HOME_DIR, DEFAULT_CLAUDE_CODE_PATH)} exists.
3248
- `.trim());
3249
- return envClaudeCodePath;
3289
+ const envPath = (process$1.env[CLAUDE_CONFIG_DIR_ENV] ?? "").trim();
3290
+ if (envPath !== "") {
3291
+ const firstPath = envPath.split(",")[0]?.trim();
3292
+ if (firstPath != null && firstPath !== "") {
3293
+ if (!isDirectorySync(firstPath)) throw new Error(`CLAUDE_CONFIG_DIR path is not a valid directory: ${firstPath}`);
3294
+ return firstPath;
3295
+ }
3296
+ }
3297
+ const newDefaultPath = path.join(USER_HOME_DIR, DEFAULT_CLAUDE_CONFIG_PATH);
3298
+ if (isDirectorySync(newDefaultPath)) return newDefaultPath;
3299
+ const oldDefaultPath = path.join(USER_HOME_DIR, DEFAULT_CLAUDE_CODE_PATH);
3300
+ if (isDirectorySync(oldDefaultPath)) return oldDefaultPath;
3301
+ return oldDefaultPath;
3250
3302
  }
3251
3303
  /**
3252
3304
  * Zod schema for validating Claude usage data from JSONL files
@@ -3559,14 +3611,18 @@ async function calculateCostForEntry(data, mode, fetcher) {
3559
3611
  async function loadDailyUsageData(options) {
3560
3612
  try {
3561
3613
  var _usingCtx = (0, import_usingCtx.default)();
3562
- const claudePath = options?.claudePath ?? getDefaultClaudePath();
3563
- const claudeDir = path.join(claudePath, CLAUDE_PROJECTS_DIR_NAME);
3564
- const files = await glob([USAGE_DATA_GLOB_PATTERN], {
3565
- cwd: claudeDir,
3566
- absolute: true
3567
- });
3568
- if (files.length === 0) return [];
3569
- const sortedFiles = await sortFilesByTimestamp(files);
3614
+ const claudePaths = toArray(options?.claudePath ?? getClaudePaths());
3615
+ const allFiles = [];
3616
+ for (const claudePath of claudePaths) {
3617
+ const claudeDir = path.join(claudePath, CLAUDE_PROJECTS_DIR_NAME);
3618
+ const files = await glob([USAGE_DATA_GLOB_PATTERN], {
3619
+ cwd: claudeDir,
3620
+ absolute: true
3621
+ });
3622
+ allFiles.push(...files);
3623
+ }
3624
+ if (allFiles.length === 0) return [];
3625
+ const sortedFiles = await sortFilesByTimestamp(allFiles);
3570
3626
  const mode = options?.mode ?? "auto";
3571
3627
  const fetcher = _usingCtx.u(mode === "display" ? null : new PricingFetcher(options?.offline));
3572
3628
  const processedHashes = /* @__PURE__ */ new Set();
@@ -3623,20 +3679,31 @@ async function loadDailyUsageData(options) {
3623
3679
  async function loadSessionData(options) {
3624
3680
  try {
3625
3681
  var _usingCtx3 = (0, import_usingCtx.default)();
3626
- const claudePath = options?.claudePath ?? getDefaultClaudePath();
3627
- const claudeDir = path.join(claudePath, CLAUDE_PROJECTS_DIR_NAME);
3628
- const files = await glob([USAGE_DATA_GLOB_PATTERN], {
3629
- cwd: claudeDir,
3630
- absolute: true
3631
- });
3632
- if (files.length === 0) return [];
3633
- const sortedFiles = await sortFilesByTimestamp(files);
3682
+ const claudePaths = toArray(options?.claudePath ?? getClaudePaths());
3683
+ const filesWithBase = [];
3684
+ for (const claudePath of claudePaths) {
3685
+ const claudeDir = path.join(claudePath, CLAUDE_PROJECTS_DIR_NAME);
3686
+ const files = await glob([USAGE_DATA_GLOB_PATTERN], {
3687
+ cwd: claudeDir,
3688
+ absolute: true
3689
+ });
3690
+ for (const file of files) filesWithBase.push({
3691
+ file,
3692
+ baseDir: claudeDir
3693
+ });
3694
+ }
3695
+ if (filesWithBase.length === 0) return [];
3696
+ const fileToBaseMap = new Map(filesWithBase.map((f$1) => [f$1.file, f$1.baseDir]));
3697
+ const sortedFilesWithBase = await sortFilesByTimestamp(filesWithBase.map((f$1) => f$1.file)).then((sortedFiles) => sortedFiles.map((file) => ({
3698
+ file,
3699
+ baseDir: fileToBaseMap.get(file) ?? ""
3700
+ })));
3634
3701
  const mode = options?.mode ?? "auto";
3635
3702
  const fetcher = _usingCtx3.u(mode === "display" ? null : new PricingFetcher(options?.offline));
3636
3703
  const processedHashes = /* @__PURE__ */ new Set();
3637
3704
  const allEntries = [];
3638
- for (const file of sortedFiles) {
3639
- const relativePath = path.relative(claudeDir, file);
3705
+ for (const { file, baseDir } of sortedFilesWithBase) {
3706
+ const relativePath = path.relative(baseDir, file);
3640
3707
  const parts = relativePath.split(path.sep);
3641
3708
  const sessionId = parts[parts.length - 2] ?? "unknown";
3642
3709
  const joinedPath = parts.slice(0, -2).join(path.sep);
@@ -3744,14 +3811,18 @@ async function loadMonthlyUsageData(options) {
3744
3811
  async function loadSessionBlockData(options) {
3745
3812
  try {
3746
3813
  var _usingCtx4 = (0, import_usingCtx.default)();
3747
- const claudePath = options?.claudePath ?? getDefaultClaudePath();
3748
- const claudeDir = path.join(claudePath, CLAUDE_PROJECTS_DIR_NAME);
3749
- const files = await glob([USAGE_DATA_GLOB_PATTERN], {
3750
- cwd: claudeDir,
3751
- absolute: true
3752
- });
3753
- if (files.length === 0) return [];
3754
- const sortedFiles = await sortFilesByTimestamp(files);
3814
+ const claudePaths = toArray(options?.claudePath ?? getClaudePaths());
3815
+ const allFiles = [];
3816
+ for (const claudePath of claudePaths) {
3817
+ const claudeDir = path.join(claudePath, CLAUDE_PROJECTS_DIR_NAME);
3818
+ const files = await glob([USAGE_DATA_GLOB_PATTERN], {
3819
+ cwd: claudeDir,
3820
+ absolute: true
3821
+ });
3822
+ allFiles.push(...files);
3823
+ }
3824
+ if (allFiles.length === 0) return [];
3825
+ const sortedFiles = await sortFilesByTimestamp(allFiles);
3755
3826
  const mode = options?.mode ?? "auto";
3756
3827
  const fetcher = _usingCtx4.u(mode === "display" ? null : new PricingFetcher());
3757
3828
  const processedHashes = /* @__PURE__ */ new Set();
@@ -3798,4 +3869,4 @@ async function loadSessionBlockData(options) {
3798
3869
  _usingCtx4.d();
3799
3870
  }
3800
3871
  }
3801
- export { DEFAULT_SESSION_DURATION_HOURS, calculateBurnRate, calculateCostForEntry, createUniqueHash, dailyUsageSchema, filterRecentBlocks, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, glob, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, projectBlockUsage, sessionUsageSchema, sortFilesByTimestamp, uniq, usageDataSchema };
3872
+ export { DEFAULT_SESSION_DURATION_HOURS, calculateBurnRate, calculateCostForEntry, createUniqueHash, dailyUsageSchema, filterRecentBlocks, formatDate, formatDateCompact, getClaudePaths, getDefaultClaudePath, getEarliestTimestamp, glob, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, projectBlockUsage, sessionUsageSchema, sortFilesByTimestamp, uniq, usageDataSchema };
@@ -1,3 +1,3 @@
1
- import "./pricing-fetcher-A6FC8DhM.js";
2
- import { DailyUsage, DateFilter, LoadOptions, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData, calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema } from "./data-loader-BrxITdVN.js";
3
- export { DailyUsage, DateFilter, LoadOptions, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData, calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema };
1
+ import "./pricing-fetcher-CXnYw4TA.js";
2
+ import { DailyUsage, DateFilter, LoadOptions, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData, calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema } from "./data-loader-C1n0ww95.js";
3
+ export { DailyUsage, DateFilter, LoadOptions, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData, calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema };
@@ -1,5 +1,5 @@
1
- import "./pricing-fetcher-Dc_QCa7C.js";
2
- import "./types.internal-CX7kpidj.js";
3
- import { calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema } from "./data-loader-D0kGuKxU.js";
4
- import "./logger-Pv_1kl_H.js";
5
- export { calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema };
1
+ import "./pricing-fetcher-Dup-4216.js";
2
+ import "./_types-Cr2YEzKm.js";
3
+ import { calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema } from "./data-loader-C8KM6jhg.js";
4
+ import "./logger-fRKbFGRA.js";
5
+ export { calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema };
@@ -1,6 +1,6 @@
1
- import { CLAUDE_PROJECTS_DIR_NAME, DEBUG_MATCH_THRESHOLD_PERCENT, DEFAULT_CLAUDE_CODE_PATH, PricingFetcher, USAGE_DATA_GLOB_PATTERN, USER_HOME_DIR, __toESM, require_usingCtx } from "./pricing-fetcher-Dc_QCa7C.js";
2
- import { glob, usageDataSchema } from "./data-loader-D0kGuKxU.js";
3
- import { logger } from "./logger-Pv_1kl_H.js";
1
+ import { CLAUDE_PROJECTS_DIR_NAME, DEBUG_MATCH_THRESHOLD_PERCENT, DEFAULT_CLAUDE_CODE_PATH, PricingFetcher, USAGE_DATA_GLOB_PATTERN, USER_HOME_DIR, __toESM, require_usingCtx } from "./pricing-fetcher-Dup-4216.js";
2
+ import { glob, usageDataSchema } from "./data-loader-C8KM6jhg.js";
3
+ import { logger } from "./logger-fRKbFGRA.js";
4
4
  import { readFile } from "node:fs/promises";
5
5
  import path from "node:path";
6
6
  var import_usingCtx = __toESM(require_usingCtx(), 1);
package/dist/debug.js CHANGED
@@ -1,6 +1,6 @@
1
- import "./pricing-fetcher-Dc_QCa7C.js";
2
- import "./types.internal-CX7kpidj.js";
3
- import "./data-loader-D0kGuKxU.js";
4
- import "./logger-Pv_1kl_H.js";
5
- import { detectMismatches, printMismatchReport } from "./debug-DBezzOxe.js";
1
+ import "./pricing-fetcher-Dup-4216.js";
2
+ import "./_types-Cr2YEzKm.js";
3
+ import "./data-loader-C8KM6jhg.js";
4
+ import "./logger-fRKbFGRA.js";
5
+ import { detectMismatches, printMismatchReport } from "./debug-BJaJWLMi.js";
6
6
  export { detectMismatches, printMismatchReport };
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import { BLOCKS_COMPACT_WIDTH_THRESHOLD, BLOCKS_DEFAULT_TERMINAL_WIDTH, BLOCKS_WARNING_THRESHOLD, DEFAULT_RECENT_DAYS, MCP_DEFAULT_PORT, __commonJSMin, __require, __toESM } from "./pricing-fetcher-Dc_QCa7C.js";
3
- import { CostModes, SortOrders, dateSchema } from "./types.internal-CX7kpidj.js";
2
+ import { BLOCKS_COMPACT_WIDTH_THRESHOLD, BLOCKS_DEFAULT_TERMINAL_WIDTH, BLOCKS_WARNING_THRESHOLD, DEFAULT_RECENT_DAYS, MCP_DEFAULT_PORT, __commonJSMin, __require, __toESM } from "./pricing-fetcher-Dup-4216.js";
3
+ import { CostModes, SortOrders, dateSchema } from "./_types-Cr2YEzKm.js";
4
4
  import { calculateTotals, createTotalsObject, getTotalTokens } from "./calculate-cost-CoS7we68.js";
5
- import { DEFAULT_SESSION_DURATION_HOURS, calculateBurnRate, filterRecentBlocks, formatDateCompact, getDefaultClaudePath, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, projectBlockUsage, uniq } from "./data-loader-D0kGuKxU.js";
6
- import { description, log, logger, name, version } from "./logger-Pv_1kl_H.js";
7
- import { detectMismatches, printMismatchReport } from "./debug-DBezzOxe.js";
8
- import { createMcpHttpApp, createMcpServer, startMcpServerStdio } from "./mcp-lSzQickg.js";
5
+ import { DEFAULT_SESSION_DURATION_HOURS, calculateBurnRate, filterRecentBlocks, formatDateCompact, getDefaultClaudePath, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, projectBlockUsage, uniq } from "./data-loader-C8KM6jhg.js";
6
+ import { description, log, logger, name, version } from "./logger-fRKbFGRA.js";
7
+ import { detectMismatches, printMismatchReport } from "./debug-BJaJWLMi.js";
8
+ import { createMcpHttpApp, createMcpServer, startMcpServerStdio } from "./mcp-Beq_0A1e.js";
9
9
  import process$1 from "node:process";
10
10
  import { createServer } from "node:http";
11
11
  import { Http2ServerRequest } from "node:http2";
@@ -2940,6 +2940,10 @@ var ResponsiveTable = class {
2940
2940
  colAligns;
2941
2941
  style;
2942
2942
  dateFormatter;
2943
+ compactHead;
2944
+ compactColAligns;
2945
+ compactThreshold;
2946
+ compactMode = false;
2943
2947
  /**
2944
2948
  * Creates a new responsive table instance
2945
2949
  * @param options - Table configuration options
@@ -2949,6 +2953,9 @@ var ResponsiveTable = class {
2949
2953
  this.colAligns = options.colAligns ?? Array.from({ length: this.head.length }, () => "left");
2950
2954
  this.style = options.style;
2951
2955
  this.dateFormatter = options.dateFormatter;
2956
+ this.compactHead = options.compactHead;
2957
+ this.compactColAligns = options.compactColAligns;
2958
+ this.compactThreshold = options.compactThreshold ?? 100;
2952
2959
  }
2953
2960
  /**
2954
2961
  * Adds a row to the table
@@ -2958,26 +2965,75 @@ var ResponsiveTable = class {
2958
2965
  this.rows.push(row);
2959
2966
  }
2960
2967
  /**
2968
+ * Filters a row to compact mode columns
2969
+ * @param row - Row to filter
2970
+ * @param compactIndices - Indices of columns to keep in compact mode
2971
+ * @returns Filtered row
2972
+ */
2973
+ filterRowToCompact(row, compactIndices) {
2974
+ return compactIndices.map((index) => row[index] ?? "");
2975
+ }
2976
+ /**
2977
+ * Gets the current table head and col aligns based on compact mode
2978
+ * @returns Current head and colAligns arrays
2979
+ */
2980
+ getCurrentTableConfig() {
2981
+ if (this.compactMode && this.compactHead != null && this.compactColAligns != null) return {
2982
+ head: this.compactHead,
2983
+ colAligns: this.compactColAligns
2984
+ };
2985
+ return {
2986
+ head: this.head,
2987
+ colAligns: this.colAligns
2988
+ };
2989
+ }
2990
+ /**
2991
+ * Gets indices mapping from full table to compact table
2992
+ * @returns Array of column indices to keep in compact mode
2993
+ */
2994
+ getCompactIndices() {
2995
+ if (this.compactHead == null || !this.compactMode) return Array.from({ length: this.head.length }, (_, i) => i);
2996
+ return this.compactHead.map((compactHeader) => {
2997
+ const index = this.head.indexOf(compactHeader);
2998
+ if (index < 0) {
2999
+ console.warn(`Warning: Compact header "${compactHeader}" not found in table headers [${this.head.join(", ")}]. Using first column as fallback.`);
3000
+ return 0;
3001
+ }
3002
+ return index;
3003
+ });
3004
+ }
3005
+ /**
3006
+ * Returns whether the table is currently in compact mode
3007
+ * @returns True if compact mode is active
3008
+ */
3009
+ isCompactMode() {
3010
+ return this.compactMode;
3011
+ }
3012
+ /**
2961
3013
  * Renders the table as a formatted string
2962
3014
  * Automatically adjusts layout based on terminal width
2963
3015
  * @returns Formatted table string
2964
3016
  */
2965
3017
  toString() {
2966
- const terminalWidth = process$1.stdout.columns || 120;
3018
+ const terminalWidth = Number.parseInt(process$1.env.COLUMNS ?? "", 10) || process$1.stdout.columns || 120;
3019
+ this.compactMode = terminalWidth < this.compactThreshold && this.compactHead != null;
3020
+ const { head, colAligns } = this.getCurrentTableConfig();
3021
+ const compactIndices = this.getCompactIndices();
2967
3022
  const dataRows = this.rows.filter((row) => !this.isSeparatorRow(row));
2968
- const allRows = [this.head.map(String), ...dataRows.map((row) => row.map((cell) => {
3023
+ const processedDataRows = this.compactMode ? dataRows.map((row) => this.filterRowToCompact(row, compactIndices)) : dataRows;
3024
+ const allRows = [head.map(String), ...processedDataRows.map((row) => row.map((cell) => {
2969
3025
  if (typeof cell === "object" && cell != null && "content" in cell) return String(cell.content);
2970
3026
  return String(cell ?? "");
2971
3027
  }))];
2972
- const contentWidths = this.head.map((_, colIndex) => {
3028
+ const contentWidths = head.map((_, colIndex) => {
2973
3029
  const maxLength = Math.max(...allRows.map((row) => stringWidth(String(row[colIndex] ?? ""))));
2974
3030
  return maxLength;
2975
3031
  });
2976
- const numColumns = this.head.length;
3032
+ const numColumns = head.length;
2977
3033
  const tableOverhead = 3 * numColumns + 1;
2978
3034
  const availableWidth = terminalWidth - tableOverhead;
2979
3035
  const columnWidths = contentWidths.map((width, index) => {
2980
- const align = this.colAligns[index];
3036
+ const align = colAligns[index];
2981
3037
  if (align === "right") return Math.max(width + 3, 11);
2982
3038
  else if (index === 1) return Math.max(width + 2, 15);
2983
3039
  return Math.max(width + 2, 10);
@@ -2986,7 +3042,7 @@ var ResponsiveTable = class {
2986
3042
  if (totalRequiredWidth > terminalWidth) {
2987
3043
  const scaleFactor = availableWidth / columnWidths.reduce((sum, width) => sum + width, 0);
2988
3044
  const adjustedWidths = columnWidths.map((width, index) => {
2989
- const align = this.colAligns[index];
3045
+ const align = colAligns[index];
2990
3046
  let adjustedWidth = Math.floor(width * scaleFactor);
2991
3047
  if (align === "right") adjustedWidth = Math.max(adjustedWidth, 10);
2992
3048
  else if (index === 0) adjustedWidth = Math.max(adjustedWidth, 10);
@@ -2995,33 +3051,37 @@ var ResponsiveTable = class {
2995
3051
  return adjustedWidth;
2996
3052
  });
2997
3053
  const table = new import_cli_table3.default({
2998
- head: this.head,
3054
+ head,
2999
3055
  style: this.style,
3000
- colAligns: this.colAligns,
3056
+ colAligns,
3001
3057
  colWidths: adjustedWidths,
3002
3058
  wordWrap: true,
3003
3059
  wrapOnWordBoundary: true
3004
3060
  });
3005
3061
  for (const row of this.rows) if (this.isSeparatorRow(row)) continue;
3006
3062
  else {
3007
- const processedRow = row.map((cell, index) => {
3063
+ let processedRow = row.map((cell, index) => {
3008
3064
  if (index === 0 && this.dateFormatter != null && typeof cell === "string" && this.isDateString(cell)) return this.dateFormatter(cell);
3009
3065
  return cell;
3010
3066
  });
3067
+ if (this.compactMode) processedRow = this.filterRowToCompact(processedRow, compactIndices);
3011
3068
  table.push(processedRow);
3012
3069
  }
3013
3070
  return table.toString();
3014
3071
  } else {
3015
3072
  const table = new import_cli_table3.default({
3016
- head: this.head,
3073
+ head,
3017
3074
  style: this.style,
3018
- colAligns: this.colAligns,
3075
+ colAligns,
3019
3076
  colWidths: columnWidths,
3020
3077
  wordWrap: true,
3021
3078
  wrapOnWordBoundary: true
3022
3079
  });
3023
3080
  for (const row of this.rows) if (this.isSeparatorRow(row)) continue;
3024
- else table.push(row);
3081
+ else {
3082
+ const processedRow = this.compactMode ? this.filterRowToCompact(row, compactIndices) : row;
3083
+ table.push(processedRow);
3084
+ }
3025
3085
  return table.toString();
3026
3086
  }
3027
3087
  }
@@ -3073,14 +3133,14 @@ function formatModelName(modelName) {
3073
3133
  return modelName;
3074
3134
  }
3075
3135
  /**
3076
- * Formats an array of model names for display as a comma-separated string
3136
+ * Formats an array of model names for display with each model on a new line
3077
3137
  * Removes duplicates and sorts alphabetically
3078
3138
  * @param models - Array of model names
3079
- * @returns Formatted string with unique, sorted model names separated by commas
3139
+ * @returns Formatted string with unique, sorted model names as a bulleted list
3080
3140
  */
3081
- function formatModelsDisplay(models) {
3141
+ function formatModelsDisplayMultiline(models) {
3082
3142
  const uniqueModels = uniq(models.map(formatModelName));
3083
- return uniqueModels.sort().join(", ");
3143
+ return uniqueModels.sort().map((model) => `- ${model}`).join("\n");
3084
3144
  }
3085
3145
  /**
3086
3146
  * Pushes model breakdown rows to a table
@@ -3143,12 +3203,11 @@ function formatBlockTime(block, compact = false) {
3143
3203
  /**
3144
3204
  * Formats the list of models used in a block for display
3145
3205
  * @param models - Array of model names
3146
- * @param compact - Whether to use compact formatting (unused currently)
3147
3206
  * @returns Formatted model names string
3148
3207
  */
3149
- function formatModels(models, compact = false) {
3208
+ function formatModels(models) {
3150
3209
  if (models.length === 0) return "-";
3151
- return compact ? formatModelsDisplay(models) : formatModelsDisplay(models);
3210
+ return formatModelsDisplayMultiline(models);
3152
3211
  }
3153
3212
  /**
3154
3213
  * Parses token limit argument, supporting 'max' keyword
@@ -3347,7 +3406,7 @@ const blocksCommand = define({
3347
3406
  const row = [
3348
3407
  formatBlockTime(block, useCompactFormat),
3349
3408
  status,
3350
- formatModels(block.models, useCompactFormat),
3409
+ formatModels(block.models),
3351
3410
  formatNumber(totalTokens)
3352
3411
  ];
3353
3412
  if (actualTokenLimit != null && actualTokenLimit > 0) {
@@ -3469,12 +3528,27 @@ const dailyCommand = define({
3469
3528
  "right",
3470
3529
  "right"
3471
3530
  ],
3472
- dateFormatter: formatDateCompact
3531
+ dateFormatter: formatDateCompact,
3532
+ compactHead: [
3533
+ "Date",
3534
+ "Models",
3535
+ "Input",
3536
+ "Output",
3537
+ "Cost (USD)"
3538
+ ],
3539
+ compactColAligns: [
3540
+ "left",
3541
+ "left",
3542
+ "right",
3543
+ "right",
3544
+ "right"
3545
+ ],
3546
+ compactThreshold: 100
3473
3547
  });
3474
3548
  for (const data of dailyData) {
3475
3549
  table.push([
3476
3550
  data.date,
3477
- formatModelsDisplay(data.modelsUsed),
3551
+ formatModelsDisplayMultiline(data.modelsUsed),
3478
3552
  formatNumber(data.inputTokens),
3479
3553
  formatNumber(data.outputTokens),
3480
3554
  formatNumber(data.cacheCreationTokens),
@@ -3505,6 +3579,10 @@ const dailyCommand = define({
3505
3579
  import_picocolors$2.default.yellow(formatCurrency(totals.totalCost))
3506
3580
  ]);
3507
3581
  log(table.toString());
3582
+ if (table.isCompactMode()) {
3583
+ logger.info("\nRunning in Compact Mode");
3584
+ logger.info("Expand terminal width to see cache metrics and total tokens");
3585
+ }
3508
3586
  }
3509
3587
  }
3510
3588
  });
@@ -3992,12 +4070,27 @@ const monthlyCommand = define({
3992
4070
  "right",
3993
4071
  "right"
3994
4072
  ],
3995
- dateFormatter: formatDateCompact
4073
+ dateFormatter: formatDateCompact,
4074
+ compactHead: [
4075
+ "Month",
4076
+ "Models",
4077
+ "Input",
4078
+ "Output",
4079
+ "Cost (USD)"
4080
+ ],
4081
+ compactColAligns: [
4082
+ "left",
4083
+ "left",
4084
+ "right",
4085
+ "right",
4086
+ "right"
4087
+ ],
4088
+ compactThreshold: 100
3996
4089
  });
3997
4090
  for (const data of monthlyData) {
3998
4091
  table.push([
3999
4092
  data.month,
4000
- formatModelsDisplay(data.modelsUsed),
4093
+ formatModelsDisplayMultiline(data.modelsUsed),
4001
4094
  formatNumber(data.inputTokens),
4002
4095
  formatNumber(data.outputTokens),
4003
4096
  formatNumber(data.cacheCreationTokens),
@@ -4028,6 +4121,10 @@ const monthlyCommand = define({
4028
4121
  import_picocolors$1.default.yellow(formatCurrency(totals.totalCost))
4029
4122
  ]);
4030
4123
  log(table.toString());
4124
+ if (table.isCompactMode()) {
4125
+ logger.info("\nRunning in Compact Mode");
4126
+ logger.info("Expand terminal width to see cache metrics and total tokens");
4127
+ }
4031
4128
  }
4032
4129
  }
4033
4130
  });
@@ -4099,7 +4196,24 @@ const sessionCommand = define({
4099
4196
  "right",
4100
4197
  "left"
4101
4198
  ],
4102
- dateFormatter: formatDateCompact
4199
+ dateFormatter: formatDateCompact,
4200
+ compactHead: [
4201
+ "Session",
4202
+ "Models",
4203
+ "Input",
4204
+ "Output",
4205
+ "Cost (USD)",
4206
+ "Last Activity"
4207
+ ],
4208
+ compactColAligns: [
4209
+ "left",
4210
+ "left",
4211
+ "right",
4212
+ "right",
4213
+ "right",
4214
+ "left"
4215
+ ],
4216
+ compactThreshold: 100
4103
4217
  });
4104
4218
  let maxSessionLength = 0;
4105
4219
  for (const data of sessionData) {
@@ -4107,7 +4221,7 @@ const sessionCommand = define({
4107
4221
  maxSessionLength = Math.max(maxSessionLength, sessionDisplay.length);
4108
4222
  table.push([
4109
4223
  sessionDisplay,
4110
- formatModelsDisplay(data.modelsUsed),
4224
+ formatModelsDisplayMultiline(data.modelsUsed),
4111
4225
  formatNumber(data.inputTokens),
4112
4226
  formatNumber(data.outputTokens),
4113
4227
  formatNumber(data.cacheCreationTokens),
@@ -4141,6 +4255,10 @@ const sessionCommand = define({
4141
4255
  ""
4142
4256
  ]);
4143
4257
  log(table.toString());
4258
+ if (table.isCompactMode()) {
4259
+ logger.info("\nRunning in Compact Mode");
4260
+ logger.info("Expand terminal width to see cache metrics and total tokens");
4261
+ }
4144
4262
  }
4145
4263
  }
4146
4264
  });
@@ -951,7 +951,7 @@ function _getDefaultLogLevel() {
951
951
  }
952
952
  const consola = createConsola$1();
953
953
  var name = "ccusage";
954
- var version = "13.0.1";
954
+ var version = "14.0.0";
955
955
  var description = "Usage analysis tool for Claude Code";
956
956
  /**
957
957
  * Application logger instance with package name tag
package/dist/logger.js CHANGED
@@ -1,2 +1,2 @@
1
- import { log, logger } from "./logger-Pv_1kl_H.js";
1
+ import { log, logger } from "./logger-fRKbFGRA.js";
2
2
  export { log, logger };
@@ -1,7 +1,7 @@
1
- import { __commonJSMin, __toESM, require_usingCtx } from "./pricing-fetcher-Dc_QCa7C.js";
2
- import { ZodFirstPartyTypeKind, ZodOptional, ZodType, arrayType, booleanType, dateSchema, discriminatedUnionType, enumType, literalType, numberType, objectType, optionalType, recordType, stringType, unionType, unknownType } from "./types.internal-CX7kpidj.js";
3
- import { getDefaultClaudePath, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData } from "./data-loader-D0kGuKxU.js";
4
- import { name, version } from "./logger-Pv_1kl_H.js";
1
+ import { __commonJSMin, __toESM, require_usingCtx } from "./pricing-fetcher-Dup-4216.js";
2
+ import { ZodFirstPartyTypeKind, ZodOptional, ZodType, arrayType, booleanType, dateSchema, discriminatedUnionType, enumType, literalType, numberType, objectType, optionalType, recordType, stringType, unionType, unknownType } from "./_types-Cr2YEzKm.js";
3
+ import { getDefaultClaudePath, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData } from "./data-loader-C8KM6jhg.js";
4
+ import { name, version } from "./logger-fRKbFGRA.js";
5
5
  import process from "node:process";
6
6
  const LATEST_PROTOCOL_VERSION = "2025-03-26";
7
7
  const SUPPORTED_PROTOCOL_VERSIONS = [
package/dist/mcp.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import "./pricing-fetcher-A6FC8DhM.js";
2
- import { LoadOptions } from "./data-loader-BrxITdVN.js";
1
+ import "./pricing-fetcher-CXnYw4TA.js";
2
+ import { LoadOptions } from "./data-loader-C1n0ww95.js";
3
3
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
4
  import { Hono } from "hono/tiny";
5
5
 
package/dist/mcp.js CHANGED
@@ -1,6 +1,6 @@
1
- import "./pricing-fetcher-Dc_QCa7C.js";
2
- import "./types.internal-CX7kpidj.js";
3
- import "./data-loader-D0kGuKxU.js";
4
- import "./logger-Pv_1kl_H.js";
5
- import { createMcpHttpApp, createMcpServer, startMcpServerStdio } from "./mcp-lSzQickg.js";
1
+ import "./pricing-fetcher-Dup-4216.js";
2
+ import "./_types-Cr2YEzKm.js";
3
+ import "./data-loader-C8KM6jhg.js";
4
+ import "./logger-fRKbFGRA.js";
5
+ import { createMcpHttpApp, createMcpServer, startMcpServerStdio } from "./mcp-Beq_0A1e.js";
6
6
  export { createMcpHttpApp, createMcpServer, startMcpServerStdio };
@@ -73,7 +73,7 @@ type NumberBool = typeof numberBool[number];
73
73
  */
74
74
  type TupleToUnion<ArrayType> = ArrayType extends readonly unknown[] ? ArrayType[number] : never;
75
75
  //#endregion
76
- //#region src/types.internal.d.ts
76
+ //#region src/_types.d.ts
77
77
 
78
78
  /**
79
79
  * Available cost calculation modes
@@ -1,5 +1,5 @@
1
- import { modelPricingSchema } from "./types.internal-CX7kpidj.js";
2
- import { logger } from "./logger-Pv_1kl_H.js";
1
+ import { modelPricingSchema } from "./_types-Cr2YEzKm.js";
2
+ import { logger } from "./logger-fRKbFGRA.js";
3
3
  import { createRequire } from "node:module";
4
4
  import { homedir } from "node:os";
5
5
  var __create = Object.create;
@@ -59,6 +59,16 @@ const DEBUG_MATCH_THRESHOLD_PERCENT = .1;
59
59
  */
60
60
  const DEFAULT_CLAUDE_CODE_PATH = ".claude";
61
61
  /**
62
+ * Additional default Claude data directory path (~/.config/claude)
63
+ * Used as secondary path for loading usage data from JSONL files
64
+ */
65
+ const DEFAULT_CLAUDE_CONFIG_PATH = ".config/claude";
66
+ /**
67
+ * Environment variable for specifying multiple Claude data directories
68
+ * Supports comma-separated paths for multiple locations
69
+ */
70
+ const CLAUDE_CONFIG_DIR_ENV = "CLAUDE_CONFIG_DIR";
71
+ /**
62
72
  * Claude projects directory name within the data directory
63
73
  * Contains subdirectories for each project with usage data
64
74
  */
@@ -362,4 +372,4 @@ var PricingFetcher = class {
362
372
  return cost;
363
373
  }
364
374
  };
365
- export { BLOCKS_COMPACT_WIDTH_THRESHOLD, BLOCKS_DEFAULT_TERMINAL_WIDTH, BLOCKS_WARNING_THRESHOLD, CLAUDE_PROJECTS_DIR_NAME, DEBUG_MATCH_THRESHOLD_PERCENT, DEFAULT_CLAUDE_CODE_PATH, DEFAULT_RECENT_DAYS, MCP_DEFAULT_PORT, PricingFetcher, USAGE_DATA_GLOB_PATTERN, USER_HOME_DIR, __commonJSMin, __require, __toESM, require_usingCtx };
375
+ export { BLOCKS_COMPACT_WIDTH_THRESHOLD, BLOCKS_DEFAULT_TERMINAL_WIDTH, BLOCKS_WARNING_THRESHOLD, CLAUDE_CONFIG_DIR_ENV, CLAUDE_PROJECTS_DIR_NAME, DEBUG_MATCH_THRESHOLD_PERCENT, DEFAULT_CLAUDE_CODE_PATH, DEFAULT_CLAUDE_CONFIG_PATH, DEFAULT_RECENT_DAYS, MCP_DEFAULT_PORT, PricingFetcher, USAGE_DATA_GLOB_PATTERN, USER_HOME_DIR, __commonJSMin, __require, __toESM, require_usingCtx };
@@ -1,2 +1,2 @@
1
- import { PricingFetcher } from "./pricing-fetcher-A6FC8DhM.js";
1
+ import { PricingFetcher } from "./pricing-fetcher-CXnYw4TA.js";
2
2
  export { PricingFetcher };
@@ -1,4 +1,4 @@
1
- import { PricingFetcher } from "./pricing-fetcher-Dc_QCa7C.js";
2
- import "./types.internal-CX7kpidj.js";
3
- import "./logger-Pv_1kl_H.js";
1
+ import { PricingFetcher } from "./pricing-fetcher-Dup-4216.js";
2
+ import "./_types-Cr2YEzKm.js";
3
+ import "./logger-fRKbFGRA.js";
4
4
  export { PricingFetcher };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ccusage",
3
3
  "type": "module",
4
- "version": "13.0.1",
4
+ "version": "14.0.0",
5
5
  "description": "Usage analysis tool for Claude Code",
6
6
  "author": "ryoppippi",
7
7
  "license": "MIT",