ccusage 0.5.0 → 0.6.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
@@ -38,12 +38,13 @@ This tool helps you understand the value you're getting from your subscription b
38
38
  ## Features
39
39
 
40
40
  - 📊 **Daily Report**: View token usage and costs aggregated by date
41
+ - 📅 **Monthly Report**: View token usage and costs aggregated by month
41
42
  - 💬 **Session Report**: View usage grouped by conversation sessions
42
43
  - 📅 **Date Filtering**: Filter reports by date range using `--since` and `--until`
43
44
  - 📁 **Custom Path**: Support for custom Claude data directory locations
44
45
  - 🎨 **Beautiful Output**: Colorful table-formatted display
45
46
  - 📄 **JSON Output**: Export data in structured JSON format with `--json`
46
- - 💰 **Cost Tracking**: Shows costs in USD for each day/session
47
+ - 💰 **Cost Tracking**: Shows costs in USD for each day/month/session
47
48
  - 🔄 **Cache Token Support**: Tracks and displays cache creation and cache read tokens separately
48
49
 
49
50
  ## Important Disclaimer
@@ -134,10 +135,41 @@ ccusage daily --json
134
135
  ccusage daily --mode auto # Use costUSD when available, calculate otherwise (default)
135
136
  ccusage daily --mode calculate # Always calculate costs from tokens
136
137
  ccusage daily --mode display # Always show pre-calculated costUSD values
138
+
139
+ # Control sort order
140
+ ccusage daily --order asc # Show oldest dates first
141
+ ccusage daily --order desc # Show newest dates first (default)
137
142
  ```
138
143
 
139
144
  `ccusage` is an alias for `ccusage daily`, so you can run it without specifying the subcommand.
140
145
 
146
+ ### Monthly Report
147
+
148
+ Shows token usage and costs aggregated by month:
149
+
150
+ ```bash
151
+ # Show all monthly usage
152
+ ccusage monthly
153
+
154
+ # Filter by date range
155
+ ccusage monthly --since 20250101 --until 20250531
156
+
157
+ # Use custom Claude data directory
158
+ ccusage monthly --path /custom/path/to/.claude
159
+
160
+ # Output in JSON format
161
+ ccusage monthly --json
162
+
163
+ # Control cost calculation mode
164
+ ccusage monthly --mode auto # Use costUSD when available, calculate otherwise (default)
165
+ ccusage monthly --mode calculate # Always calculate costs from tokens
166
+ ccusage monthly --mode display # Always show pre-calculated costUSD values
167
+
168
+ # Control sort order
169
+ ccusage monthly --order asc # Show oldest months first
170
+ ccusage monthly --order desc # Show newest months first (default)
171
+ ```
172
+
141
173
  ### Session Report
142
174
 
143
175
  Shows usage grouped by conversation sessions, sorted by cost:
@@ -159,6 +191,10 @@ ccusage session --json
159
191
  ccusage session --mode auto # Use costUSD when available, calculate otherwise (default)
160
192
  ccusage session --mode calculate # Always calculate costs from tokens
161
193
  ccusage session --mode display # Always show pre-calculated costUSD values
194
+
195
+ # Control sort order
196
+ ccusage session --order asc # Show oldest sessions first
197
+ ccusage session --order desc # Show newest sessions first (default)
162
198
  ```
163
199
 
164
200
  ### Options
@@ -170,6 +206,7 @@ All commands support the following options:
170
206
  - `-p, --path <path>`: Custom path to Claude data directory (default: `~/.claude`)
171
207
  - `-j, --json`: Output results in JSON format instead of table
172
208
  - `-m, --mode <mode>`: Cost calculation mode: `auto` (default), `calculate`, or `display`
209
+ - `-o, --order <order>`: Sort order: `desc` (newest first, default) or `asc` (oldest first).
173
210
  - `-d, --debug`: Show pricing mismatch information for debugging
174
211
  - `--debug-samples <number>`: Number of sample discrepancies to show in debug output (default: 5)
175
212
  - `-h, --help`: Display help message
@@ -1,10 +1,10 @@
1
1
  import "./index-BurjgCfW.js";
2
2
  import "./pricing-fetcher-DygIroMj.js";
3
- import { TokenData, TokenTotals } from "./types-B3ib19os.js";
4
- import { DailyUsage, SessionUsage } from "./data-loader-LMCrJ-lW.js";
3
+ import { TokenData, TokenTotals } from "./types-y1JQzaKZ.js";
4
+ import { DailyUsage, MonthlyUsage, SessionUsage } from "./data-loader-aUOjeZ06.js";
5
5
 
6
6
  //#region src/calculate-cost.d.ts
7
- declare function calculateTotals(data: Array<DailyUsage | SessionUsage>): TokenTotals;
7
+ declare function calculateTotals(data: Array<DailyUsage | MonthlyUsage | SessionUsage>): TokenTotals;
8
8
  declare function getTotalTokens(tokens: TokenData): number;
9
9
  declare function createTotalsObject(totals: TokenTotals): {
10
10
  inputTokens: number;
@@ -1,5 +1,5 @@
1
- import { array, number, object, optional, safeParse, string } from "./dist-FwNhpFrW.js";
2
- import { calculateCostFromTokens, fetchModelPricing, getModelPricing } from "./pricing-fetcher-B5yPtoTB.js";
1
+ import { array, number, object, optional, pipe, regex, safeParse, string } from "./dist-FwNhpFrW.js";
2
+ import { calculateCostFromTokens, fetchModelPricing, getModelPricing } from "./pricing-fetcher-BCv1Vods.js";
3
3
  import { createRequire } from "node:module";
4
4
  import { readFile } from "node:fs/promises";
5
5
  import { homedir } from "node:os";
@@ -2110,9 +2110,9 @@ var require_picomatch$1 = __commonJS({ "node_modules/picomatch/lib/picomatch.js"
2110
2110
  if (glob$1 === "" || typeof glob$1 !== "string" && !isState) throw new TypeError("Expected pattern to be a non-empty string");
2111
2111
  const opts = options || {};
2112
2112
  const posix$1 = opts.windows;
2113
- const regex = isState ? picomatch$2.compileRe(glob$1, options) : picomatch$2.makeRe(glob$1, options, false, true);
2114
- const state = regex.state;
2115
- delete regex.state;
2113
+ const regex$1 = isState ? picomatch$2.compileRe(glob$1, options) : picomatch$2.makeRe(glob$1, options, false, true);
2114
+ const state = regex$1.state;
2115
+ delete regex$1.state;
2116
2116
  let isIgnored = () => false;
2117
2117
  if (opts.ignore) {
2118
2118
  const ignoreOpts = {
@@ -2124,14 +2124,14 @@ var require_picomatch$1 = __commonJS({ "node_modules/picomatch/lib/picomatch.js"
2124
2124
  isIgnored = picomatch$2(opts.ignore, ignoreOpts, returnState);
2125
2125
  }
2126
2126
  const matcher = (input, returnObject = false) => {
2127
- const { isMatch, match, output } = picomatch$2.test(input, regex, options, {
2127
+ const { isMatch, match, output } = picomatch$2.test(input, regex$1, options, {
2128
2128
  glob: glob$1,
2129
2129
  posix: posix$1
2130
2130
  });
2131
2131
  const result = {
2132
2132
  glob: glob$1,
2133
2133
  state,
2134
- regex,
2134
+ regex: regex$1,
2135
2135
  posix: posix$1,
2136
2136
  input,
2137
2137
  output,
@@ -2170,7 +2170,7 @@ var require_picomatch$1 = __commonJS({ "node_modules/picomatch/lib/picomatch.js"
2170
2170
  * @return {Object} Returns an object with matching info.
2171
2171
  * @api public
2172
2172
  */
2173
- picomatch$2.test = (input, regex, options, { glob: glob$1, posix: posix$1 } = {}) => {
2173
+ picomatch$2.test = (input, regex$1, options, { glob: glob$1, posix: posix$1 } = {}) => {
2174
2174
  if (typeof input !== "string") throw new TypeError("Expected input to be a string");
2175
2175
  if (input === "") return {
2176
2176
  isMatch: false,
@@ -2184,8 +2184,8 @@ var require_picomatch$1 = __commonJS({ "node_modules/picomatch/lib/picomatch.js"
2184
2184
  output = format ? format(input) : input;
2185
2185
  match = output === glob$1;
2186
2186
  }
2187
- if (match === false || opts.capture === true) if (opts.matchBase === true || opts.basename === true) match = picomatch$2.matchBase(input, regex, options, posix$1);
2188
- else match = regex.exec(output);
2187
+ if (match === false || opts.capture === true) if (opts.matchBase === true || opts.basename === true) match = picomatch$2.matchBase(input, regex$1, options, posix$1);
2188
+ else match = regex$1.exec(output);
2189
2189
  return {
2190
2190
  isMatch: Boolean(match),
2191
2191
  match,
@@ -2206,8 +2206,8 @@ var require_picomatch$1 = __commonJS({ "node_modules/picomatch/lib/picomatch.js"
2206
2206
  * @api public
2207
2207
  */
2208
2208
  picomatch$2.matchBase = (input, glob$1, options) => {
2209
- const regex = glob$1 instanceof RegExp ? glob$1 : picomatch$2.makeRe(glob$1, options);
2210
- return regex.test(utils$1.basename(input));
2209
+ const regex$1 = glob$1 instanceof RegExp ? glob$1 : picomatch$2.makeRe(glob$1, options);
2210
+ return regex$1.test(utils$1.basename(input));
2211
2211
  };
2212
2212
  /**
2213
2213
  * Returns true if **any** of the given glob `patterns` match the specified `string`.
@@ -2291,9 +2291,9 @@ var require_picomatch$1 = __commonJS({ "node_modules/picomatch/lib/picomatch.js"
2291
2291
  const append = opts.contains ? "" : "$";
2292
2292
  let source = `${prepend}(?:${state.output})${append}`;
2293
2293
  if (state && state.negated === true) source = `^(?!${source}).*$`;
2294
- const regex = picomatch$2.toRegex(source, options);
2295
- if (returnState === true) regex.state = state;
2296
- return regex;
2294
+ const regex$1 = picomatch$2.toRegex(source, options);
2295
+ if (returnState === true) regex$1.state = state;
2296
+ return regex$1;
2297
2297
  };
2298
2298
  /**
2299
2299
  * Create a regular expression from a parsed glob pattern.
@@ -2573,14 +2573,14 @@ function getPartialMatcher(patterns, options) {
2573
2573
  if (inputParts[0] === ".." && ONLY_PARENT_DIRECTORIES.test(input)) return true;
2574
2574
  for (let i = 0; i < patterns.length; i++) {
2575
2575
  const patternParts = patternsParts[i];
2576
- const regex = regexes[i];
2576
+ const regex$1 = regexes[i];
2577
2577
  const inputPatternCount = inputParts.length;
2578
2578
  const minParts = Math.min(inputPatternCount, patternParts.length);
2579
2579
  let j = 0;
2580
2580
  while (j < minParts) {
2581
2581
  const part = patternParts[j];
2582
2582
  if (part.includes("/")) return true;
2583
- const match = regex[j].test(inputParts[j]);
2583
+ const match = regex$1[j].test(inputParts[j]);
2584
2584
  if (!match) break;
2585
2585
  if (part === "**") return true;
2586
2586
  j++;
@@ -2786,7 +2786,7 @@ const UsageDataSchema = object({
2786
2786
  costUSD: optional(number())
2787
2787
  });
2788
2788
  const DailyUsageSchema = object({
2789
- date: string(),
2789
+ date: pipe(string(), regex(/^\d{4}-\d{2}-\d{2}$/)),
2790
2790
  inputTokens: number(),
2791
2791
  outputTokens: number(),
2792
2792
  cacheCreationTokens: number(),
@@ -2804,6 +2804,14 @@ const SessionUsageSchema = object({
2804
2804
  lastActivity: string(),
2805
2805
  versions: array(string())
2806
2806
  });
2807
+ const MonthlyUsageSchema = object({
2808
+ month: pipe(string(), regex(/^\d{4}-\d{2}$/)),
2809
+ inputTokens: number(),
2810
+ outputTokens: number(),
2811
+ cacheCreationTokens: number(),
2812
+ cacheReadTokens: number(),
2813
+ totalCost: number()
2814
+ });
2807
2815
  const formatDate = (dateStr) => {
2808
2816
  const date = new Date(dateStr);
2809
2817
  const year = date.getFullYear();
@@ -2827,7 +2835,7 @@ const calculateCostForEntry = (data, mode, modelPricing) => {
2827
2835
  }
2828
2836
  return 0;
2829
2837
  };
2830
- async function loadUsageData(options) {
2838
+ async function loadDailyUsageData(options) {
2831
2839
  const claudePath = options?.claudePath ?? getDefaultClaudePath();
2832
2840
  const claudeDir = path.join(claudePath, "projects");
2833
2841
  const files = await glob(["**/*.jsonl"], {
@@ -2871,7 +2879,9 @@ async function loadUsageData(options) {
2871
2879
  if (options.until && dateStr > options.until) return false;
2872
2880
  return true;
2873
2881
  });
2874
- return sort(results).desc((item) => new Date(item.date).getTime());
2882
+ const sortOrder = options?.order || "desc";
2883
+ const sortedResults = sort(results);
2884
+ return sortOrder === "desc" ? sortedResults.desc((item) => new Date(item.date).getTime()) : sortedResults.asc((item) => new Date(item.date).getTime());
2875
2885
  }
2876
2886
  async function loadSessionData(options) {
2877
2887
  const claudePath = options?.claudePath ?? getDefaultClaudePath();
@@ -2937,8 +2947,35 @@ async function loadSessionData(options) {
2937
2947
  if (options.until && dateStr > options.until) return false;
2938
2948
  return true;
2939
2949
  });
2940
- return sort(results).desc((item) => new Date(item.lastActivity).getTime());
2950
+ const sortOrder = options?.order || "desc";
2951
+ const sortedResults = sort(results);
2952
+ return sortOrder === "desc" ? sortedResults.desc((item) => new Date(item.lastActivity).getTime()) : sortedResults.asc((item) => new Date(item.lastActivity).getTime());
2953
+ }
2954
+ async function loadMonthlyUsageData(options) {
2955
+ const dailyData = await loadDailyUsageData(options);
2956
+ const monthlyMap = new Map();
2957
+ for (const data of dailyData) {
2958
+ const month = data.date.substring(0, 7);
2959
+ const existing = monthlyMap.get(month) || {
2960
+ month,
2961
+ inputTokens: 0,
2962
+ outputTokens: 0,
2963
+ cacheCreationTokens: 0,
2964
+ cacheReadTokens: 0,
2965
+ totalCost: 0
2966
+ };
2967
+ existing.inputTokens += data.inputTokens;
2968
+ existing.outputTokens += data.outputTokens;
2969
+ existing.cacheCreationTokens += data.cacheCreationTokens;
2970
+ existing.cacheReadTokens += data.cacheReadTokens;
2971
+ existing.totalCost += data.totalCost;
2972
+ monthlyMap.set(month, existing);
2973
+ }
2974
+ const monthlyArray = Array.from(monthlyMap.values());
2975
+ const sortOrder = options?.order || "desc";
2976
+ const sortedMonthly = sort(monthlyArray);
2977
+ return sortOrder === "desc" ? sortedMonthly.desc((item) => item.month) : sortedMonthly.asc((item) => item.month);
2941
2978
  }
2942
2979
 
2943
2980
  //#endregion
2944
- export { DailyUsageSchema, SessionUsageSchema, UsageDataSchema, __commonJS, __require, __toESM, calculateCostForEntry, formatDate, getDefaultClaudePath, glob, loadSessionData, loadUsageData };
2981
+ export { DailyUsageSchema, MonthlyUsageSchema, SessionUsageSchema, UsageDataSchema, __commonJS, __require, __toESM, calculateCostForEntry, formatDate, getDefaultClaudePath, glob, loadDailyUsageData, loadMonthlyUsageData, loadSessionData };
@@ -1,6 +1,6 @@
1
- import { ArraySchema, InferOutput, NumberSchema, ObjectSchema, OptionalSchema, StringSchema } from "./index-BurjgCfW.js";
1
+ import { ArraySchema, InferOutput, NumberSchema, ObjectSchema, OptionalSchema, RegexAction, SchemaWithPipe, StringSchema } from "./index-BurjgCfW.js";
2
2
  import { ModelPricing } from "./pricing-fetcher-DygIroMj.js";
3
- import { CostMode } from "./types-B3ib19os.js";
3
+ import { CostMode, SortOrder } from "./types-y1JQzaKZ.js";
4
4
 
5
5
  //#region src/data-loader.d.ts
6
6
  declare const getDefaultClaudePath: () => string;
@@ -20,7 +20,7 @@ declare const UsageDataSchema: ObjectSchema<{
20
20
  }, undefined>;
21
21
  type UsageData = InferOutput<typeof UsageDataSchema>;
22
22
  declare const DailyUsageSchema: ObjectSchema<{
23
- readonly date: StringSchema<undefined>;
23
+ readonly date: SchemaWithPipe<readonly [StringSchema<undefined>, RegexAction<string, undefined>]>;
24
24
  readonly inputTokens: NumberSchema<undefined>;
25
25
  readonly outputTokens: NumberSchema<undefined>;
26
26
  readonly cacheCreationTokens: NumberSchema<undefined>;
@@ -40,6 +40,15 @@ declare const SessionUsageSchema: ObjectSchema<{
40
40
  readonly versions: ArraySchema<StringSchema<undefined>, undefined>;
41
41
  }, undefined>;
42
42
  type SessionUsage = InferOutput<typeof SessionUsageSchema>;
43
+ declare const MonthlyUsageSchema: ObjectSchema<{
44
+ readonly month: SchemaWithPipe<readonly [StringSchema<undefined>, RegexAction<string, undefined>]>;
45
+ readonly inputTokens: NumberSchema<undefined>;
46
+ readonly outputTokens: NumberSchema<undefined>;
47
+ readonly cacheCreationTokens: NumberSchema<undefined>;
48
+ readonly cacheReadTokens: NumberSchema<undefined>;
49
+ readonly totalCost: NumberSchema<undefined>;
50
+ }, undefined>;
51
+ type MonthlyUsage = InferOutput<typeof MonthlyUsageSchema>;
43
52
  declare const formatDate: (dateStr: string) => string;
44
53
  declare const calculateCostForEntry: (data: UsageData, mode: CostMode, modelPricing: Record<string, ModelPricing>) => number;
45
54
  interface DateFilter {
@@ -49,8 +58,10 @@ interface DateFilter {
49
58
  interface LoadOptions extends DateFilter {
50
59
  claudePath?: string;
51
60
  mode?: CostMode;
61
+ order?: SortOrder;
52
62
  }
53
- declare function loadUsageData(options?: LoadOptions): Promise<DailyUsage[]>;
63
+ declare function loadDailyUsageData(options?: LoadOptions): Promise<DailyUsage[]>;
54
64
  declare function loadSessionData(options?: LoadOptions): Promise<SessionUsage[]>;
65
+ declare function loadMonthlyUsageData(options?: LoadOptions): Promise<MonthlyUsage[]>;
55
66
  //#endregion
56
- export { DailyUsage, DailyUsageSchema as DailyUsageSchema$1, DateFilter, LoadOptions, SessionUsage, SessionUsageSchema as SessionUsageSchema$1, UsageData, UsageDataSchema as UsageDataSchema$1, calculateCostForEntry as calculateCostForEntry$1, formatDate as formatDate$1, getDefaultClaudePath as getDefaultClaudePath$1, loadSessionData as loadSessionData$1, loadUsageData as loadUsageData$1 };
67
+ export { DailyUsage, DailyUsageSchema as DailyUsageSchema$1, DateFilter, LoadOptions, MonthlyUsage, MonthlyUsageSchema as MonthlyUsageSchema$1, SessionUsage, SessionUsageSchema as SessionUsageSchema$1, UsageData, UsageDataSchema as UsageDataSchema$1, calculateCostForEntry as calculateCostForEntry$1, formatDate as formatDate$1, getDefaultClaudePath as getDefaultClaudePath$1, loadDailyUsageData as loadDailyUsageData$1, loadMonthlyUsageData as loadMonthlyUsageData$1, loadSessionData as loadSessionData$1 };
@@ -1,5 +1,5 @@
1
1
  import "./index-BurjgCfW.js";
2
2
  import "./pricing-fetcher-DygIroMj.js";
3
- import "./types-B3ib19os.js";
4
- import { DailyUsage, DailyUsageSchema$1 as DailyUsageSchema, DateFilter, LoadOptions, SessionUsage, SessionUsageSchema$1 as SessionUsageSchema, UsageData, UsageDataSchema$1 as UsageDataSchema, calculateCostForEntry$1 as calculateCostForEntry, formatDate$1 as formatDate, getDefaultClaudePath$1 as getDefaultClaudePath, loadSessionData$1 as loadSessionData, loadUsageData$1 as loadUsageData } from "./data-loader-LMCrJ-lW.js";
5
- export { DailyUsage, DailyUsageSchema, DateFilter, LoadOptions, SessionUsage, SessionUsageSchema, UsageData, UsageDataSchema, calculateCostForEntry, formatDate, getDefaultClaudePath, loadSessionData, loadUsageData };
3
+ import "./types-y1JQzaKZ.js";
4
+ import { DailyUsage, DailyUsageSchema$1 as DailyUsageSchema, DateFilter, LoadOptions, MonthlyUsage, MonthlyUsageSchema$1 as MonthlyUsageSchema, SessionUsage, SessionUsageSchema$1 as SessionUsageSchema, UsageData, UsageDataSchema$1 as UsageDataSchema, calculateCostForEntry$1 as calculateCostForEntry, formatDate$1 as formatDate, getDefaultClaudePath$1 as getDefaultClaudePath, loadDailyUsageData$1 as loadDailyUsageData, loadMonthlyUsageData$1 as loadMonthlyUsageData, loadSessionData$1 as loadSessionData } from "./data-loader-aUOjeZ06.js";
5
+ export { DailyUsage, DailyUsageSchema, DateFilter, LoadOptions, MonthlyUsage, MonthlyUsageSchema, SessionUsage, SessionUsageSchema, UsageData, UsageDataSchema, calculateCostForEntry, formatDate, getDefaultClaudePath, loadDailyUsageData, loadMonthlyUsageData, loadSessionData };
@@ -1,6 +1,6 @@
1
- import { DailyUsageSchema, SessionUsageSchema, UsageDataSchema, calculateCostForEntry, formatDate, getDefaultClaudePath, loadSessionData, loadUsageData } from "./data-loader-r5ZcMQy7.js";
1
+ import { DailyUsageSchema, MonthlyUsageSchema, SessionUsageSchema, UsageDataSchema, calculateCostForEntry, formatDate, getDefaultClaudePath, loadDailyUsageData, loadMonthlyUsageData, loadSessionData } from "./data-loader-CDv0IYZx.js";
2
2
  import "./dist-FwNhpFrW.js";
3
- import "./logger-Cu4Ir1a5.js";
4
- import "./pricing-fetcher-B5yPtoTB.js";
3
+ import "./logger-DhDyJEC5.js";
4
+ import "./pricing-fetcher-BCv1Vods.js";
5
5
 
6
- export { DailyUsageSchema, SessionUsageSchema, UsageDataSchema, calculateCostForEntry, formatDate, getDefaultClaudePath, loadSessionData, loadUsageData };
6
+ export { DailyUsageSchema, MonthlyUsageSchema, SessionUsageSchema, UsageDataSchema, calculateCostForEntry, formatDate, getDefaultClaudePath, loadDailyUsageData, loadMonthlyUsageData, loadSessionData };
@@ -1,7 +1,7 @@
1
- import { UsageDataSchema, glob } from "./data-loader-r5ZcMQy7.js";
1
+ import { UsageDataSchema, glob } from "./data-loader-CDv0IYZx.js";
2
2
  import { safeParse } from "./dist-FwNhpFrW.js";
3
- import { logger } from "./logger-Cu4Ir1a5.js";
4
- import { calculateCostFromTokens, fetchModelPricing, getModelPricing } from "./pricing-fetcher-B5yPtoTB.js";
3
+ import { logger } from "./logger-DhDyJEC5.js";
4
+ import { calculateCostFromTokens, fetchModelPricing, getModelPricing } from "./pricing-fetcher-BCv1Vods.js";
5
5
  import { readFile } from "node:fs/promises";
6
6
  import { homedir } from "node:os";
7
7
  import path from "node:path";
package/dist/debug.js CHANGED
@@ -1,7 +1,7 @@
1
- import "./data-loader-r5ZcMQy7.js";
1
+ import "./data-loader-CDv0IYZx.js";
2
2
  import "./dist-FwNhpFrW.js";
3
- import "./logger-Cu4Ir1a5.js";
4
- import "./pricing-fetcher-B5yPtoTB.js";
5
- import { detectMismatches, printMismatchReport } from "./debug-BVxGf4UL.js";
3
+ import "./logger-DhDyJEC5.js";
4
+ import "./pricing-fetcher-BCv1Vods.js";
5
+ import { detectMismatches, printMismatchReport } from "./debug-Dk36WQTw.js";
6
6
 
7
7
  export { detectMismatches, printMismatchReport };
package/dist/index.js CHANGED
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import { __commonJS, __require, __toESM, loadSessionData, loadUsageData } from "./data-loader-r5ZcMQy7.js";
2
+ import { __commonJS, __require, __toESM, loadDailyUsageData, loadMonthlyUsageData, loadSessionData } from "./data-loader-CDv0IYZx.js";
3
3
  import { calculateTotals, createTotalsObject, getTotalTokens } from "./calculate-cost-2IwHSzmi.js";
4
4
  import "./dist-FwNhpFrW.js";
5
- import { description, log, logger, name, version } from "./logger-Cu4Ir1a5.js";
6
- import "./pricing-fetcher-B5yPtoTB.js";
7
- import { detectMismatches, printMismatchReport } from "./debug-BVxGf4UL.js";
8
- import "./types-DFrbJmnT.js";
9
- import { sharedArgs, sharedCommandConfig } from "./shared-args-DN3jRldX.js";
5
+ import { description, log, logger, name, version } from "./logger-DhDyJEC5.js";
6
+ import "./pricing-fetcher-BCv1Vods.js";
7
+ import { detectMismatches, printMismatchReport } from "./debug-Dk36WQTw.js";
8
+ import "./types-BcXIBMQk.js";
9
+ import { sharedArgs, sharedCommandConfig } from "./shared-args-BtMSktLn.js";
10
10
  import { formatCurrency, formatNumber } from "./utils-C7kg8MXN.js";
11
11
  import "./types-CFnCBr2I.js";
12
- import { createMcpServer } from "./mcp-DAzj5Pua.js";
12
+ import { createMcpServer } from "./mcp-G-TIOcuj.js";
13
13
  import "./index-CISmcbXk-BotItq1T.js";
14
14
  import process$1 from "node:process";
15
15
 
@@ -2729,7 +2729,7 @@ var require_table = __commonJS({ "node_modules/cli-table3/src/table.js"(exports,
2729
2729
  const debug = require_debug$1();
2730
2730
  const utils = require_utils$1();
2731
2731
  const tableLayout = require_layout_manager();
2732
- var Table$2 = class extends Array {
2732
+ var Table$3 = class extends Array {
2733
2733
  constructor(opts) {
2734
2734
  super();
2735
2735
  const options = utils.mergeOptions(opts);
@@ -2792,7 +2792,7 @@ var require_table = __commonJS({ "node_modules/cli-table3/src/table.js"(exports,
2792
2792
  return str[0].length;
2793
2793
  }
2794
2794
  };
2795
- Table$2.reset = () => debug.reset();
2795
+ Table$3.reset = () => debug.reset();
2796
2796
  function doDraw(row, lineNum, result) {
2797
2797
  let line = [];
2798
2798
  row.forEach(function(cell) {
@@ -2801,7 +2801,7 @@ var require_table = __commonJS({ "node_modules/cli-table3/src/table.js"(exports,
2801
2801
  let str = line.join("");
2802
2802
  if (str.length) result.push(str);
2803
2803
  }
2804
- module.exports = Table$2;
2804
+ module.exports = Table$3;
2805
2805
  } });
2806
2806
 
2807
2807
  //#endregion
@@ -2881,21 +2881,21 @@ var require_picocolors = __commonJS({ "node_modules/picocolors/picocolors.js"(ex
2881
2881
 
2882
2882
  //#endregion
2883
2883
  //#region src/commands/daily.ts
2884
- var import_cli_table3$1 = __toESM(require_cli_table3(), 1);
2885
- var import_picocolors$1 = __toESM(require_picocolors(), 1);
2884
+ var import_cli_table3$2 = __toESM(require_cli_table3(), 1);
2885
+ var import_picocolors$2 = __toESM(require_picocolors(), 1);
2886
2886
  const dailyCommand = define({
2887
2887
  name: "daily",
2888
2888
  description: "Show usage report grouped by date",
2889
2889
  ...sharedCommandConfig,
2890
2890
  async run(ctx) {
2891
2891
  if (ctx.values.json) logger.level = 0;
2892
- const options = {
2892
+ const dailyData = await loadDailyUsageData({
2893
2893
  since: ctx.values.since,
2894
2894
  until: ctx.values.until,
2895
2895
  claudePath: ctx.values.path,
2896
- mode: ctx.values.mode
2897
- };
2898
- const dailyData = await loadUsageData(options);
2896
+ mode: ctx.values.mode,
2897
+ order: ctx.values.order
2898
+ });
2899
2899
  if (dailyData.length === 0) {
2900
2900
  if (ctx.values.json) log(JSON.stringify([]));
2901
2901
  else logger.warn("No Claude usage data found.");
@@ -2922,7 +2922,7 @@ const dailyCommand = define({
2922
2922
  log(JSON.stringify(jsonOutput, null, 2));
2923
2923
  } else {
2924
2924
  logger.box("Claude Code Token Usage Report - Daily");
2925
- const table = new import_cli_table3$1.default({
2925
+ const table = new import_cli_table3$2.default({
2926
2926
  head: [
2927
2927
  "Date",
2928
2928
  "Input",
@@ -2962,13 +2962,13 @@ const dailyCommand = define({
2962
2962
  "─".repeat(10)
2963
2963
  ]);
2964
2964
  table.push([
2965
- import_picocolors$1.default.yellow("Total"),
2966
- import_picocolors$1.default.yellow(formatNumber(totals.inputTokens)),
2967
- import_picocolors$1.default.yellow(formatNumber(totals.outputTokens)),
2968
- import_picocolors$1.default.yellow(formatNumber(totals.cacheCreationTokens)),
2969
- import_picocolors$1.default.yellow(formatNumber(totals.cacheReadTokens)),
2970
- import_picocolors$1.default.yellow(formatNumber(getTotalTokens(totals))),
2971
- import_picocolors$1.default.yellow(formatCurrency(totals.totalCost))
2965
+ import_picocolors$2.default.yellow("Total"),
2966
+ import_picocolors$2.default.yellow(formatNumber(totals.inputTokens)),
2967
+ import_picocolors$2.default.yellow(formatNumber(totals.outputTokens)),
2968
+ import_picocolors$2.default.yellow(formatNumber(totals.cacheCreationTokens)),
2969
+ import_picocolors$2.default.yellow(formatNumber(totals.cacheReadTokens)),
2970
+ import_picocolors$2.default.yellow(formatNumber(getTotalTokens(totals))),
2971
+ import_picocolors$2.default.yellow(formatCurrency(totals.totalCost))
2972
2972
  ]);
2973
2973
  log(table.toString());
2974
2974
  }
@@ -3010,6 +3010,114 @@ const mcpCommand = define({
3010
3010
  }
3011
3011
  });
3012
3012
 
3013
+ //#endregion
3014
+ //#region src/commands/monthly.ts
3015
+ var import_cli_table3$1 = __toESM(require_cli_table3(), 1);
3016
+ var import_picocolors$1 = __toESM(require_picocolors(), 1);
3017
+ const monthlyCommand = define({
3018
+ name: "monthly",
3019
+ description: "Show usage report grouped by month",
3020
+ ...sharedCommandConfig,
3021
+ async run(ctx) {
3022
+ if (ctx.values.json) logger.level = 0;
3023
+ const monthlyData = await loadMonthlyUsageData({
3024
+ since: ctx.values.since,
3025
+ until: ctx.values.until,
3026
+ claudePath: ctx.values.path,
3027
+ mode: ctx.values.mode,
3028
+ order: ctx.values.order
3029
+ });
3030
+ if (monthlyData.length === 0) {
3031
+ if (ctx.values.json) {
3032
+ const emptyOutput = {
3033
+ monthly: [],
3034
+ totals: {
3035
+ inputTokens: 0,
3036
+ outputTokens: 0,
3037
+ cacheCreationTokens: 0,
3038
+ cacheReadTokens: 0,
3039
+ totalTokens: 0,
3040
+ totalCost: 0
3041
+ }
3042
+ };
3043
+ log(JSON.stringify(emptyOutput, null, 2));
3044
+ } else logger.warn("No Claude usage data found.");
3045
+ process$1.exit(0);
3046
+ }
3047
+ const totals = calculateTotals(monthlyData);
3048
+ if (ctx.values.debug && !ctx.values.json) {
3049
+ const mismatchStats = await detectMismatches(ctx.values.path);
3050
+ printMismatchReport(mismatchStats, ctx.values.debugSamples);
3051
+ }
3052
+ if (ctx.values.json) {
3053
+ const jsonOutput = {
3054
+ monthly: monthlyData.map((data) => ({
3055
+ month: data.month,
3056
+ inputTokens: data.inputTokens,
3057
+ outputTokens: data.outputTokens,
3058
+ cacheCreationTokens: data.cacheCreationTokens,
3059
+ cacheReadTokens: data.cacheReadTokens,
3060
+ totalTokens: getTotalTokens(data),
3061
+ totalCost: data.totalCost
3062
+ })),
3063
+ totals: createTotalsObject(totals)
3064
+ };
3065
+ log(JSON.stringify(jsonOutput, null, 2));
3066
+ } else {
3067
+ logger.box("Claude Code Token Usage Report - Monthly");
3068
+ const table = new import_cli_table3$1.default({
3069
+ head: [
3070
+ "Month",
3071
+ "Input",
3072
+ "Output",
3073
+ "Cache Create",
3074
+ "Cache Read",
3075
+ "Total Tokens",
3076
+ "Cost (USD)"
3077
+ ],
3078
+ style: { head: ["cyan"] },
3079
+ colAligns: [
3080
+ "left",
3081
+ "right",
3082
+ "right",
3083
+ "right",
3084
+ "right",
3085
+ "right",
3086
+ "right"
3087
+ ]
3088
+ });
3089
+ for (const data of monthlyData) table.push([
3090
+ data.month,
3091
+ formatNumber(data.inputTokens),
3092
+ formatNumber(data.outputTokens),
3093
+ formatNumber(data.cacheCreationTokens),
3094
+ formatNumber(data.cacheReadTokens),
3095
+ formatNumber(getTotalTokens(data)),
3096
+ formatCurrency(data.totalCost)
3097
+ ]);
3098
+ table.push([
3099
+ "─".repeat(12),
3100
+ "─".repeat(12),
3101
+ "─".repeat(12),
3102
+ "─".repeat(12),
3103
+ "─".repeat(12),
3104
+ "─".repeat(12),
3105
+ "─".repeat(10)
3106
+ ]);
3107
+ table.push([
3108
+ import_picocolors$1.default.yellow("Total"),
3109
+ import_picocolors$1.default.yellow(formatNumber(totals.inputTokens)),
3110
+ import_picocolors$1.default.yellow(formatNumber(totals.outputTokens)),
3111
+ import_picocolors$1.default.yellow(formatNumber(totals.cacheCreationTokens)),
3112
+ import_picocolors$1.default.yellow(formatNumber(totals.cacheReadTokens)),
3113
+ import_picocolors$1.default.yellow(formatNumber(getTotalTokens(totals))),
3114
+ import_picocolors$1.default.yellow(formatCurrency(totals.totalCost))
3115
+ ]);
3116
+ log(table.toString());
3117
+ }
3118
+ }
3119
+ });
3120
+
3013
3121
  //#endregion
3014
3122
  //#region src/commands/session.ts
3015
3123
  var import_cli_table3 = __toESM(require_cli_table3(), 1);
@@ -3020,13 +3128,13 @@ const sessionCommand = define({
3020
3128
  ...sharedCommandConfig,
3021
3129
  async run(ctx) {
3022
3130
  if (ctx.values.json) logger.level = 0;
3023
- const options = {
3131
+ const sessionData = await loadSessionData({
3024
3132
  since: ctx.values.since,
3025
3133
  until: ctx.values.until,
3026
3134
  claudePath: ctx.values.path,
3027
- mode: ctx.values.mode
3028
- };
3029
- const sessionData = await loadSessionData(options);
3135
+ mode: ctx.values.mode,
3136
+ order: ctx.values.order
3137
+ });
3030
3138
  if (sessionData.length === 0) {
3031
3139
  if (ctx.values.json) log(JSON.stringify([]));
3032
3140
  else logger.warn("No Claude usage data found.");
@@ -3130,6 +3238,7 @@ const sessionCommand = define({
3130
3238
  //#region src/commands/index.ts
3131
3239
  const subCommands = new Map();
3132
3240
  subCommands.set("daily", dailyCommand);
3241
+ subCommands.set("monthly", monthlyCommand);
3133
3242
  subCommands.set("session", sessionCommand);
3134
3243
  subCommands.set("mcp", mcpCommand);
3135
3244
  const mainCommand = dailyCommand;
@@ -965,7 +965,7 @@ const consola = createConsola();
965
965
  //#endregion
966
966
  //#region package.json
967
967
  var name = "ccusage";
968
- var version = "0.5.0";
968
+ var version = "0.6.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-Cu4Ir1a5.js";
1
+ import { log, logger } from "./logger-DhDyJEC5.js";
2
2
 
3
3
  export { log, logger };
@@ -1,7 +1,7 @@
1
- import { __commonJS, __require, __toESM, getDefaultClaudePath, loadSessionData, loadUsageData } from "./data-loader-r5ZcMQy7.js";
1
+ import { __commonJS, __require, __toESM, getDefaultClaudePath, loadDailyUsageData, loadSessionData } from "./data-loader-CDv0IYZx.js";
2
2
  import { description$1 as description, literal, object, optional, pipe, union } from "./dist-FwNhpFrW.js";
3
- import { name, version } from "./logger-Cu4Ir1a5.js";
4
- import { CostModes, dateSchema } from "./types-DFrbJmnT.js";
3
+ import { name, version } from "./logger-DhDyJEC5.js";
4
+ import { CostModes, dateSchema } from "./types-BcXIBMQk.js";
5
5
  import { anyType, arrayType, booleanType, discriminatedUnionType, enumType, literalType, numberType, objectType, optionalType, recordType, stringType, unionType, unknownType } from "./types-CFnCBr2I.js";
6
6
  import { toJsonSchema } from "./index-CISmcbXk-BotItq1T.js";
7
7
  import process$1 from "node:process";
@@ -37554,7 +37554,7 @@ function createMcpServer({ claudePath } = defaultOptions) {
37554
37554
  description: "Show usage report grouped by date",
37555
37555
  parameters: parametersSchema,
37556
37556
  execute: async (args) => {
37557
- const dailyData = await loadUsageData({
37557
+ const dailyData = await loadDailyUsageData({
37558
37558
  ...args,
37559
37559
  claudePath
37560
37560
  });
package/dist/mcp.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import "./index-BurjgCfW.js";
2
2
  import "./pricing-fetcher-DygIroMj.js";
3
- import "./types-B3ib19os.js";
4
- import { LoadOptions } from "./data-loader-LMCrJ-lW.js";
3
+ import "./types-y1JQzaKZ.js";
4
+ import { LoadOptions } from "./data-loader-aUOjeZ06.js";
5
5
  import { FastMCP } from "fastmcp";
6
6
 
7
7
  //#region src/mcp.d.ts
package/dist/mcp.js CHANGED
@@ -1,10 +1,10 @@
1
- import "./data-loader-r5ZcMQy7.js";
1
+ import "./data-loader-CDv0IYZx.js";
2
2
  import "./dist-FwNhpFrW.js";
3
- import "./logger-Cu4Ir1a5.js";
4
- import "./pricing-fetcher-B5yPtoTB.js";
5
- import "./types-DFrbJmnT.js";
3
+ import "./logger-DhDyJEC5.js";
4
+ import "./pricing-fetcher-BCv1Vods.js";
5
+ import "./types-BcXIBMQk.js";
6
6
  import "./types-CFnCBr2I.js";
7
- import { createMcpServer } from "./mcp-DAzj5Pua.js";
7
+ import { createMcpServer } from "./mcp-G-TIOcuj.js";
8
8
  import "./index-CISmcbXk-BotItq1T.js";
9
9
 
10
10
  export { createMcpServer };
@@ -1,5 +1,5 @@
1
1
  import { number, object, optional, parse } from "./dist-FwNhpFrW.js";
2
- import { logger } from "./logger-Cu4Ir1a5.js";
2
+ import { logger } from "./logger-DhDyJEC5.js";
3
3
 
4
4
  //#region src/pricing-fetcher.ts
5
5
  const ModelPricingSchema = object({
@@ -1,5 +1,5 @@
1
1
  import "./dist-FwNhpFrW.js";
2
- import "./logger-Cu4Ir1a5.js";
3
- import { calculateCostFromTokens, clearPricingCache, fetchModelPricing, getModelPricing } from "./pricing-fetcher-B5yPtoTB.js";
2
+ import "./logger-DhDyJEC5.js";
3
+ import { calculateCostFromTokens, clearPricingCache, fetchModelPricing, getModelPricing } from "./pricing-fetcher-BCv1Vods.js";
4
4
 
5
5
  export { calculateCostFromTokens, clearPricingCache, fetchModelPricing, getModelPricing };
@@ -1,6 +1,6 @@
1
- import { getDefaultClaudePath } from "./data-loader-r5ZcMQy7.js";
1
+ import { getDefaultClaudePath } from "./data-loader-CDv0IYZx.js";
2
2
  import { safeParse } from "./dist-FwNhpFrW.js";
3
- import { CostModes, dateSchema } from "./types-DFrbJmnT.js";
3
+ import { CostModes, SortOrders, dateSchema } from "./types-BcXIBMQk.js";
4
4
 
5
5
  //#region src/shared-args.ts
6
6
  const parseDateArg = (value) => {
@@ -50,6 +50,13 @@ const sharedArgs = {
50
50
  type: "number",
51
51
  description: "Number of sample discrepancies to show in debug output (default: 5)",
52
52
  default: 5
53
+ },
54
+ order: {
55
+ type: "enum",
56
+ short: "o",
57
+ description: "Sort order: desc (newest first) or asc (oldest first)",
58
+ default: "asc",
59
+ choices: SortOrders
53
60
  }
54
61
  };
55
62
  const sharedCommandConfig = {
@@ -42,6 +42,13 @@ declare const sharedArgs: {
42
42
  readonly description: "Number of sample discrepancies to show in debug output (default: 5)";
43
43
  readonly default: 5;
44
44
  };
45
+ readonly order: {
46
+ readonly type: "enum";
47
+ readonly short: "o";
48
+ readonly description: "Sort order: desc (newest first) or asc (oldest first)";
49
+ readonly default: "asc";
50
+ readonly choices: readonly ["desc", "asc"];
51
+ };
45
52
  };
46
53
  declare const sharedCommandConfig: {
47
54
  readonly args: {
@@ -87,6 +94,13 @@ declare const sharedCommandConfig: {
87
94
  readonly description: "Number of sample discrepancies to show in debug output (default: 5)";
88
95
  readonly default: 5;
89
96
  };
97
+ readonly order: {
98
+ readonly type: "enum";
99
+ readonly short: "o";
100
+ readonly description: "Sort order: desc (newest first) or asc (oldest first)";
101
+ readonly default: "asc";
102
+ readonly choices: readonly ["desc", "asc"];
103
+ };
90
104
  };
91
105
  readonly toKebab: true;
92
106
  };
@@ -1,8 +1,8 @@
1
- import "./data-loader-r5ZcMQy7.js";
1
+ import "./data-loader-CDv0IYZx.js";
2
2
  import "./dist-FwNhpFrW.js";
3
- import "./logger-Cu4Ir1a5.js";
4
- import "./pricing-fetcher-B5yPtoTB.js";
5
- import "./types-DFrbJmnT.js";
6
- import { sharedArgs, sharedCommandConfig } from "./shared-args-DN3jRldX.js";
3
+ import "./logger-DhDyJEC5.js";
4
+ import "./pricing-fetcher-BCv1Vods.js";
5
+ import "./types-BcXIBMQk.js";
6
+ import { sharedArgs, sharedCommandConfig } from "./shared-args-BtMSktLn.js";
7
7
 
8
8
  export { sharedArgs, sharedCommandConfig };
@@ -36,6 +36,7 @@ const CostModes = [
36
36
  "calculate",
37
37
  "display"
38
38
  ];
39
+ const SortOrders = ["desc", "asc"];
39
40
 
40
41
  //#endregion
41
- export { CostModes, LiteLLMModelPricesSchema, ModelSpecSchema, dateSchema };
42
+ export { CostModes, LiteLLMModelPricesSchema, ModelSpecSchema, SortOrders, dateSchema };
@@ -75,5 +75,7 @@ interface TokenData {
75
75
  }
76
76
  declare const CostModes: readonly ["auto", "calculate", "display"];
77
77
  type CostMode = (typeof CostModes)[number];
78
+ declare const SortOrders: readonly ["desc", "asc"];
79
+ type SortOrder = (typeof SortOrders)[number];
78
80
  //#endregion
79
- export { CostMode, CostModes as CostModes$1, LiteLLMModelPrices, LiteLLMModelPricesSchema as LiteLLMModelPricesSchema$1, ModelSpec, ModelSpecSchema as ModelSpecSchema$1, TokenData, TokenTotals, dateSchema as dateSchema$1 };
81
+ export { CostMode, CostModes as CostModes$1, LiteLLMModelPrices, LiteLLMModelPricesSchema as LiteLLMModelPricesSchema$1, ModelSpec, ModelSpecSchema as ModelSpecSchema$1, SortOrder, SortOrders as SortOrders$1, TokenData, TokenTotals, dateSchema as dateSchema$1 };
package/dist/types.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import "./index-BurjgCfW.js";
2
- import { CostMode, CostModes$1 as CostModes, LiteLLMModelPrices, LiteLLMModelPricesSchema$1 as LiteLLMModelPricesSchema, ModelSpec, ModelSpecSchema$1 as ModelSpecSchema, TokenData, TokenTotals, dateSchema$1 as dateSchema } from "./types-B3ib19os.js";
3
- export { CostMode, CostModes, LiteLLMModelPrices, LiteLLMModelPricesSchema, ModelSpec, ModelSpecSchema, TokenData, TokenTotals, dateSchema };
2
+ import { CostMode, CostModes$1 as CostModes, LiteLLMModelPrices, LiteLLMModelPricesSchema$1 as LiteLLMModelPricesSchema, ModelSpec, ModelSpecSchema$1 as ModelSpecSchema, SortOrder, SortOrders$1 as SortOrders, TokenData, TokenTotals, dateSchema$1 as dateSchema } from "./types-y1JQzaKZ.js";
3
+ export { CostMode, CostModes, LiteLLMModelPrices, LiteLLMModelPricesSchema, ModelSpec, ModelSpecSchema, SortOrder, SortOrders, TokenData, TokenTotals, dateSchema };
package/dist/types.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import "./dist-FwNhpFrW.js";
2
- import { CostModes, LiteLLMModelPricesSchema, ModelSpecSchema, dateSchema } from "./types-DFrbJmnT.js";
2
+ import { CostModes, LiteLLMModelPricesSchema, ModelSpecSchema, SortOrders, dateSchema } from "./types-BcXIBMQk.js";
3
3
 
4
- export { CostModes, LiteLLMModelPricesSchema, ModelSpecSchema, dateSchema };
4
+ export { CostModes, LiteLLMModelPricesSchema, ModelSpecSchema, SortOrders, dateSchema };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccusage",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Usage analysis tool for Claude Code",
5
5
  "homepage": "https://github.com/ryoppippi/ccusage#readme",
6
6
  "bugs": {