ccusage 9.0.0 → 10.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/README.md CHANGED
@@ -45,10 +45,12 @@ This tool helps you understand the value you're getting from your subscription b
45
45
  - 📊 **Model Breakdown**: View per-model cost breakdown with `--breakdown` flag
46
46
  - 📅 **Date Filtering**: Filter reports by date range using `--since` and `--until`
47
47
  - 📁 **Custom Path**: Support for custom Claude data directory locations
48
- - 🎨 **Beautiful Output**: Colorful table-formatted display
48
+ - 🎨 **Beautiful Output**: Colorful table-formatted display with responsive width adjustment
49
49
  - 📄 **JSON Output**: Export data in structured JSON format with `--json`
50
50
  - 💰 **Cost Tracking**: Shows costs in USD for each day/month/session
51
51
  - 🔄 **Cache Token Support**: Tracks and displays cache creation and cache read tokens separately
52
+ - 🌐 **Offline Mode**: Use pre-cached pricing data without network connectivity with `--offline` (Claude models only)
53
+ - 📏 **Responsive Tables**: Automatic table width adjustment for narrow terminals with intelligent word wrapping
52
54
 
53
55
  ## Important Disclaimer
54
56
 
@@ -147,6 +149,10 @@ ccusage daily --order desc # Show newest dates first (default)
147
149
 
148
150
  # Show per-model cost breakdown
149
151
  ccusage daily --breakdown # Show cost breakdown by model (opus-4, sonnet-4, etc.)
152
+
153
+ # Use offline mode (no network required)
154
+ ccusage daily --offline # Use pre-cached pricing data
155
+ ccusage daily -O # Short alias for --offline
150
156
  ```
151
157
 
152
158
  `ccusage` is an alias for `ccusage daily`, so you can run it without specifying the subcommand.
@@ -183,6 +189,10 @@ ccusage monthly --order desc # Show newest months first (default)
183
189
 
184
190
  # Show per-model cost breakdown
185
191
  ccusage monthly --breakdown # Show cost breakdown by model
192
+
193
+ # Use offline mode (no network required)
194
+ ccusage monthly --offline # Use pre-cached pricing data
195
+ ccusage monthly -O # Short alias for --offline
186
196
  ```
187
197
 
188
198
  ### Session Report
@@ -214,8 +224,61 @@ ccusage session --order desc # Show newest sessions first (default)
214
224
 
215
225
  # Show per-model cost breakdown
216
226
  ccusage session --breakdown # Show cost breakdown by model
227
+
228
+ # Use offline mode (no network required)
229
+ ccusage session --offline # Use pre-cached pricing data
230
+ ccusage session -O # Short alias for --offline
217
231
  ```
218
232
 
233
+ ### 5-Hour Blocks Report
234
+
235
+ Shows usage grouped by Claude's 5-hour billing windows:
236
+
237
+ ```bash
238
+ # Show all 5-hour blocks
239
+ ccusage blocks
240
+
241
+ # Show only the active block with detailed projections
242
+ ccusage blocks --active
243
+
244
+ # Show blocks from the last 3 days (including active)
245
+ ccusage blocks --recent
246
+
247
+ # Set a token limit to see if you'll exceed it
248
+ ccusage blocks -t 500000
249
+
250
+ # Use the highest previous block as the token limit
251
+ ccusage blocks -t max
252
+
253
+ # Combine options
254
+ ccusage blocks --recent -t max
255
+
256
+ # Output in JSON format
257
+ ccusage blocks --json
258
+
259
+ # Control cost calculation mode
260
+ ccusage blocks --mode auto # Use costUSD when available, calculate otherwise (default)
261
+ ccusage blocks --mode calculate # Always calculate costs from tokens
262
+ ccusage blocks --mode display # Always show pre-calculated costUSD values
263
+
264
+ # Control sort order
265
+ ccusage blocks --order asc # Show oldest blocks first
266
+ ccusage blocks --order desc # Show newest blocks first (default)
267
+ ```
268
+
269
+ The blocks report helps you understand Claude Code's 5-hour rolling session windows:
270
+
271
+ - Sessions start with your first message and last for 5 hours
272
+ - Shows active blocks with time remaining and burn rate projections
273
+ - Helps track if you're approaching token limits within a session
274
+ - The `-t max` option automatically uses your highest previous block as the limit
275
+
276
+ #### Blocks-specific options:
277
+
278
+ - `-t, --token-limit <number|max>`: Set token limit for quota warnings (use "max" for highest previous block)
279
+ - `-a, --active`: Show only active block with detailed projections
280
+ - `-r, --recent`: Show blocks from last 3 days (including active)
281
+
219
282
  ### Options
220
283
 
221
284
  All commands support the following options:
@@ -226,6 +289,7 @@ All commands support the following options:
226
289
  - `-m, --mode <mode>`: Cost calculation mode: `auto` (default), `calculate`, or `display`
227
290
  - `-o, --order <order>`: Sort order: `desc` (newest first, default) or `asc` (oldest first).
228
291
  - `-b, --breakdown`: Show per-model cost breakdown (splits usage by Opus, Sonnet, etc.)
292
+ - `-O, --offline`: Use pre-cached pricing data for Claude models (no network connection required)
229
293
  - `-d, --debug`: Show pricing mismatch information for debugging
230
294
  - `--debug-samples <number>`: Number of sample discrepancies to show in debug output (default: 5)
231
295
  - `-h, --help`: Display help message
@@ -1,5 +1,5 @@
1
1
  import "./pricing-fetcher-BkSZh4lR.js";
2
- import { DailyUsage, MonthlyUsage, SessionUsage } from "./data-loader-OGaMjZTD.js";
2
+ import { DailyUsage, MonthlyUsage, SessionUsage } from "./data-loader-DPQaq8_n.js";
3
3
 
4
4
  //#region src/calculate-cost.d.ts
5
5
  type TokenData = {
@@ -1,7 +1,7 @@
1
1
  import { __commonJS, __require, __toESM } from "./chunk-BLXvPPr8.js";
2
2
  import { array, number, object, optional, pipe, regex, safeParse, string } from "./dist-DCvt9hEv.js";
3
- import { logger } from "./logger-DKw-DPXD.js";
4
- import { PricingFetcher } from "./pricing-fetcher-BlxDpqFj.js";
3
+ import { logger } from "./logger-C2GZVy1v.js";
4
+ import { PricingFetcher } from "./pricing-fetcher-BxlbW4km.js";
5
5
  import fsPromises, { readFile } from "node:fs/promises";
6
6
  import { homedir } from "node:os";
7
7
  import path from "node:path";
@@ -3041,6 +3041,144 @@ async function glob(patternsOrOptions, options) {
3041
3041
  return crawl(opts, cwd, false);
3042
3042
  }
3043
3043
 
3044
+ //#endregion
3045
+ //#region src/five-hour-blocks.internal.ts
3046
+ const FIVE_HOURS_MS = 5 * 60 * 60 * 1e3;
3047
+ const DEFAULT_RECENT_DAYS = 3;
3048
+ function identifyFiveHourBlocks(entries) {
3049
+ if (entries.length === 0) return [];
3050
+ const blocks = [];
3051
+ const sortedEntries = [...entries].sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
3052
+ let currentBlockStart = null;
3053
+ let currentBlockEntries = [];
3054
+ const now = /* @__PURE__ */ new Date();
3055
+ for (const entry of sortedEntries) {
3056
+ const entryTime = entry.timestamp;
3057
+ if (currentBlockStart == null) {
3058
+ currentBlockStart = entryTime;
3059
+ currentBlockEntries = [entry];
3060
+ } else {
3061
+ const timeSinceBlockStart = entryTime.getTime() - currentBlockStart.getTime();
3062
+ const lastEntry = currentBlockEntries.at(-1);
3063
+ if (lastEntry == null) continue;
3064
+ const lastEntryTime = lastEntry.timestamp;
3065
+ const timeSinceLastEntry = entryTime.getTime() - lastEntryTime.getTime();
3066
+ if (timeSinceBlockStart > FIVE_HOURS_MS || timeSinceLastEntry > FIVE_HOURS_MS) {
3067
+ const block = createBlock(currentBlockStart, currentBlockEntries, now);
3068
+ blocks.push(block);
3069
+ if (timeSinceLastEntry > FIVE_HOURS_MS) {
3070
+ const gapBlock = createGapBlock(lastEntryTime, entryTime);
3071
+ if (gapBlock != null) blocks.push(gapBlock);
3072
+ }
3073
+ currentBlockStart = entryTime;
3074
+ currentBlockEntries = [entry];
3075
+ } else currentBlockEntries.push(entry);
3076
+ }
3077
+ }
3078
+ if (currentBlockStart != null && currentBlockEntries.length > 0) {
3079
+ const block = createBlock(currentBlockStart, currentBlockEntries, now);
3080
+ blocks.push(block);
3081
+ }
3082
+ return blocks;
3083
+ }
3084
+ function createBlock(startTime, entries, now) {
3085
+ const endTime = new Date(startTime.getTime() + FIVE_HOURS_MS);
3086
+ const lastEntry = entries[entries.length - 1];
3087
+ const actualEndTime = lastEntry != null ? lastEntry.timestamp : startTime;
3088
+ const isActive = now.getTime() - actualEndTime.getTime() < FIVE_HOURS_MS && now < endTime;
3089
+ const tokenCounts = {
3090
+ inputTokens: 0,
3091
+ outputTokens: 0,
3092
+ cacheCreationInputTokens: 0,
3093
+ cacheReadInputTokens: 0
3094
+ };
3095
+ let costUSD = 0;
3096
+ const modelsSet = /* @__PURE__ */ new Set();
3097
+ for (const entry of entries) {
3098
+ tokenCounts.inputTokens += entry.usage.inputTokens;
3099
+ tokenCounts.outputTokens += entry.usage.outputTokens;
3100
+ tokenCounts.cacheCreationInputTokens += entry.usage.cacheCreationInputTokens;
3101
+ tokenCounts.cacheReadInputTokens += entry.usage.cacheReadInputTokens;
3102
+ costUSD += entry.costUSD != null ? entry.costUSD : 0;
3103
+ modelsSet.add(entry.model);
3104
+ }
3105
+ return {
3106
+ id: startTime.toISOString(),
3107
+ startTime,
3108
+ endTime,
3109
+ actualEndTime,
3110
+ isActive,
3111
+ entries,
3112
+ tokenCounts,
3113
+ costUSD,
3114
+ models: Array.from(modelsSet)
3115
+ };
3116
+ }
3117
+ function createGapBlock(lastActivityTime, nextActivityTime) {
3118
+ const gapDuration = nextActivityTime.getTime() - lastActivityTime.getTime();
3119
+ if (gapDuration <= FIVE_HOURS_MS) return null;
3120
+ const gapStart = new Date(lastActivityTime.getTime() + FIVE_HOURS_MS);
3121
+ const gapEnd = nextActivityTime;
3122
+ return {
3123
+ id: `gap-${gapStart.toISOString()}`,
3124
+ startTime: gapStart,
3125
+ endTime: gapEnd,
3126
+ isActive: false,
3127
+ isGap: true,
3128
+ entries: [],
3129
+ tokenCounts: {
3130
+ inputTokens: 0,
3131
+ outputTokens: 0,
3132
+ cacheCreationInputTokens: 0,
3133
+ cacheReadInputTokens: 0
3134
+ },
3135
+ costUSD: 0,
3136
+ models: []
3137
+ };
3138
+ }
3139
+ function calculateBurnRate(block) {
3140
+ if (block.entries.length === 0 || (block.isGap ?? false)) return null;
3141
+ const firstEntryData = block.entries[0];
3142
+ const lastEntryData = block.entries[block.entries.length - 1];
3143
+ if (firstEntryData == null || lastEntryData == null) return null;
3144
+ const firstEntry = firstEntryData.timestamp;
3145
+ const lastEntry = lastEntryData.timestamp;
3146
+ const durationMinutes = (lastEntry.getTime() - firstEntry.getTime()) / (1e3 * 60);
3147
+ if (durationMinutes <= 0) return null;
3148
+ const totalTokens = block.tokenCounts.inputTokens + block.tokenCounts.outputTokens;
3149
+ const tokensPerMinute = totalTokens / durationMinutes;
3150
+ const costPerHour = block.costUSD / durationMinutes * 60;
3151
+ return {
3152
+ tokensPerMinute,
3153
+ costPerHour
3154
+ };
3155
+ }
3156
+ function projectBlockUsage(block) {
3157
+ if (!block.isActive || (block.isGap ?? false)) return null;
3158
+ const burnRate = calculateBurnRate(block);
3159
+ if (burnRate == null) return null;
3160
+ const now = /* @__PURE__ */ new Date();
3161
+ const remainingTime = block.endTime.getTime() - now.getTime();
3162
+ const remainingMinutes = Math.max(0, remainingTime / (1e3 * 60));
3163
+ const currentTokens = block.tokenCounts.inputTokens + block.tokenCounts.outputTokens;
3164
+ const projectedAdditionalTokens = burnRate.tokensPerMinute * remainingMinutes;
3165
+ const totalTokens = currentTokens + projectedAdditionalTokens;
3166
+ const projectedAdditionalCost = burnRate.costPerHour / 60 * remainingMinutes;
3167
+ const totalCost = block.costUSD + projectedAdditionalCost;
3168
+ return {
3169
+ totalTokens: Math.round(totalTokens),
3170
+ totalCost: Math.round(totalCost * 100) / 100,
3171
+ remainingMinutes: Math.round(remainingMinutes)
3172
+ };
3173
+ }
3174
+ function filterRecentBlocks(blocks, days = DEFAULT_RECENT_DAYS) {
3175
+ const now = /* @__PURE__ */ new Date();
3176
+ const cutoffTime = new Date(now.getTime() - days * 24 * 60 * 60 * 1e3);
3177
+ return blocks.filter((block) => {
3178
+ return block.startTime >= cutoffTime || block.isActive;
3179
+ });
3180
+ }
3181
+
3044
3182
  //#endregion
3045
3183
  //#region node_modules/rolldown/node_modules/@oxc-project/runtime/src/helpers/usingCtx.js
3046
3184
  var require_usingCtx = __commonJS({ "node_modules/rolldown/node_modules/@oxc-project/runtime/src/helpers/usingCtx.js"(exports, module) {
@@ -3553,6 +3691,63 @@ async function loadMonthlyUsageData(options) {
3553
3691
  }
3554
3692
  return sortByDate(monthlyArray, (item) => `${item.month}-01`, options?.order);
3555
3693
  }
3694
+ async function loadFiveHourBlockData(options) {
3695
+ try {
3696
+ var _usingCtx4 = (0, import_usingCtx.default)();
3697
+ const claudePath = options?.claudePath ?? getDefaultClaudePath();
3698
+ const claudeDir = path.join(claudePath, "projects");
3699
+ const files = await glob(["**/*.jsonl"], {
3700
+ cwd: claudeDir,
3701
+ absolute: true
3702
+ });
3703
+ if (files.length === 0) return [];
3704
+ const sortedFiles = await sortFilesByTimestamp(files);
3705
+ const mode = options?.mode ?? "auto";
3706
+ const fetcher = _usingCtx4.u(mode === "display" ? null : new PricingFetcher());
3707
+ const processedHashes = /* @__PURE__ */ new Set();
3708
+ const allEntries = [];
3709
+ for (const file of sortedFiles) {
3710
+ const content = await readFile(file, "utf-8");
3711
+ const lines = content.trim().split("\n").filter((line) => line.length > 0);
3712
+ for (const line of lines) try {
3713
+ const parsed = JSON.parse(line);
3714
+ const result = safeParse(UsageDataSchema, parsed);
3715
+ if (!result.success) continue;
3716
+ const data = result.output;
3717
+ const uniqueHash = createUniqueHash(data);
3718
+ if (isDuplicateEntry(uniqueHash, processedHashes)) continue;
3719
+ markAsProcessed(uniqueHash, processedHashes);
3720
+ const cost = fetcher != null ? await calculateCostForEntry(data, mode, fetcher) : data.costUSD ?? 0;
3721
+ allEntries.push({
3722
+ timestamp: new Date(data.timestamp),
3723
+ usage: {
3724
+ inputTokens: data.message.usage.input_tokens,
3725
+ outputTokens: data.message.usage.output_tokens,
3726
+ cacheCreationInputTokens: data.message.usage.cache_creation_input_tokens ?? 0,
3727
+ cacheReadInputTokens: data.message.usage.cache_read_input_tokens ?? 0
3728
+ },
3729
+ costUSD: cost,
3730
+ model: data.message.model ?? "unknown",
3731
+ version: data.version
3732
+ });
3733
+ } catch (error) {
3734
+ logger.debug(`Skipping invalid JSON line in 5-hour blocks: ${error instanceof Error ? error.message : String(error)}`);
3735
+ }
3736
+ }
3737
+ const blocks = identifyFiveHourBlocks(allEntries);
3738
+ const filtered = options?.since != null && options.since !== "" || options?.until != null && options.until !== "" ? blocks.filter((block) => {
3739
+ const blockDateStr = formatDate(block.startTime.toISOString()).replace(/-/g, "");
3740
+ if (options.since != null && options.since !== "" && blockDateStr < options.since) return false;
3741
+ if (options.until != null && options.until !== "" && blockDateStr > options.until) return false;
3742
+ return true;
3743
+ }) : blocks;
3744
+ return sortByDate(filtered, (block) => block.startTime, options?.order);
3745
+ } catch (_) {
3746
+ _usingCtx4.e = _;
3747
+ } finally {
3748
+ _usingCtx4.d();
3749
+ }
3750
+ }
3556
3751
 
3557
3752
  //#endregion
3558
- export { DailyUsageSchema, ModelBreakdownSchema, MonthlyUsageSchema, SessionUsageSchema, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, glob, loadDailyUsageData, loadMonthlyUsageData, loadSessionData, require_usingCtx, sortFilesByTimestamp };
3753
+ export { DailyUsageSchema, ModelBreakdownSchema, MonthlyUsageSchema, SessionUsageSchema, UsageDataSchema, calculateBurnRate, calculateCostForEntry, createUniqueHash, filterRecentBlocks, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, glob, loadDailyUsageData, loadFiveHourBlockData, loadMonthlyUsageData, loadSessionData, projectBlockUsage, require_usingCtx, sortFilesByTimestamp };
@@ -1,7 +1,38 @@
1
1
  import { ArraySchema, CostMode, InferOutput, NumberSchema, ObjectSchema, OptionalSchema, PricingFetcher, RegexAction, SchemaWithPipe, SortOrder, StringSchema } from "./pricing-fetcher-BkSZh4lR.js";
2
2
 
3
+ //#region src/five-hour-blocks.internal.d.ts
4
+ type LoadedUsageEntry = {
5
+ timestamp: Date;
6
+ usage: {
7
+ inputTokens: number;
8
+ outputTokens: number;
9
+ cacheCreationInputTokens: number;
10
+ cacheReadInputTokens: number;
11
+ };
12
+ costUSD: number | null;
13
+ model: string;
14
+ version?: string;
15
+ };
16
+ type TokenCounts = {
17
+ inputTokens: number;
18
+ outputTokens: number;
19
+ cacheCreationInputTokens: number;
20
+ cacheReadInputTokens: number;
21
+ };
22
+ type FiveHourBlock = {
23
+ id: string;
24
+ startTime: Date;
25
+ endTime: Date;
26
+ actualEndTime?: Date;
27
+ isActive: boolean;
28
+ isGap?: boolean;
29
+ entries: LoadedUsageEntry[];
30
+ tokenCounts: TokenCounts;
31
+ costUSD: number;
32
+ models: string[];
33
+ };
34
+ //#endregion
3
35
  //#region src/data-loader.d.ts
4
-
5
36
  /**
6
37
  * Default path for Claude data directory
7
38
  * Uses environment variable CLAUDE_CONFIG_DIR if set, otherwise defaults to ~/.claude
@@ -120,5 +151,6 @@ type LoadOptions = {
120
151
  declare function loadDailyUsageData(options?: LoadOptions): Promise<DailyUsage[]>;
121
152
  declare function loadSessionData(options?: LoadOptions): Promise<SessionUsage[]>;
122
153
  declare function loadMonthlyUsageData(options?: LoadOptions): Promise<MonthlyUsage[]>;
154
+ declare function loadFiveHourBlockData(options?: LoadOptions): Promise<FiveHourBlock[]>;
123
155
  //#endregion
124
- export { DailyUsage, DailyUsageSchema, DateFilter, LoadOptions, ModelBreakdown, ModelBreakdownSchema, MonthlyUsage, MonthlyUsageSchema, SessionUsage, SessionUsageSchema, UsageData, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionData, sortFilesByTimestamp };
156
+ export { DailyUsage, DailyUsageSchema, DateFilter, LoadOptions, ModelBreakdown, ModelBreakdownSchema, MonthlyUsage, MonthlyUsageSchema, SessionUsage, SessionUsageSchema, UsageData, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadFiveHourBlockData, loadMonthlyUsageData, loadSessionData, sortFilesByTimestamp };
@@ -1,3 +1,3 @@
1
1
  import "./pricing-fetcher-BkSZh4lR.js";
2
- import { DailyUsage, DailyUsageSchema, DateFilter, LoadOptions, ModelBreakdown, ModelBreakdownSchema, MonthlyUsage, MonthlyUsageSchema, SessionUsage, SessionUsageSchema, UsageData, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionData, sortFilesByTimestamp } from "./data-loader-OGaMjZTD.js";
3
- export { DailyUsage, DailyUsageSchema, DateFilter, LoadOptions, ModelBreakdown, ModelBreakdownSchema, MonthlyUsage, MonthlyUsageSchema, SessionUsage, SessionUsageSchema, UsageData, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionData, sortFilesByTimestamp };
2
+ import { DailyUsage, DailyUsageSchema, DateFilter, LoadOptions, ModelBreakdown, ModelBreakdownSchema, MonthlyUsage, MonthlyUsageSchema, SessionUsage, SessionUsageSchema, UsageData, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadFiveHourBlockData, loadMonthlyUsageData, loadSessionData, sortFilesByTimestamp } from "./data-loader-DPQaq8_n.js";
3
+ export { DailyUsage, DailyUsageSchema, DateFilter, LoadOptions, ModelBreakdown, ModelBreakdownSchema, MonthlyUsage, MonthlyUsageSchema, SessionUsage, SessionUsageSchema, UsageData, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadFiveHourBlockData, loadMonthlyUsageData, loadSessionData, sortFilesByTimestamp };
@@ -1,6 +1,6 @@
1
- import { DailyUsageSchema, ModelBreakdownSchema, MonthlyUsageSchema, SessionUsageSchema, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionData, sortFilesByTimestamp } from "./data-loader-Ca8k_uX0.js";
1
+ import { DailyUsageSchema, ModelBreakdownSchema, MonthlyUsageSchema, SessionUsageSchema, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadFiveHourBlockData, loadMonthlyUsageData, loadSessionData, sortFilesByTimestamp } from "./data-loader-Bp9KV0_a.js";
2
2
  import "./dist-DCvt9hEv.js";
3
- import "./logger-DKw-DPXD.js";
4
- import "./pricing-fetcher-BlxDpqFj.js";
3
+ import "./logger-C2GZVy1v.js";
4
+ import "./pricing-fetcher-BxlbW4km.js";
5
5
 
6
- export { DailyUsageSchema, ModelBreakdownSchema, MonthlyUsageSchema, SessionUsageSchema, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionData, sortFilesByTimestamp };
6
+ export { DailyUsageSchema, ModelBreakdownSchema, MonthlyUsageSchema, SessionUsageSchema, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadFiveHourBlockData, loadMonthlyUsageData, loadSessionData, sortFilesByTimestamp };
@@ -1,8 +1,8 @@
1
1
  import { __toESM } from "./chunk-BLXvPPr8.js";
2
- import { UsageDataSchema, glob, require_usingCtx } from "./data-loader-Ca8k_uX0.js";
2
+ import { UsageDataSchema, glob, require_usingCtx } from "./data-loader-Bp9KV0_a.js";
3
3
  import { safeParse } from "./dist-DCvt9hEv.js";
4
- import { logger } from "./logger-DKw-DPXD.js";
5
- import { PricingFetcher } from "./pricing-fetcher-BlxDpqFj.js";
4
+ import { logger } from "./logger-C2GZVy1v.js";
5
+ import { PricingFetcher } from "./pricing-fetcher-BxlbW4km.js";
6
6
  import { readFile } from "node:fs/promises";
7
7
  import { homedir } from "node:os";
8
8
  import path from "node:path";
package/dist/debug.js CHANGED
@@ -1,7 +1,7 @@
1
- import "./data-loader-Ca8k_uX0.js";
1
+ import "./data-loader-Bp9KV0_a.js";
2
2
  import "./dist-DCvt9hEv.js";
3
- import "./logger-DKw-DPXD.js";
4
- import "./pricing-fetcher-BlxDpqFj.js";
5
- import { detectMismatches, printMismatchReport } from "./debug-zCcXwR8p.js";
3
+ import "./logger-C2GZVy1v.js";
4
+ import "./pricing-fetcher-BxlbW4km.js";
5
+ import { detectMismatches, printMismatchReport } from "./debug-bjc38_Gx.js";
6
6
 
7
7
  export { detectMismatches, printMismatchReport };
package/dist/index.js CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import { __commonJS, __toESM } from "./chunk-BLXvPPr8.js";
3
3
  import { calculateTotals, createTotalsObject, getTotalTokens } from "./calculate-cost-2IwHSzmi.js";
4
- import { formatDateCompact, getDefaultClaudePath, loadDailyUsageData, loadMonthlyUsageData, loadSessionData } from "./data-loader-Ca8k_uX0.js";
4
+ import { calculateBurnRate, filterRecentBlocks, formatDateCompact, getDefaultClaudePath, loadDailyUsageData, loadFiveHourBlockData, loadMonthlyUsageData, loadSessionData, projectBlockUsage } from "./data-loader-Bp9KV0_a.js";
5
5
  import { safeParse } from "./dist-DCvt9hEv.js";
6
- import { description, log, logger, name, version } from "./logger-DKw-DPXD.js";
7
- import { CostModes, SortOrders, dateSchema } from "./pricing-fetcher-BlxDpqFj.js";
8
- import { detectMismatches, printMismatchReport } from "./debug-zCcXwR8p.js";
9
- import { ResponsiveTable } from "./utils.table-DRzF8vZJ.js";
6
+ import { description, log, logger, name, version } from "./logger-C2GZVy1v.js";
7
+ import { CostModes, SortOrders, dateSchema } from "./pricing-fetcher-BxlbW4km.js";
8
+ import { detectMismatches, printMismatchReport } from "./debug-bjc38_Gx.js";
9
+ import { ResponsiveTable } from "./utils.table-USks3NGv.js";
10
10
  import "./types-BlyCnKwN.js";
11
- import { createMcpServer } from "./mcp-BRFYI5rd.js";
11
+ import { createMcpServer } from "./mcp-DVjQeqCy.js";
12
12
  import "./index-CISmcbXk-CW1Gj6Ab.js";
13
13
  import process$1 from "node:process";
14
14
 
@@ -1273,7 +1273,7 @@ const sharedArgs = {
1273
1273
  type: "boolean",
1274
1274
  negatable: true,
1275
1275
  short: "O",
1276
- description: "Use cached data instead of fetching from API",
1276
+ description: "Use cached pricing data for Claude models instead of fetching from API",
1277
1277
  default: false
1278
1278
  }
1279
1279
  };
@@ -1284,7 +1284,7 @@ const sharedCommandConfig = {
1284
1284
 
1285
1285
  //#endregion
1286
1286
  //#region src/utils.internal.ts
1287
- var import_picocolors$3 = __toESM(require_picocolors(), 1);
1287
+ var import_picocolors$4 = __toESM(require_picocolors(), 1);
1288
1288
  function formatNumber(num) {
1289
1289
  return num.toLocaleString("en-US");
1290
1290
  }
@@ -1313,12 +1313,294 @@ function pushBreakdownRows(table, breakdowns, extraColumns = 1, trailingColumns
1313
1313
  const row = [` └─ ${formatModelName(breakdown.modelName)}`];
1314
1314
  for (let i = 0; i < extraColumns; i++) row.push("");
1315
1315
  const totalTokens = breakdown.inputTokens + breakdown.outputTokens + breakdown.cacheCreationTokens + breakdown.cacheReadTokens;
1316
- row.push(import_picocolors$3.default.gray(formatNumber(breakdown.inputTokens)), import_picocolors$3.default.gray(formatNumber(breakdown.outputTokens)), import_picocolors$3.default.gray(formatNumber(breakdown.cacheCreationTokens)), import_picocolors$3.default.gray(formatNumber(breakdown.cacheReadTokens)), import_picocolors$3.default.gray(formatNumber(totalTokens)), import_picocolors$3.default.gray(formatCurrency(breakdown.cost)));
1316
+ row.push(import_picocolors$4.default.gray(formatNumber(breakdown.inputTokens)), import_picocolors$4.default.gray(formatNumber(breakdown.outputTokens)), import_picocolors$4.default.gray(formatNumber(breakdown.cacheCreationTokens)), import_picocolors$4.default.gray(formatNumber(breakdown.cacheReadTokens)), import_picocolors$4.default.gray(formatNumber(totalTokens)), import_picocolors$4.default.gray(formatCurrency(breakdown.cost)));
1317
1317
  for (let i = 0; i < trailingColumns; i++) row.push("");
1318
1318
  table.push(row);
1319
1319
  }
1320
1320
  }
1321
1321
 
1322
+ //#endregion
1323
+ //#region src/commands/blocks.ts
1324
+ var import_picocolors$3 = __toESM(require_picocolors(), 1);
1325
+ const RECENT_DAYS_DEFAULT = 3;
1326
+ const WARNING_THRESHOLD = .8;
1327
+ const COMPACT_WIDTH_THRESHOLD = 120;
1328
+ const DEFAULT_TERMINAL_WIDTH = 120;
1329
+ function formatBlockTime(block, compact = false) {
1330
+ const start = compact ? block.startTime.toLocaleString(void 0, {
1331
+ month: "2-digit",
1332
+ day: "2-digit",
1333
+ hour: "2-digit",
1334
+ minute: "2-digit"
1335
+ }) : block.startTime.toLocaleString();
1336
+ if (block.isGap ?? false) {
1337
+ const end = compact ? block.endTime.toLocaleString(void 0, {
1338
+ hour: "2-digit",
1339
+ minute: "2-digit"
1340
+ }) : block.endTime.toLocaleString();
1341
+ const duration$1 = Math.round((block.endTime.getTime() - block.startTime.getTime()) / (1e3 * 60 * 60));
1342
+ return compact ? `${start}-${end}\n(${duration$1}h gap)` : `${start} - ${end} (${duration$1}h gap)`;
1343
+ }
1344
+ const duration = block.actualEndTime != null ? Math.round((block.actualEndTime.getTime() - block.startTime.getTime()) / (1e3 * 60)) : 0;
1345
+ if (block.isActive) {
1346
+ const now = /* @__PURE__ */ new Date();
1347
+ const elapsed = Math.round((now.getTime() - block.startTime.getTime()) / (1e3 * 60));
1348
+ const remaining = Math.round((block.endTime.getTime() - now.getTime()) / (1e3 * 60));
1349
+ const elapsedHours = Math.floor(elapsed / 60);
1350
+ const elapsedMins = elapsed % 60;
1351
+ const remainingHours = Math.floor(remaining / 60);
1352
+ const remainingMins = remaining % 60;
1353
+ if (compact) return `${start}\n(${elapsedHours}h${elapsedMins}m/${remainingHours}h${remainingMins}m)`;
1354
+ return `${start} (${elapsedHours}h ${elapsedMins}m elapsed, ${remainingHours}h ${remainingMins}m remaining)`;
1355
+ }
1356
+ const hours = Math.floor(duration / 60);
1357
+ const mins = duration % 60;
1358
+ if (compact) return hours > 0 ? `${start}\n(${hours}h${mins}m)` : `${start}\n(${mins}m)`;
1359
+ if (hours > 0) return `${start} (${hours}h ${mins}m)`;
1360
+ return `${start} (${mins}m)`;
1361
+ }
1362
+ function formatModels(models, compact = false) {
1363
+ if (models.length === 0) return "-";
1364
+ return compact ? formatModelsDisplay(models) : formatModelsDisplay(models);
1365
+ }
1366
+ function parseTokenLimit(value, maxFromAll) {
1367
+ if (value == null || value === "") return void 0;
1368
+ if (value === "max") return maxFromAll > 0 ? maxFromAll : void 0;
1369
+ const limit = Number.parseInt(value, 10);
1370
+ return Number.isNaN(limit) ? void 0 : limit;
1371
+ }
1372
+ const blocksCommand = define({
1373
+ name: "blocks",
1374
+ description: "Show usage report grouped by 5-hour billing blocks",
1375
+ args: {
1376
+ ...sharedCommandConfig.args,
1377
+ active: {
1378
+ type: "boolean",
1379
+ short: "a",
1380
+ description: "Show only active block with projections",
1381
+ default: false
1382
+ },
1383
+ recent: {
1384
+ type: "boolean",
1385
+ short: "r",
1386
+ description: `Show blocks from last ${RECENT_DAYS_DEFAULT} days (including active)`,
1387
+ default: false
1388
+ },
1389
+ tokenLimit: {
1390
+ type: "string",
1391
+ short: "t",
1392
+ description: "Token limit for quota warnings (e.g., 500000 or \"max\" for highest previous block)"
1393
+ }
1394
+ },
1395
+ toKebab: true,
1396
+ async run(ctx) {
1397
+ if (ctx.values.json) logger.level = 0;
1398
+ let blocks = await loadFiveHourBlockData({
1399
+ since: ctx.values.since,
1400
+ until: ctx.values.until,
1401
+ claudePath: getDefaultClaudePath(),
1402
+ mode: ctx.values.mode,
1403
+ order: ctx.values.order
1404
+ });
1405
+ if (blocks.length === 0) {
1406
+ if (ctx.values.json) log(JSON.stringify({ blocks: [] }));
1407
+ else logger.warn("No Claude usage data found.");
1408
+ process$1.exit(0);
1409
+ }
1410
+ let maxTokensFromAll = 0;
1411
+ if (ctx.values.tokenLimit === "max") {
1412
+ for (const block of blocks) if (!(block.isGap ?? false) && !block.isActive) {
1413
+ const blockTokens = block.tokenCounts.inputTokens + block.tokenCounts.outputTokens;
1414
+ if (blockTokens > maxTokensFromAll) maxTokensFromAll = blockTokens;
1415
+ }
1416
+ if (!ctx.values.json && maxTokensFromAll > 0) logger.info(`Using max tokens from previous sessions: ${formatNumber(maxTokensFromAll)}`);
1417
+ }
1418
+ if (ctx.values.recent) blocks = filterRecentBlocks(blocks, RECENT_DAYS_DEFAULT);
1419
+ if (ctx.values.active) {
1420
+ blocks = blocks.filter((block) => block.isActive);
1421
+ if (blocks.length === 0) {
1422
+ if (ctx.values.json) log(JSON.stringify({
1423
+ blocks: [],
1424
+ message: "No active block"
1425
+ }));
1426
+ else logger.info("No active 5-hour block found.");
1427
+ process$1.exit(0);
1428
+ }
1429
+ }
1430
+ if (ctx.values.json) {
1431
+ const jsonOutput = { blocks: blocks.map((block) => {
1432
+ const burnRate = block.isActive ? calculateBurnRate(block) : null;
1433
+ const projection = block.isActive ? projectBlockUsage(block) : null;
1434
+ return {
1435
+ id: block.id,
1436
+ startTime: block.startTime.toISOString(),
1437
+ endTime: block.endTime.toISOString(),
1438
+ actualEndTime: block.actualEndTime != null ? block.actualEndTime.toISOString() : null,
1439
+ isActive: block.isActive,
1440
+ isGap: block.isGap ?? false,
1441
+ entries: block.entries.length,
1442
+ tokenCounts: block.tokenCounts,
1443
+ totalTokens: block.tokenCounts.inputTokens + block.tokenCounts.outputTokens,
1444
+ costUSD: block.costUSD,
1445
+ models: block.models,
1446
+ burnRate,
1447
+ projection,
1448
+ tokenLimitStatus: projection != null && ctx.values.tokenLimit != null ? (() => {
1449
+ const limit = parseTokenLimit(ctx.values.tokenLimit, maxTokensFromAll);
1450
+ return limit != null ? {
1451
+ limit,
1452
+ projectedUsage: projection.totalTokens,
1453
+ percentUsed: projection.totalTokens / limit * 100,
1454
+ status: projection.totalTokens > limit ? "exceeds" : projection.totalTokens > limit * WARNING_THRESHOLD ? "warning" : "ok"
1455
+ } : void 0;
1456
+ })() : void 0
1457
+ };
1458
+ }) };
1459
+ log(JSON.stringify(jsonOutput, null, 2));
1460
+ } else if (ctx.values.active && blocks.length === 1) {
1461
+ const block = blocks[0];
1462
+ if (block == null) {
1463
+ logger.warn("No active block found.");
1464
+ process$1.exit(0);
1465
+ }
1466
+ const burnRate = calculateBurnRate(block);
1467
+ const projection = projectBlockUsage(block);
1468
+ logger.box("Current 5-Hour Block Status");
1469
+ const now = /* @__PURE__ */ new Date();
1470
+ const elapsed = Math.round((now.getTime() - block.startTime.getTime()) / (1e3 * 60));
1471
+ const remaining = Math.round((block.endTime.getTime() - now.getTime()) / (1e3 * 60));
1472
+ log(`Block Started: ${import_picocolors$3.default.cyan(block.startTime.toLocaleString())} (${import_picocolors$3.default.yellow(`${Math.floor(elapsed / 60)}h ${elapsed % 60}m`)} ago)`);
1473
+ log(`Time Remaining: ${import_picocolors$3.default.green(`${Math.floor(remaining / 60)}h ${remaining % 60}m`)}\n`);
1474
+ log(import_picocolors$3.default.bold("Current Usage:"));
1475
+ log(` Input Tokens: ${formatNumber(block.tokenCounts.inputTokens)}`);
1476
+ log(` Output Tokens: ${formatNumber(block.tokenCounts.outputTokens)}`);
1477
+ log(` Total Cost: ${formatCurrency(block.costUSD)}\n`);
1478
+ if (burnRate != null) {
1479
+ log(import_picocolors$3.default.bold("Burn Rate:"));
1480
+ log(` Tokens/minute: ${formatNumber(burnRate.tokensPerMinute)}`);
1481
+ log(` Cost/hour: ${formatCurrency(burnRate.costPerHour)}\n`);
1482
+ }
1483
+ if (projection != null) {
1484
+ log(import_picocolors$3.default.bold("Projected Usage (if current rate continues):"));
1485
+ log(` Total Tokens: ${formatNumber(projection.totalTokens)}`);
1486
+ log(` Total Cost: ${formatCurrency(projection.totalCost)}\n`);
1487
+ if (ctx.values.tokenLimit != null) {
1488
+ const limit = parseTokenLimit(ctx.values.tokenLimit, maxTokensFromAll);
1489
+ if (limit != null && limit > 0) {
1490
+ const currentTokens = block.tokenCounts.inputTokens + block.tokenCounts.outputTokens;
1491
+ const remainingTokens = Math.max(0, limit - currentTokens);
1492
+ const percentUsed = projection.totalTokens / limit * 100;
1493
+ const status = percentUsed > 100 ? import_picocolors$3.default.red("EXCEEDS LIMIT") : percentUsed > WARNING_THRESHOLD * 100 ? import_picocolors$3.default.yellow("WARNING") : import_picocolors$3.default.green("OK");
1494
+ log(import_picocolors$3.default.bold("Token Limit Status:"));
1495
+ log(` Limit: ${formatNumber(limit)} tokens`);
1496
+ log(` Current Usage: ${formatNumber(currentTokens)} (${(currentTokens / limit * 100).toFixed(1)}%)`);
1497
+ log(` Remaining: ${formatNumber(remainingTokens)} tokens`);
1498
+ log(` Projected Usage: ${percentUsed.toFixed(1)}% ${status}`);
1499
+ }
1500
+ }
1501
+ }
1502
+ } else {
1503
+ logger.box("Claude Code Token Usage Report - 5-Hour Blocks");
1504
+ const actualTokenLimit = parseTokenLimit(ctx.values.tokenLimit, maxTokensFromAll);
1505
+ const tableHeaders = [
1506
+ "Block Start",
1507
+ "Duration/Status",
1508
+ "Models",
1509
+ "Tokens"
1510
+ ];
1511
+ const tableAligns = [
1512
+ "left",
1513
+ "left",
1514
+ "left",
1515
+ "right"
1516
+ ];
1517
+ if (actualTokenLimit != null && actualTokenLimit > 0) {
1518
+ tableHeaders.push("%");
1519
+ tableAligns.push("right");
1520
+ }
1521
+ tableHeaders.push("Cost");
1522
+ tableAligns.push("right");
1523
+ const table = new ResponsiveTable({
1524
+ head: tableHeaders,
1525
+ style: { head: ["cyan"] },
1526
+ colAligns: tableAligns
1527
+ });
1528
+ const terminalWidth = process$1.stdout.columns || DEFAULT_TERMINAL_WIDTH;
1529
+ const useCompactFormat = terminalWidth < COMPACT_WIDTH_THRESHOLD;
1530
+ for (const block of blocks) if (block.isGap ?? false) {
1531
+ const gapRow = [
1532
+ import_picocolors$3.default.gray(formatBlockTime(block, useCompactFormat)),
1533
+ import_picocolors$3.default.gray("(inactive)"),
1534
+ import_picocolors$3.default.gray("-"),
1535
+ import_picocolors$3.default.gray("-")
1536
+ ];
1537
+ if (actualTokenLimit != null && actualTokenLimit > 0) gapRow.push(import_picocolors$3.default.gray("-"));
1538
+ gapRow.push(import_picocolors$3.default.gray("-"));
1539
+ table.push(gapRow);
1540
+ } else {
1541
+ const totalTokens = block.tokenCounts.inputTokens + block.tokenCounts.outputTokens;
1542
+ const status = block.isActive ? import_picocolors$3.default.green("ACTIVE") : "";
1543
+ const row = [
1544
+ formatBlockTime(block, useCompactFormat),
1545
+ status,
1546
+ formatModels(block.models, useCompactFormat),
1547
+ formatNumber(totalTokens)
1548
+ ];
1549
+ if (actualTokenLimit != null && actualTokenLimit > 0) {
1550
+ const percentage = totalTokens / actualTokenLimit * 100;
1551
+ const percentText = `${percentage.toFixed(1)}%`;
1552
+ row.push(percentage > 100 ? import_picocolors$3.default.red(percentText) : percentText);
1553
+ }
1554
+ row.push(formatCurrency(block.costUSD));
1555
+ table.push(row);
1556
+ if (block.isActive) {
1557
+ if (actualTokenLimit != null && actualTokenLimit > 0) {
1558
+ const currentTokens = block.tokenCounts.inputTokens + block.tokenCounts.outputTokens;
1559
+ const remainingTokens = Math.max(0, actualTokenLimit - currentTokens);
1560
+ const remainingText = remainingTokens > 0 ? formatNumber(remainingTokens) : import_picocolors$3.default.red("0");
1561
+ const remainingPercent = (actualTokenLimit - currentTokens) / actualTokenLimit * 100;
1562
+ const remainingPercentText = remainingPercent > 0 ? `${remainingPercent.toFixed(1)}%` : import_picocolors$3.default.red("0.0%");
1563
+ const remainingRow = [
1564
+ {
1565
+ content: import_picocolors$3.default.gray(`(assuming ${formatNumber(actualTokenLimit)} token limit)`),
1566
+ hAlign: "right"
1567
+ },
1568
+ import_picocolors$3.default.blue("REMAINING"),
1569
+ "",
1570
+ remainingText,
1571
+ remainingPercentText,
1572
+ ""
1573
+ ];
1574
+ table.push(remainingRow);
1575
+ }
1576
+ const projection = projectBlockUsage(block);
1577
+ if (projection != null) {
1578
+ const projectedTokens = formatNumber(projection.totalTokens);
1579
+ const projectedText = actualTokenLimit != null && actualTokenLimit > 0 && projection.totalTokens > actualTokenLimit ? import_picocolors$3.default.red(projectedTokens) : projectedTokens;
1580
+ const projectedRow = [
1581
+ {
1582
+ content: import_picocolors$3.default.gray("(assuming current burn rate)"),
1583
+ hAlign: "right"
1584
+ },
1585
+ import_picocolors$3.default.yellow("PROJECTED"),
1586
+ "",
1587
+ projectedText
1588
+ ];
1589
+ if (actualTokenLimit != null && actualTokenLimit > 0) {
1590
+ const percentage = projection.totalTokens / actualTokenLimit * 100;
1591
+ const percentText = `${percentage.toFixed(1)}%`;
1592
+ projectedRow.push(percentText);
1593
+ }
1594
+ projectedRow.push(formatCurrency(projection.totalCost));
1595
+ table.push(projectedRow);
1596
+ }
1597
+ }
1598
+ }
1599
+ log(table.toString());
1600
+ }
1601
+ }
1602
+ });
1603
+
1322
1604
  //#endregion
1323
1605
  //#region src/commands/daily.ts
1324
1606
  var import_picocolors$2 = __toESM(require_picocolors(), 1);
@@ -1701,6 +1983,7 @@ const subCommands = /* @__PURE__ */ new Map();
1701
1983
  subCommands.set("daily", dailyCommand);
1702
1984
  subCommands.set("monthly", monthlyCommand);
1703
1985
  subCommands.set("session", sessionCommand);
1986
+ subCommands.set("blocks", blocksCommand);
1704
1987
  subCommands.set("mcp", mcpCommand);
1705
1988
  const mainCommand = dailyCommand;
1706
1989
  await cli(process$1.argv.slice(2), mainCommand, {
@@ -965,7 +965,7 @@ const consola = createConsola$1();
965
965
  //#endregion
966
966
  //#region package.json
967
967
  var name = "ccusage";
968
- var version = "9.0.0";
968
+ var version = "10.0.0";
969
969
  var description = "Usage analysis tool for Claude Code";
970
970
 
971
971
  //#endregion
package/dist/logger.js CHANGED
@@ -1,3 +1,3 @@
1
- import { log, logger } from "./logger-DKw-DPXD.js";
1
+ import { log, logger } from "./logger-C2GZVy1v.js";
2
2
 
3
3
  export { log, logger };
@@ -1,8 +1,8 @@
1
1
  import { __commonJS, __require, __toESM } from "./chunk-BLXvPPr8.js";
2
- import { getDefaultClaudePath, loadDailyUsageData, loadSessionData } from "./data-loader-Ca8k_uX0.js";
2
+ import { getDefaultClaudePath, loadDailyUsageData, loadSessionData } from "./data-loader-Bp9KV0_a.js";
3
3
  import { description, literal, object, optional, pipe, union } from "./dist-DCvt9hEv.js";
4
- import { name, version } from "./logger-DKw-DPXD.js";
5
- import { CostModes, dateSchema } from "./pricing-fetcher-BlxDpqFj.js";
4
+ import { name, version } from "./logger-C2GZVy1v.js";
5
+ import { CostModes, dateSchema } from "./pricing-fetcher-BxlbW4km.js";
6
6
  import { anyType, arrayType, booleanType, discriminatedUnionType, enumType, literalType, numberType, objectType, optionalType, recordType, stringType, unionType, unknownType } from "./types-BlyCnKwN.js";
7
7
  import { toJsonSchema } from "./index-CISmcbXk-CW1Gj6Ab.js";
8
8
  import process$1 from "node:process";
package/dist/mcp.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import "./pricing-fetcher-BkSZh4lR.js";
2
- import { LoadOptions } from "./data-loader-OGaMjZTD.js";
2
+ import { LoadOptions } from "./data-loader-DPQaq8_n.js";
3
3
  import { FastMCP } from "fastmcp";
4
4
 
5
5
  //#region src/mcp.d.ts
package/dist/mcp.js CHANGED
@@ -1,9 +1,9 @@
1
- import "./data-loader-Ca8k_uX0.js";
1
+ import "./data-loader-Bp9KV0_a.js";
2
2
  import "./dist-DCvt9hEv.js";
3
- import "./logger-DKw-DPXD.js";
4
- import "./pricing-fetcher-BlxDpqFj.js";
3
+ import "./logger-C2GZVy1v.js";
4
+ import "./pricing-fetcher-BxlbW4km.js";
5
5
  import "./types-BlyCnKwN.js";
6
- import { createMcpServer } from "./mcp-BRFYI5rd.js";
6
+ import { createMcpServer } from "./mcp-DVjQeqCy.js";
7
7
  import "./index-CISmcbXk-CW1Gj6Ab.js";
8
8
 
9
9
  export { createMcpServer };
@@ -1,5 +1,5 @@
1
1
  import { number, object, optional, pipe, regex, safeParse, string } from "./dist-DCvt9hEv.js";
2
- import { logger } from "./logger-DKw-DPXD.js";
2
+ import { logger } from "./logger-C2GZVy1v.js";
3
3
 
4
4
  //#region src/consts.internal.ts
5
5
  const LITELLM_PRICING_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json";
@@ -1,5 +1,5 @@
1
1
  import "./dist-DCvt9hEv.js";
2
- import "./logger-DKw-DPXD.js";
3
- import { PricingFetcher } from "./pricing-fetcher-BlxDpqFj.js";
2
+ import "./logger-C2GZVy1v.js";
3
+ import { PricingFetcher } from "./pricing-fetcher-BxlbW4km.js";
4
4
 
5
5
  export { PricingFetcher };
@@ -1769,7 +1769,10 @@ var ResponsiveTable = class {
1769
1769
  toString() {
1770
1770
  const terminalWidth = process$1.stdout.columns || 120;
1771
1771
  const dataRows = this.rows.filter((row) => !this.isSeparatorRow(row));
1772
- const allRows = [this.head.map(String), ...dataRows.map((row) => row.map(String))];
1772
+ const allRows = [this.head.map(String), ...dataRows.map((row) => row.map((cell) => {
1773
+ if (typeof cell === "object" && cell != null && "content" in cell) return String(cell.content);
1774
+ return String(cell ?? "");
1775
+ }))];
1773
1776
  const contentWidths = this.head.map((_, colIndex) => {
1774
1777
  const maxLength = Math.max(...allRows.map((row) => stringWidth(String(row[colIndex] ?? ""))));
1775
1778
  return maxLength;
@@ -1827,7 +1830,10 @@ var ResponsiveTable = class {
1827
1830
  }
1828
1831
  }
1829
1832
  isSeparatorRow(row) {
1830
- return row.every((cell) => typeof cell === "string" && (cell === "" || /^─+$/.test(cell)));
1833
+ return row.every((cell) => {
1834
+ if (typeof cell === "object" && cell != null && "content" in cell) return cell.content === "" || /^─+$/.test(cell.content);
1835
+ return typeof cell === "string" && (cell === "" || /^─+$/.test(cell));
1836
+ });
1831
1837
  }
1832
1838
  isDateString(text) {
1833
1839
  return /^\d{4}-\d{2}-\d{2}$/.test(text);
@@ -1,5 +1,8 @@
1
1
  //#region src/utils.table.d.ts
2
- type TableRow = (string | number)[];
2
+ type TableRow = (string | number | {
3
+ content: string;
4
+ hAlign?: 'left' | 'right' | 'center';
5
+ })[];
3
6
  type TableOptions = {
4
7
  head: string[];
5
8
  colAligns?: ('left' | 'right' | 'center')[];
@@ -1,3 +1,3 @@
1
- import { ResponsiveTable } from "./utils.table-DRzF8vZJ.js";
1
+ import { ResponsiveTable } from "./utils.table-USks3NGv.js";
2
2
 
3
3
  export { ResponsiveTable };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ccusage",
3
3
  "type": "module",
4
- "version": "9.0.0",
4
+ "version": "10.0.0",
5
5
  "description": "Usage analysis tool for Claude Code",
6
6
  "author": "ryoppippi",
7
7
  "license": "MIT",