ccusage 9.0.1 → 11.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
@@ -41,6 +41,7 @@ This tool helps you understand the value you're getting from your subscription b
41
41
  - 📊 **Daily Report**: View token usage and costs aggregated by date
42
42
  - 📅 **Monthly Report**: View token usage and costs aggregated by month
43
43
  - 💬 **Session Report**: View usage grouped by conversation sessions
44
+ - ⏰ **5-Hour Blocks Report**: Track usage within Claude's billing windows with active block monitoring
44
45
  - 🤖 **Model Tracking**: See which Claude models you're using (Opus, Sonnet, etc.)
45
46
  - 📊 **Model Breakdown**: View per-model cost breakdown with `--breakdown` flag
46
47
  - 📅 **Date Filtering**: Filter reports by date range using `--since` and `--until`
@@ -51,6 +52,7 @@ This tool helps you understand the value you're getting from your subscription b
51
52
  - 🔄 **Cache Token Support**: Tracks and displays cache creation and cache read tokens separately
52
53
  - 🌐 **Offline Mode**: Use pre-cached pricing data without network connectivity with `--offline` (Claude models only)
53
54
  - 📏 **Responsive Tables**: Automatic table width adjustment for narrow terminals with intelligent word wrapping
55
+ - 🔌 **MCP Integration**: Built-in Model Context Protocol server for integration with other tools
54
56
 
55
57
  ## Important Disclaimer
56
58
 
@@ -230,6 +232,55 @@ ccusage session --offline # Use pre-cached pricing data
230
232
  ccusage session -O # Short alias for --offline
231
233
  ```
232
234
 
235
+ ### 5-Hour Blocks Report
236
+
237
+ Shows usage grouped by Claude's 5-hour billing windows:
238
+
239
+ ```bash
240
+ # Show all 5-hour blocks
241
+ ccusage blocks
242
+
243
+ # Show only the active block with detailed projections
244
+ ccusage blocks --active
245
+
246
+ # Show blocks from the last 3 days (including active)
247
+ ccusage blocks --recent
248
+
249
+ # Set a token limit to see if you'll exceed it
250
+ ccusage blocks -t 500000
251
+
252
+ # Use the highest previous block as the token limit
253
+ ccusage blocks -t max
254
+
255
+ # Combine options
256
+ ccusage blocks --recent -t max
257
+
258
+ # Output in JSON format
259
+ ccusage blocks --json
260
+
261
+ # Control cost calculation mode
262
+ ccusage blocks --mode auto # Use costUSD when available, calculate otherwise (default)
263
+ ccusage blocks --mode calculate # Always calculate costs from tokens
264
+ ccusage blocks --mode display # Always show pre-calculated costUSD values
265
+
266
+ # Control sort order
267
+ ccusage blocks --order asc # Show oldest blocks first
268
+ ccusage blocks --order desc # Show newest blocks first (default)
269
+ ```
270
+
271
+ The blocks report helps you understand Claude Code's 5-hour rolling session windows:
272
+
273
+ - Sessions start with your first message and last for 5 hours
274
+ - Shows active blocks with time remaining and burn rate projections
275
+ - Helps track if you're approaching token limits within a session
276
+ - The `-t max` option automatically uses your highest previous block as the limit
277
+
278
+ #### Blocks-specific options:
279
+
280
+ - `-t, --token-limit <number|max>`: Set token limit for quota warnings (use "max" for highest previous block)
281
+ - `-a, --active`: Show only active block with detailed projections
282
+ - `-r, --recent`: Show blocks from last 3 days (including active)
283
+
233
284
  ### Options
234
285
 
235
286
  All commands support the following options:
@@ -291,6 +342,8 @@ Available MCP tools:
291
342
 
292
343
  - `daily`: Returns daily usage reports (accepts `since`, `until`, `mode` parameters)
293
344
  - `session`: Returns session usage reports (accepts `since`, `until`, `mode` parameters)
345
+ - `monthly`: Returns monthly usage reports (accepts `since`, `until`, `mode` parameters)
346
+ - `blocks`: Returns 5-hour billing blocks usage reports (accepts `since`, `until`, `mode` parameters)
294
347
 
295
348
  #### Claude Desktop Configuration Example
296
349
 
@@ -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-BgKOQAEs.js";
4
- import { PricingFetcher } from "./pricing-fetcher-1KHPTXC3.js";
3
+ import { logger } from "./logger-D3WD64Tx.js";
4
+ import { PricingFetcher } from "./pricing-fetcher-D-eJQBEJ.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-CotJGlGF.js";
1
+ import { DailyUsageSchema, ModelBreakdownSchema, MonthlyUsageSchema, SessionUsageSchema, UsageDataSchema, calculateCostForEntry, createUniqueHash, formatDate, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadFiveHourBlockData, loadMonthlyUsageData, loadSessionData, sortFilesByTimestamp } from "./data-loader-BAoqS297.js";
2
2
  import "./dist-DCvt9hEv.js";
3
- import "./logger-BgKOQAEs.js";
4
- import "./pricing-fetcher-1KHPTXC3.js";
3
+ import "./logger-D3WD64Tx.js";
4
+ import "./pricing-fetcher-D-eJQBEJ.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-CotJGlGF.js";
2
+ import { UsageDataSchema, glob, require_usingCtx } from "./data-loader-BAoqS297.js";
3
3
  import { safeParse } from "./dist-DCvt9hEv.js";
4
- import { logger } from "./logger-BgKOQAEs.js";
5
- import { PricingFetcher } from "./pricing-fetcher-1KHPTXC3.js";
4
+ import { logger } from "./logger-D3WD64Tx.js";
5
+ import { PricingFetcher } from "./pricing-fetcher-D-eJQBEJ.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-CotJGlGF.js";
1
+ import "./data-loader-BAoqS297.js";
2
2
  import "./dist-DCvt9hEv.js";
3
- import "./logger-BgKOQAEs.js";
4
- import "./pricing-fetcher-1KHPTXC3.js";
5
- import { detectMismatches, printMismatchReport } from "./debug-DH_5GWYh.js";
3
+ import "./logger-D3WD64Tx.js";
4
+ import "./pricing-fetcher-D-eJQBEJ.js";
5
+ import { detectMismatches, printMismatchReport } from "./debug-DzFJHzM5.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-CotJGlGF.js";
4
+ import { calculateBurnRate, filterRecentBlocks, formatDateCompact, getDefaultClaudePath, loadDailyUsageData, loadFiveHourBlockData, loadMonthlyUsageData, loadSessionData, projectBlockUsage } from "./data-loader-BAoqS297.js";
5
5
  import { safeParse } from "./dist-DCvt9hEv.js";
6
- import { description, log, logger, name, version } from "./logger-BgKOQAEs.js";
7
- import { CostModes, SortOrders, dateSchema } from "./pricing-fetcher-1KHPTXC3.js";
8
- import { detectMismatches, printMismatchReport } from "./debug-DH_5GWYh.js";
9
- import { ResponsiveTable } from "./utils.table-DRzF8vZJ.js";
6
+ import { description, log, logger, name, version } from "./logger-D3WD64Tx.js";
7
+ import { CostModes, SortOrders, dateSchema } from "./pricing-fetcher-D-eJQBEJ.js";
8
+ import { detectMismatches, printMismatchReport } from "./debug-DzFJHzM5.js";
9
+ import { ResponsiveTable } from "./utils.table-USks3NGv.js";
10
10
  import "./types-BlyCnKwN.js";
11
- import { createMcpServer } from "./mcp-BUmEAQZm.js";
11
+ import { createMcpServer } from "./mcp-1J7r1g6-.js";
12
12
  import "./index-CISmcbXk-CW1Gj6Ab.js";
13
13
  import process$1 from "node:process";
14
14
 
@@ -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.1";
968
+ var version = "11.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-BgKOQAEs.js";
1
+ import { log, logger } from "./logger-D3WD64Tx.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-CotJGlGF.js";
2
+ import { getDefaultClaudePath, loadDailyUsageData, loadFiveHourBlockData, loadMonthlyUsageData, loadSessionData } from "./data-loader-BAoqS297.js";
3
3
  import { description, literal, object, optional, pipe, union } from "./dist-DCvt9hEv.js";
4
- import { name, version } from "./logger-BgKOQAEs.js";
5
- import { CostModes, dateSchema } from "./pricing-fetcher-1KHPTXC3.js";
4
+ import { name, version } from "./logger-D3WD64Tx.js";
5
+ import { CostModes, dateSchema } from "./pricing-fetcher-D-eJQBEJ.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";
@@ -37580,6 +37580,30 @@ function createMcpServer({ claudePath } = defaultOptions) {
37580
37580
  return JSON.stringify(sessionData);
37581
37581
  }
37582
37582
  });
37583
+ server.addTool({
37584
+ name: "monthly",
37585
+ description: "Show usage report grouped by month",
37586
+ parameters: parametersSchema,
37587
+ execute: async (args) => {
37588
+ const monthlyData = await loadMonthlyUsageData({
37589
+ ...args,
37590
+ claudePath
37591
+ });
37592
+ return JSON.stringify(monthlyData);
37593
+ }
37594
+ });
37595
+ server.addTool({
37596
+ name: "blocks",
37597
+ description: "Show usage report grouped by 5-hour billing blocks",
37598
+ parameters: parametersSchema,
37599
+ execute: async (args) => {
37600
+ const blocksData = await loadFiveHourBlockData({
37601
+ ...args,
37602
+ claudePath
37603
+ });
37604
+ return JSON.stringify(blocksData);
37605
+ }
37606
+ });
37583
37607
  return server;
37584
37608
  }
37585
37609
 
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-CotJGlGF.js";
1
+ import "./data-loader-BAoqS297.js";
2
2
  import "./dist-DCvt9hEv.js";
3
- import "./logger-BgKOQAEs.js";
4
- import "./pricing-fetcher-1KHPTXC3.js";
3
+ import "./logger-D3WD64Tx.js";
4
+ import "./pricing-fetcher-D-eJQBEJ.js";
5
5
  import "./types-BlyCnKwN.js";
6
- import { createMcpServer } from "./mcp-BUmEAQZm.js";
6
+ import { createMcpServer } from "./mcp-1J7r1g6-.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-BgKOQAEs.js";
2
+ import { logger } from "./logger-D3WD64Tx.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-BgKOQAEs.js";
3
- import { PricingFetcher } from "./pricing-fetcher-1KHPTXC3.js";
2
+ import "./logger-D3WD64Tx.js";
3
+ import { PricingFetcher } from "./pricing-fetcher-D-eJQBEJ.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.1",
4
+ "version": "11.0.0",
5
5
  "description": "Usage analysis tool for Claude Code",
6
6
  "author": "ryoppippi",
7
7
  "license": "MIT",