ccusage 15.5.2 → 15.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
@@ -9,6 +9,7 @@
9
9
  <a href="https://packagephobia.com/result?p=ccusage"><img src="https://packagephobia.com/badge?p=ccusage" alt="install size" /></a>
10
10
  <a href="https://deepwiki.com/ryoppippi/ccusage"><img src="https://img.shields.io/badge/DeepWiki-ryoppippi%2Fccusage-blue.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAyCAYAAAAnWDnqAAAAAXNSR0IArs4c6QAAA05JREFUaEPtmUtyEzEQhtWTQyQLHNak2AB7ZnyXZMEjXMGeK/AIi+QuHrMnbChYY7MIh8g01fJoopFb0uhhEqqcbWTp06/uv1saEDv4O3n3dV60RfP947Mm9/SQc0ICFQgzfc4CYZoTPAswgSJCCUJUnAAoRHOAUOcATwbmVLWdGoH//PB8mnKqScAhsD0kYP3j/Yt5LPQe2KvcXmGvRHcDnpxfL2zOYJ1mFwrryWTz0advv1Ut4CJgf5uhDuDj5eUcAUoahrdY/56ebRWeraTjMt/00Sh3UDtjgHtQNHwcRGOC98BJEAEymycmYcWwOprTgcB6VZ5JK5TAJ+fXGLBm3FDAmn6oPPjR4rKCAoJCal2eAiQp2x0vxTPB3ALO2CRkwmDy5WohzBDwSEFKRwPbknEggCPB/imwrycgxX2NzoMCHhPkDwqYMr9tRcP5qNrMZHkVnOjRMWwLCcr8ohBVb1OMjxLwGCvjTikrsBOiA6fNyCrm8V1rP93iVPpwaE+gO0SsWmPiXB+jikdf6SizrT5qKasx5j8ABbHpFTx+vFXp9EnYQmLx02h1QTTrl6eDqxLnGjporxl3NL3agEvXdT0WmEost648sQOYAeJS9Q7bfUVoMGnjo4AZdUMQku50McDcMWcBPvr0SzbTAFDfvJqwLzgxwATnCgnp4wDl6Aa+Ax283gghmj+vj7feE2KBBRMW3FzOpLOADl0Isb5587h/U4gGvkt5v60Z1VLG8BhYjbzRwyQZemwAd6cCR5/XFWLYZRIMpX39AR0tjaGGiGzLVyhse5C9RKC6ai42ppWPKiBagOvaYk8lO7DajerabOZP46Lby5wKjw1HCRx7p9sVMOWGzb/vA1hwiWc6jm3MvQDTogQkiqIhJV0nBQBTU+3okKCFDy9WwferkHjtxib7t3xIUQtHxnIwtx4mpg26/HfwVNVDb4oI9RHmx5WGelRVlrtiw43zboCLaxv46AZeB3IlTkwouebTr1y2NjSpHz68WNFjHvupy3q8TFn3Hos2IAk4Ju5dCo8B3wP7VPr/FGaKiG+T+v+TQqIrOqMTL1VdWV1DdmcbO8KXBz6esmYWYKPwDL5b5FA1a0hwapHiom0r/cKaoqr+27/XcrS5UwSMbQAAAABJRU5ErkJggg==" alt="DeepWiki"></a>
11
11
  <!-- DeepWiki badge generated by https://deepwiki.ryoppippi.com/ -->
12
+ <a href="https://claudelog.com/"><img src="https://claudelog.com/img/claude_log_badge.svg" alt="ClaudeLog - A comprehensive knowledge base for Claude." /></a>
12
13
  <a href="https://github.com/hesreallyhim/awesome-claude-code"><img src="https://awesome.re/mentioned-badge.svg" alt="Mentioned in Awesome Claude Code" /></a>
13
14
  </p>
14
15
 
@@ -113,6 +114,16 @@ Check out these [47 Claude Code ProTips from Greg Baugues.](https://www.youtube.
113
114
  </a>
114
115
  </p>
115
116
 
117
+ ## Claude Code Resources
118
+
119
+ [`ClaudeLog`](https://claudelog.com) &nbsp; by &nbsp; [InventorBlack](https://www.reddit.com/user/inventor_black/)
120
+ A comprehensive knowledge base with detailed breakdowns of advanced topics, including:
121
+
122
+ - Advanced [mechanics](https://claudelog.com/mechanics/you-are-the-main-thread/) and [CLAUDE.md best practices](https://claudelog.com/mechanics/claude-md-supremacy).
123
+ - Practical technique guides for [plan mode](https://claudelog.com/mechanics/plan-mode), [ultrathink](https://claudelog.com/faqs/what-is-ultrathink/), and [sub-agents](https://claudelog.com/mechanics/task-agent-tools/).
124
+ - Concepts like [agent-first design](https://claudelog.com/mechanics/agent-first-design/), [agent engineering](https://claudelog.com/mechanics/agent-engineering/), and [humanizing agents](https://claudelog.com/mechanics/humanising-agents/).
125
+ - [Configuration guides](https://claudelog.com/configuration).
126
+
116
127
  ## Star History
117
128
 
118
129
  <a href="https://www.star-history.com/#ryoppippi/ccusage&Date">
@@ -725,7 +725,7 @@ const ipv6CidrRegex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1
725
725
  const base64Regex = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/;
726
726
  const base64urlRegex = /^([0-9a-zA-Z-_]{4})*(([0-9a-zA-Z-_]{2}(==)?)|([0-9a-zA-Z-_]{3}(=)?))?$/;
727
727
  const dateRegexSource = `((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\d|30)|(02)-(0[1-9]|1\\d|2[0-8])))`;
728
- const dateRegex = /* @__PURE__ */ new RegExp(`^${dateRegexSource}$`);
728
+ const dateRegex = new RegExp(`^${dateRegexSource}$`);
729
729
  function timeRegexSource(args) {
730
730
  let secondsRegexSource = `[0-5]\\d`;
731
731
  if (args.precision) secondsRegexSource = `${secondsRegexSource}\\.\\d{${args.precision}}`;
@@ -734,7 +734,7 @@ function timeRegexSource(args) {
734
734
  return `([01]\\d|2[0-3]):[0-5]\\d(:${secondsRegexSource})${secondsQuantifier}`;
735
735
  }
736
736
  function timeRegex(args) {
737
- return /* @__PURE__ */ new RegExp(`^${timeRegexSource(args)}$`);
737
+ return new RegExp(`^${timeRegexSource(args)}$`);
738
738
  }
739
739
  function datetimeRegex(args) {
740
740
  let regex = `${dateRegexSource}T${timeRegexSource(args)}`;
@@ -742,7 +742,7 @@ function datetimeRegex(args) {
742
742
  opts.push(args.local ? `Z?` : `Z`);
743
743
  if (args.offset) opts.push(`([+-]\\d{2}:?\\d{2})`);
744
744
  regex = `${regex}(${opts.join("|")})`;
745
- return /* @__PURE__ */ new RegExp(`^${regex}$`);
745
+ return new RegExp(`^${regex}$`);
746
746
  }
747
747
  function isValidIP(ip, version) {
748
748
  if ((version === "v4" || !version) && ipv4Regex.test(ip)) return true;
@@ -2492,7 +2492,7 @@ var ZodDiscriminatedUnion = class ZodDiscriminatedUnion extends ZodType {
2492
2492
  * @param params
2493
2493
  */
2494
2494
  static create(discriminator, options, params) {
2495
- const optionsMap = /* @__PURE__ */ new Map();
2495
+ const optionsMap = new Map();
2496
2496
  for (const type of options) {
2497
2497
  const discriminatorValues = getDiscriminator(type.shape[discriminator]);
2498
2498
  if (!discriminatorValues.length) throw new Error(`A discriminator value for key \`${discriminator}\` could not be extracted from all schema options`);
@@ -2730,7 +2730,7 @@ var ZodMap = class extends ZodType {
2730
2730
  };
2731
2731
  });
2732
2732
  if (ctx.common.async) {
2733
- const finalMap = /* @__PURE__ */ new Map();
2733
+ const finalMap = new Map();
2734
2734
  return Promise.resolve().then(async () => {
2735
2735
  for (const pair of pairs) {
2736
2736
  const key = await pair.key;
@@ -2745,7 +2745,7 @@ var ZodMap = class extends ZodType {
2745
2745
  };
2746
2746
  });
2747
2747
  } else {
2748
- const finalMap = /* @__PURE__ */ new Map();
2748
+ const finalMap = new Map();
2749
2749
  for (const pair of pairs) {
2750
2750
  const key = pair.key;
2751
2751
  const value = pair.value;
@@ -2808,7 +2808,7 @@ var ZodSet = class ZodSet extends ZodType {
2808
2808
  }
2809
2809
  const valueType = this._def.valueType;
2810
2810
  function finalizeSet(elements$1) {
2811
- const parsedSet = /* @__PURE__ */ new Set();
2811
+ const parsedSet = new Set();
2812
2812
  for (const element of elements$1) {
2813
2813
  if (element.status === "aborted") return INVALID;
2814
2814
  if (element.status === "dirty") status.dirty();
@@ -3573,13 +3573,19 @@ const isoTimestampSchema = stringType().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d
3573
3573
  const dailyDateSchema = stringType().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format").brand();
3574
3574
  const activityDateSchema = stringType().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format").brand();
3575
3575
  const monthlyDateSchema = stringType().regex(/^\d{4}-\d{2}$/, "Date must be in YYYY-MM format").brand();
3576
+ const weeklyDateSchema = stringType().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format").brand();
3576
3577
  const filterDateSchema = stringType().regex(/^\d{8}$/, "Date must be in YYYYMMDD format").brand();
3577
3578
  const projectPathSchema = stringType().min(1, "Project path cannot be empty").brand();
3578
3579
  const versionSchema = stringType().regex(/^\d+\.\d+\.\d+/, "Invalid version format").brand();
3579
3580
  const createSessionId = (value) => sessionIdSchema.parse(value);
3580
3581
  const createDailyDate = (value) => dailyDateSchema.parse(value);
3581
3582
  const createMonthlyDate = (value) => monthlyDateSchema.parse(value);
3583
+ const createWeeklyDate = (value) => weeklyDateSchema.parse(value);
3582
3584
  const createProjectPath = (value) => projectPathSchema.parse(value);
3585
+ function createBucket(value) {
3586
+ if (weeklyDateSchema.safeParse(value).success) return createWeeklyDate(value);
3587
+ return createMonthlyDate(value);
3588
+ }
3583
3589
  /**
3584
3590
  * Available cost calculation modes
3585
3591
  * - auto: Use pre-calculated costs when available, otherwise calculate from tokens
@@ -3604,4 +3610,4 @@ const modelPricingSchema = objectType({
3604
3610
  cache_creation_input_token_cost: numberType().optional(),
3605
3611
  cache_read_input_token_cost: numberType().optional()
3606
3612
  });
3607
- export { CostModes, SortOrders, ZodFirstPartyTypeKind, ZodOptional, ZodType, activityDateSchema, arrayType, booleanType, createDailyDate, createMonthlyDate, createProjectPath, createSessionId, dailyDateSchema, discriminatedUnionType, enumType, filterDateSchema, isoTimestampSchema, literalType, messageIdSchema, modelNameSchema, modelPricingSchema, monthlyDateSchema, numberType, objectType, optionalType, projectPathSchema, recordType, requestIdSchema, sessionIdSchema, stringType, unionType, unknownType, versionSchema };
3613
+ export { CostModes, SortOrders, ZodFirstPartyTypeKind, ZodOptional, ZodType, activityDateSchema, arrayType, booleanType, createBucket, createDailyDate, createMonthlyDate, createProjectPath, createSessionId, createWeeklyDate, dailyDateSchema, discriminatedUnionType, enumType, filterDateSchema, isoTimestampSchema, literalType, messageIdSchema, modelNameSchema, modelPricingSchema, monthlyDateSchema, numberType, objectType, optionalType, projectPathSchema, recordType, requestIdSchema, sessionIdSchema, stringType, unionType, unknownType, versionSchema, weeklyDateSchema };
@@ -1,5 +1,5 @@
1
- import { DailyUsage, MonthlyUsage, SessionUsage } from "./data-loader-CgvyDaQD.js";
2
- import "./pricing-fetcher-B3SvKOod.js";
1
+ import "./pricing-fetcher-AYfCy7g-.js";
2
+ import { DailyUsage, MonthlyUsage, SessionUsage, WeeklyUsage } from "./data-loader-DZMUuqrf.js";
3
3
 
4
4
  //#region src/_token-utils.d.ts
5
5
 
@@ -63,7 +63,7 @@ type TotalsObject = TokenTotals & {
63
63
  * @param data - Array of daily, monthly, or session usage data
64
64
  * @returns Aggregated token totals and cost
65
65
  */
66
- declare function calculateTotals(data: Array<DailyUsage | MonthlyUsage | SessionUsage>): TokenTotals;
66
+ declare function calculateTotals(data: Array<DailyUsage | MonthlyUsage | WeeklyUsage | SessionUsage>): TokenTotals;
67
67
  // Re-export getTotalTokens from shared utilities for backward compatibility
68
68
 
69
69
  /**
@@ -1,4 +1,4 @@
1
1
  import { getTotalTokens } from "./_token-utils-WjkbrjKv.js";
2
- import "./_types-BHFM59hI.js";
2
+ import "./_types-ed8-0BH6.js";
3
3
  import { calculateTotals, createTotalsObject } from "./calculate-cost-BDqO4yWA.js";
4
4
  export { calculateTotals, createTotalsObject, getTotalTokens };
@@ -1,7 +1,7 @@
1
- import { CLAUDE_CONFIG_DIR_ENV, CLAUDE_PROJECTS_DIR_NAME, DEFAULT_CLAUDE_CODE_PATH, DEFAULT_CLAUDE_CONFIG_PATH, DEFAULT_RECENT_DAYS, PricingFetcher, USAGE_DATA_GLOB_PATTERN, USER_HOME_DIR, __commonJSMin, __require, __toESM, isFailure, isPromise, require_usingCtx } from "./pricing-fetcher-B5b61mDE.js";
1
+ import { CLAUDE_CONFIG_DIR_ENV, CLAUDE_PROJECTS_DIR_NAME, DEFAULT_CLAUDE_CODE_PATH, DEFAULT_CLAUDE_CONFIG_PATH, DEFAULT_RECENT_DAYS, PricingFetcher, USAGE_DATA_GLOB_PATTERN, USER_HOME_DIR, __commonJSMin, __require, __toESM, isFailure, isPromise, require_usingCtx } from "./pricing-fetcher-CNjC0nXU.js";
2
2
  import { getTotalTokens } from "./_token-utils-WjkbrjKv.js";
3
- import { activityDateSchema, arrayType, booleanType, createDailyDate, createMonthlyDate, createProjectPath, createSessionId, dailyDateSchema, isoTimestampSchema, messageIdSchema, modelNameSchema, monthlyDateSchema, numberType, objectType, projectPathSchema, requestIdSchema, sessionIdSchema, stringType, versionSchema } from "./_types-BHFM59hI.js";
4
- import { logger } from "./logger-D0z-I-_d.js";
3
+ import { activityDateSchema, arrayType, booleanType, createBucket, createDailyDate, createMonthlyDate, createProjectPath, createSessionId, createWeeklyDate, dailyDateSchema, isoTimestampSchema, messageIdSchema, modelNameSchema, monthlyDateSchema, numberType, objectType, projectPathSchema, requestIdSchema, sessionIdSchema, stringType, unionType, versionSchema, weeklyDateSchema } from "./_types-ed8-0BH6.js";
4
+ import { logger } from "./logger-C35JCduT.js";
5
5
  import a, { readFile } from "node:fs/promises";
6
6
  import path, { posix } from "node:path";
7
7
  import process$1 from "node:process";
@@ -935,7 +935,7 @@ var require_walker = __commonJSMin((exports) => {
935
935
  counts: new counter_1.Counter(),
936
936
  options,
937
937
  queue: new queue_1.Queue((error, state) => this.callbackInvoker(state, error, callback$1)),
938
- symlinks: /* @__PURE__ */ new Map(),
938
+ symlinks: new Map(),
939
939
  visited: [""].slice(0, 0),
940
940
  controller: new AbortController()
941
941
  };
@@ -2931,7 +2931,7 @@ function isDynamicPattern(pattern, options) {
2931
2931
  return scan$2.isGlob || scan$2.negated;
2932
2932
  }
2933
2933
  function log(...tasks) {
2934
- console.log(`[tinyglobby ${(/* @__PURE__ */ new Date()).toLocaleTimeString("es")}]`, ...tasks);
2934
+ console.log(`[tinyglobby ${new Date().toLocaleTimeString("es")}]`, ...tasks);
2935
2935
  }
2936
2936
  const PARENT_DIRECTORY = /^(\/?\.\.)+/;
2937
2937
  const ESCAPING_BACKSLASHES = /\\(?=[()[\]{}!*+?@|])/g;
@@ -3118,7 +3118,7 @@ function identifySessionBlocks(entries, sessionDurationHours = DEFAULT_SESSION_D
3118
3118
  const sortedEntries = [...entries].sort((a$1, b$1) => a$1.timestamp.getTime() - b$1.timestamp.getTime());
3119
3119
  let currentBlockStart = null;
3120
3120
  let currentBlockEntries = [];
3121
- const now = /* @__PURE__ */ new Date();
3121
+ const now = new Date();
3122
3122
  for (const entry of sortedEntries) {
3123
3123
  const entryTime = entry.timestamp;
3124
3124
  if (currentBlockStart == null) {
@@ -3255,7 +3255,7 @@ function projectBlockUsage(block) {
3255
3255
  if (!block.isActive || (block.isGap ?? false)) return null;
3256
3256
  const burnRate = calculateBurnRate(block);
3257
3257
  if (burnRate == null) return null;
3258
- const now = /* @__PURE__ */ new Date();
3258
+ const now = new Date();
3259
3259
  const remainingTime = block.endTime.getTime() - now.getTime();
3260
3260
  const remainingMinutes = Math.max(0, remainingTime / (1e3 * 60));
3261
3261
  const currentTokens = getTotalTokens(block.tokenCounts);
@@ -3276,21 +3276,22 @@ function projectBlockUsage(block) {
3276
3276
  * @returns Filtered array of recent or active blocks
3277
3277
  */
3278
3278
  function filterRecentBlocks(blocks, days = DEFAULT_RECENT_DAYS) {
3279
- const now = /* @__PURE__ */ new Date();
3280
- const cutoffTime = /* @__PURE__ */ new Date(now.getTime() - days * 24 * 60 * 60 * 1e3);
3279
+ const now = new Date();
3280
+ const cutoffTime = new Date(now.getTime() - days * 24 * 60 * 60 * 1e3);
3281
3281
  return blocks.filter((block) => {
3282
3282
  return block.startTime >= cutoffTime || block.isActive;
3283
3283
  });
3284
3284
  }
3285
3285
  var import_usingCtx = __toESM(require_usingCtx(), 1);
3286
3286
  /**
3287
- * Get all Claude data directories to search for usage data
3288
- * Supports multiple paths: environment variable (comma-separated), new default, and old default
3287
+ * Get Claude data directories to search for usage data
3288
+ * When CLAUDE_CONFIG_DIR is set: uses only those paths
3289
+ * When not set: uses default paths (~/.config/claude and ~/.claude)
3289
3290
  * @returns Array of valid Claude data directory paths
3290
3291
  */
3291
3292
  function getClaudePaths() {
3292
3293
  const paths = [];
3293
- const normalizedPaths = /* @__PURE__ */ new Set();
3294
+ const normalizedPaths = new Set();
3294
3295
  const envPaths = (process$1.env[CLAUDE_CONFIG_DIR_ENV] ?? "").trim();
3295
3296
  if (envPaths !== "") {
3296
3297
  const envPathList = envPaths.split(",").map((p) => p.trim()).filter((p) => p !== "");
@@ -3306,6 +3307,9 @@ function getClaudePaths() {
3306
3307
  }
3307
3308
  }
3308
3309
  }
3310
+ if (paths.length > 0) return paths;
3311
+ throw new Error(`No valid Claude data directories found in CLAUDE_CONFIG_DIR. Please ensure the following exists:
3312
+ - ${envPaths}/${CLAUDE_PROJECTS_DIR_NAME}`.trim());
3309
3313
  }
3310
3314
  const defaultPaths = [DEFAULT_CLAUDE_CONFIG_PATH, path.join(USER_HOME_DIR, DEFAULT_CLAUDE_CODE_PATH)];
3311
3315
  for (const defaultPath of defaultPaths) {
@@ -3416,10 +3420,38 @@ const monthlyUsageSchema = objectType({
3416
3420
  project: stringType().optional()
3417
3421
  });
3418
3422
  /**
3423
+ * Zod schema for weekly usage aggregation data
3424
+ */
3425
+ const weeklyUsageSchema = objectType({
3426
+ week: weeklyDateSchema,
3427
+ inputTokens: numberType(),
3428
+ outputTokens: numberType(),
3429
+ cacheCreationTokens: numberType(),
3430
+ cacheReadTokens: numberType(),
3431
+ totalCost: numberType(),
3432
+ modelsUsed: arrayType(modelNameSchema),
3433
+ modelBreakdowns: arrayType(modelBreakdownSchema),
3434
+ project: stringType().optional()
3435
+ });
3436
+ /**
3437
+ * Zod schema for bucket usage aggregation data
3438
+ */
3439
+ const bucketUsageSchema = objectType({
3440
+ bucket: unionType([weeklyDateSchema, monthlyDateSchema]),
3441
+ inputTokens: numberType(),
3442
+ outputTokens: numberType(),
3443
+ cacheCreationTokens: numberType(),
3444
+ cacheReadTokens: numberType(),
3445
+ totalCost: numberType(),
3446
+ modelsUsed: arrayType(modelNameSchema),
3447
+ modelBreakdowns: arrayType(modelBreakdownSchema),
3448
+ project: stringType().optional()
3449
+ });
3450
+ /**
3419
3451
  * Aggregates token counts and costs by model name
3420
3452
  */
3421
3453
  function aggregateByModel(entries, getModel, getUsage, getCost) {
3422
- const modelAggregates = /* @__PURE__ */ new Map();
3454
+ const modelAggregates = new Map();
3423
3455
  const defaultStats = {
3424
3456
  inputTokens: 0,
3425
3457
  outputTokens: 0,
@@ -3447,7 +3479,7 @@ function aggregateByModel(entries, getModel, getUsage, getCost) {
3447
3479
  * Aggregates model breakdowns from multiple sources
3448
3480
  */
3449
3481
  function aggregateModelBreakdowns(breakdowns) {
3450
- const modelAggregates = /* @__PURE__ */ new Map();
3482
+ const modelAggregates = new Map();
3451
3483
  const defaultStats = {
3452
3484
  inputTokens: 0,
3453
3485
  outputTokens: 0,
@@ -3543,27 +3575,44 @@ function extractUniqueModels(entries, getModel) {
3543
3575
  return uniq(entries.map(getModel).filter((m$1) => m$1 != null && m$1 !== "<synthetic>"));
3544
3576
  }
3545
3577
  /**
3578
+ * Date formatter using Intl.DateTimeFormat for consistent formatting
3579
+ * Using UTC to avoid timezone issues
3580
+ */
3581
+ const dateFormatter = new Intl.DateTimeFormat("en-CA", {
3582
+ timeZone: "UTC",
3583
+ year: "numeric",
3584
+ month: "2-digit",
3585
+ day: "2-digit"
3586
+ });
3587
+ /**
3546
3588
  * Formats a date string to YYYY-MM-DD format
3547
3589
  * @param dateStr - Input date string
3548
3590
  * @returns Formatted date string in YYYY-MM-DD format
3549
3591
  */
3550
3592
  function formatDate(dateStr) {
3551
3593
  const date = new Date(dateStr);
3552
- const year = date.getFullYear();
3553
- const month = String(date.getMonth() + 1).padStart(2, "0");
3554
- const day = String(date.getDate()).padStart(2, "0");
3555
- return `${year}-${month}-${day}`;
3594
+ return dateFormatter.format(date);
3556
3595
  }
3557
3596
  /**
3597
+ * Date parts formatter for extracting year, month, and day separately
3598
+ */
3599
+ const datePartsFormatter = new Intl.DateTimeFormat("en", {
3600
+ timeZone: "UTC",
3601
+ year: "numeric",
3602
+ month: "2-digit",
3603
+ day: "2-digit"
3604
+ });
3605
+ /**
3558
3606
  * Formats a date string to compact format with year on first line and month-day on second
3559
3607
  * @param dateStr - Input date string
3560
3608
  * @returns Formatted date string with newline separator (YYYY\nMM-DD)
3561
3609
  */
3562
3610
  function formatDateCompact(dateStr) {
3563
3611
  const date = new Date(dateStr);
3564
- const year = date.getFullYear();
3565
- const month = String(date.getMonth() + 1).padStart(2, "0");
3566
- const day = String(date.getDate()).padStart(2, "0");
3612
+ const parts = datePartsFormatter.formatToParts(date);
3613
+ const year = parts.find((p) => p.type === "year")?.value ?? "";
3614
+ const month = parts.find((p) => p.type === "month")?.value ?? "";
3615
+ const day = parts.find((p) => p.type === "day")?.value ?? "";
3567
3616
  return `${year}\n${month}-${day}`;
3568
3617
  }
3569
3618
  /**
@@ -3666,7 +3715,7 @@ function getUsageLimitResetTime(data) {
3666
3715
  const timestampMatch = data.message?.content?.find((c) => c.text != null && c.text.includes("Claude AI usage limit reached"))?.text?.match(/\|(\d+)/) ?? null;
3667
3716
  if (timestampMatch?.[1] != null) {
3668
3717
  const resetTimestamp = Number.parseInt(timestampMatch[1]);
3669
- resetTime = resetTimestamp > 0 ? /* @__PURE__ */ new Date(resetTimestamp * 1e3) : null;
3718
+ resetTime = resetTimestamp > 0 ? new Date(resetTimestamp * 1e3) : null;
3670
3719
  }
3671
3720
  }
3672
3721
  return resetTime;
@@ -3707,7 +3756,7 @@ async function loadDailyUsageData(options) {
3707
3756
  const sortedFiles = await sortFilesByTimestamp(projectFilteredFiles);
3708
3757
  const mode = options?.mode ?? "auto";
3709
3758
  const fetcher = _usingCtx.u(mode === "display" ? null : new PricingFetcher(options?.offline));
3710
- const processedHashes = /* @__PURE__ */ new Set();
3759
+ const processedHashes = new Set();
3711
3760
  const allEntries = [];
3712
3761
  for (const file of sortedFiles) {
3713
3762
  const content = await readFile(file, "utf-8");
@@ -3781,7 +3830,7 @@ async function loadSessionData(options) {
3781
3830
  })));
3782
3831
  const mode = options?.mode ?? "auto";
3783
3832
  const fetcher = _usingCtx3.u(mode === "display" ? null : new PricingFetcher(options?.offline));
3784
- const processedHashes = /* @__PURE__ */ new Set();
3833
+ const processedHashes = new Set();
3785
3834
  const allEntries = [];
3786
3835
  for (const { file, baseDir } of sortedFilesWithBase) {
3787
3836
  const relativePath = path.relative(baseDir, file);
@@ -3848,15 +3897,55 @@ async function loadSessionData(options) {
3848
3897
  * @returns Array of monthly usage summaries sorted by month
3849
3898
  */
3850
3899
  async function loadMonthlyUsageData(options) {
3900
+ return loadBucketUsageData((data) => createMonthlyDate(data.date.substring(0, 7)), options).then((usages) => usages.map(({ bucket,...rest }) => ({
3901
+ month: createMonthlyDate(bucket.toString()),
3902
+ ...rest
3903
+ })));
3904
+ }
3905
+ /**
3906
+ * @param date - The date to get the week for
3907
+ * @param startDay - The day to start the week on (0 = Sunday, 1 = Monday, ..., 6 = Saturday)
3908
+ * @returns The date of the first day of the week for the given date
3909
+ */
3910
+ function getDateWeek(date, startDay) {
3911
+ const d$1 = new Date(date);
3912
+ const day = d$1.getDay();
3913
+ const shift = (day - startDay + 7) % 7;
3914
+ d$1.setDate(d$1.getDate() - shift);
3915
+ return createWeeklyDate(d$1.toISOString().substring(0, 10));
3916
+ }
3917
+ /**
3918
+ * Convert day name to number (0 = Sunday, 1 = Monday, ..., 6 = Saturday)
3919
+ */
3920
+ function getDayNumber(day) {
3921
+ const dayMap = {
3922
+ sunday: 0,
3923
+ monday: 1,
3924
+ tuesday: 2,
3925
+ wednesday: 3,
3926
+ thursday: 4,
3927
+ friday: 5,
3928
+ saturday: 6
3929
+ };
3930
+ return dayMap[day];
3931
+ }
3932
+ async function loadWeeklyUsageData(options) {
3933
+ const startDay = options?.startOfWeek != null ? getDayNumber(options.startOfWeek) : getDayNumber("sunday");
3934
+ return loadBucketUsageData((data) => getDateWeek(new Date(data.date), startDay), options).then((usages) => usages.map(({ bucket,...rest }) => ({
3935
+ week: createWeeklyDate(bucket.toString()),
3936
+ ...rest
3937
+ })));
3938
+ }
3939
+ async function loadBucketUsageData(groupingFn, options) {
3851
3940
  const dailyData = await loadDailyUsageData(options);
3852
3941
  const needsProjectGrouping = options?.groupByProject === true || options?.project != null;
3853
- const groupingKey = needsProjectGrouping ? (data) => `${data.date.substring(0, 7)}\x00${data.project ?? "unknown"}` : (data) => data.date.substring(0, 7);
3854
- const groupedByMonth = groupBy(dailyData, groupingKey);
3855
- const monthlyArray = [];
3856
- for (const [groupKey, dailyEntries] of Object.entries(groupedByMonth)) {
3942
+ const groupingKey = needsProjectGrouping ? (data) => `${groupingFn(data)}\x00${data.project ?? "unknown"}` : (data) => `${groupingFn(data)}`;
3943
+ const grouped = groupBy(dailyData, groupingKey);
3944
+ const buckets = [];
3945
+ for (const [groupKey, dailyEntries] of Object.entries(grouped)) {
3857
3946
  if (dailyEntries == null) continue;
3858
3947
  const parts = groupKey.split("\0");
3859
- const month = parts[0] ?? groupKey;
3948
+ const bucket = createBucket(parts[0] ?? groupKey);
3860
3949
  const project = parts.length > 1 ? parts[1] : void 0;
3861
3950
  const allBreakdowns = dailyEntries.flatMap((daily) => daily.modelBreakdowns);
3862
3951
  const modelAggregates = aggregateModelBreakdowns(allBreakdowns);
@@ -3875,8 +3964,8 @@ async function loadMonthlyUsageData(options) {
3875
3964
  totalCacheReadTokens += daily.cacheReadTokens;
3876
3965
  totalCost += daily.totalCost;
3877
3966
  }
3878
- const monthlyUsage = {
3879
- month: createMonthlyDate(month),
3967
+ const bucketUsage = {
3968
+ bucket,
3880
3969
  inputTokens: totalInputTokens,
3881
3970
  outputTokens: totalOutputTokens,
3882
3971
  cacheCreationTokens: totalCacheCreationTokens,
@@ -3886,9 +3975,9 @@ async function loadMonthlyUsageData(options) {
3886
3975
  modelBreakdowns,
3887
3976
  ...project != null && { project }
3888
3977
  };
3889
- monthlyArray.push(monthlyUsage);
3978
+ buckets.push(bucketUsage);
3890
3979
  }
3891
- return sortByDate(monthlyArray, (item) => `${item.month}-01`, options?.order);
3980
+ return sortByDate(buckets, (item) => item.bucket, options?.order);
3892
3981
  }
3893
3982
  /**
3894
3983
  * Loads usage data and organizes it into session blocks (typically 5-hour billing periods)
@@ -3914,7 +4003,7 @@ async function loadSessionBlockData(options) {
3914
4003
  const sortedFiles = await sortFilesByTimestamp(blocksFilteredFiles);
3915
4004
  const mode = options?.mode ?? "auto";
3916
4005
  const fetcher = _usingCtx4.u(mode === "display" ? null : new PricingFetcher(options?.offline));
3917
- const processedHashes = /* @__PURE__ */ new Set();
4006
+ const processedHashes = new Set();
3918
4007
  const allEntries = [];
3919
4008
  for (const file of sortedFiles) {
3920
4009
  const content = await readFile(file, "utf-8");
@@ -3960,4 +4049,4 @@ async function loadSessionBlockData(options) {
3960
4049
  _usingCtx4.d();
3961
4050
  }
3962
4051
  }
3963
- export { DEFAULT_SESSION_DURATION_HOURS, calculateBurnRate, calculateCostForEntry, createUniqueHash, dailyUsageSchema, extractProjectFromPath, filterRecentBlocks, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, glob, globUsageFiles, identifySessionBlocks, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, projectBlockUsage, sessionUsageSchema, sortFilesByTimestamp, uniq, unwrap, usageDataSchema };
4052
+ export { DEFAULT_SESSION_DURATION_HOURS, bucketUsageSchema, calculateBurnRate, calculateCostForEntry, createUniqueHash, dailyUsageSchema, extractProjectFromPath, filterRecentBlocks, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, glob, globUsageFiles, identifySessionBlocks, loadBucketUsageData, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, loadWeeklyUsageData, modelBreakdownSchema, monthlyUsageSchema, projectBlockUsage, sessionUsageSchema, sortFilesByTimestamp, uniq, unwrap, usageDataSchema, weeklyUsageSchema };
@@ -1,8 +1,14 @@
1
- import { CostMode, PricingFetcher, SortOrder } from "./pricing-fetcher-B3SvKOod.js";
1
+ import { Bucket, CostMode, PricingFetcher$1 as PricingFetcher, SortOrder, TupleToUnion } from "./pricing-fetcher-AYfCy7g-.js";
2
2
  import { z } from "zod";
3
3
 
4
- //#region src/_session-blocks.d.ts
4
+ //#region src/_consts.d.ts
5
5
 
6
+ /**
7
+ * Days of the week for weekly aggregation
8
+ */
9
+ declare const WEEK_DAYS: readonly ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];
10
+ //#endregion
11
+ //#region src/_session-blocks.d.ts
6
12
  /**
7
13
  * Represents a single usage data entry loaded from JSONL files
8
14
  */
@@ -50,8 +56,9 @@ type SessionBlock = {
50
56
  //#endregion
51
57
  //#region src/data-loader.d.ts
52
58
  /**
53
- * Get all Claude data directories to search for usage data
54
- * Supports multiple paths: environment variable (comma-separated), new default, and old default
59
+ * Get Claude data directories to search for usage data
60
+ * When CLAUDE_CONFIG_DIR is set: uses only those paths
61
+ * When not set: uses default paths (~/.config/claude and ~/.claude)
55
62
  * @returns Array of valid Claude data directory paths
56
63
  */
57
64
  declare function getClaudePaths(): string[];
@@ -418,6 +425,152 @@ declare const monthlyUsageSchema: z.ZodObject<{
418
425
  * Type definition for monthly usage aggregation
419
426
  */
420
427
  type MonthlyUsage = z.infer<typeof monthlyUsageSchema>;
428
+ /**
429
+ * Zod schema for weekly usage aggregation data
430
+ */
431
+ declare const weeklyUsageSchema: z.ZodObject<{
432
+ week: z.ZodBranded<z.ZodString, "WeeklyDate">;
433
+ inputTokens: z.ZodNumber;
434
+ outputTokens: z.ZodNumber;
435
+ cacheCreationTokens: z.ZodNumber;
436
+ cacheReadTokens: z.ZodNumber;
437
+ totalCost: z.ZodNumber;
438
+ modelsUsed: z.ZodArray<z.ZodBranded<z.ZodString, "ModelName">, "many">;
439
+ modelBreakdowns: z.ZodArray<z.ZodObject<{
440
+ modelName: z.ZodBranded<z.ZodString, "ModelName">;
441
+ inputTokens: z.ZodNumber;
442
+ outputTokens: z.ZodNumber;
443
+ cacheCreationTokens: z.ZodNumber;
444
+ cacheReadTokens: z.ZodNumber;
445
+ cost: z.ZodNumber;
446
+ }, "strip", z.ZodTypeAny, {
447
+ modelName: string & z.BRAND<"ModelName">;
448
+ inputTokens: number;
449
+ outputTokens: number;
450
+ cacheCreationTokens: number;
451
+ cacheReadTokens: number;
452
+ cost: number;
453
+ }, {
454
+ modelName: string;
455
+ inputTokens: number;
456
+ outputTokens: number;
457
+ cacheCreationTokens: number;
458
+ cacheReadTokens: number;
459
+ cost: number;
460
+ }>, "many">;
461
+ project: z.ZodOptional<z.ZodString>;
462
+ }, "strip", z.ZodTypeAny, {
463
+ week: string & z.BRAND<"WeeklyDate">;
464
+ inputTokens: number;
465
+ outputTokens: number;
466
+ cacheCreationTokens: number;
467
+ cacheReadTokens: number;
468
+ totalCost: number;
469
+ modelsUsed: (string & z.BRAND<"ModelName">)[];
470
+ modelBreakdowns: {
471
+ modelName: string & z.BRAND<"ModelName">;
472
+ inputTokens: number;
473
+ outputTokens: number;
474
+ cacheCreationTokens: number;
475
+ cacheReadTokens: number;
476
+ cost: number;
477
+ }[];
478
+ project?: string | undefined;
479
+ }, {
480
+ week: string;
481
+ inputTokens: number;
482
+ outputTokens: number;
483
+ cacheCreationTokens: number;
484
+ cacheReadTokens: number;
485
+ totalCost: number;
486
+ modelsUsed: string[];
487
+ modelBreakdowns: {
488
+ modelName: string;
489
+ inputTokens: number;
490
+ outputTokens: number;
491
+ cacheCreationTokens: number;
492
+ cacheReadTokens: number;
493
+ cost: number;
494
+ }[];
495
+ project?: string | undefined;
496
+ }>;
497
+ /**
498
+ * Type definition for weekly usage aggregation
499
+ */
500
+ type WeeklyUsage = z.infer<typeof weeklyUsageSchema>;
501
+ /**
502
+ * Zod schema for bucket usage aggregation data
503
+ */
504
+ declare const bucketUsageSchema: z.ZodObject<{
505
+ bucket: z.ZodUnion<[z.ZodBranded<z.ZodString, "WeeklyDate">, z.ZodBranded<z.ZodString, "MonthlyDate">]>;
506
+ inputTokens: z.ZodNumber;
507
+ outputTokens: z.ZodNumber;
508
+ cacheCreationTokens: z.ZodNumber;
509
+ cacheReadTokens: z.ZodNumber;
510
+ totalCost: z.ZodNumber;
511
+ modelsUsed: z.ZodArray<z.ZodBranded<z.ZodString, "ModelName">, "many">;
512
+ modelBreakdowns: z.ZodArray<z.ZodObject<{
513
+ modelName: z.ZodBranded<z.ZodString, "ModelName">;
514
+ inputTokens: z.ZodNumber;
515
+ outputTokens: z.ZodNumber;
516
+ cacheCreationTokens: z.ZodNumber;
517
+ cacheReadTokens: z.ZodNumber;
518
+ cost: z.ZodNumber;
519
+ }, "strip", z.ZodTypeAny, {
520
+ modelName: string & z.BRAND<"ModelName">;
521
+ inputTokens: number;
522
+ outputTokens: number;
523
+ cacheCreationTokens: number;
524
+ cacheReadTokens: number;
525
+ cost: number;
526
+ }, {
527
+ modelName: string;
528
+ inputTokens: number;
529
+ outputTokens: number;
530
+ cacheCreationTokens: number;
531
+ cacheReadTokens: number;
532
+ cost: number;
533
+ }>, "many">;
534
+ project: z.ZodOptional<z.ZodString>;
535
+ }, "strip", z.ZodTypeAny, {
536
+ bucket: (string & z.BRAND<"MonthlyDate">) | (string & z.BRAND<"WeeklyDate">);
537
+ inputTokens: number;
538
+ outputTokens: number;
539
+ cacheCreationTokens: number;
540
+ cacheReadTokens: number;
541
+ totalCost: number;
542
+ modelsUsed: (string & z.BRAND<"ModelName">)[];
543
+ modelBreakdowns: {
544
+ modelName: string & z.BRAND<"ModelName">;
545
+ inputTokens: number;
546
+ outputTokens: number;
547
+ cacheCreationTokens: number;
548
+ cacheReadTokens: number;
549
+ cost: number;
550
+ }[];
551
+ project?: string | undefined;
552
+ }, {
553
+ bucket: string;
554
+ inputTokens: number;
555
+ outputTokens: number;
556
+ cacheCreationTokens: number;
557
+ cacheReadTokens: number;
558
+ totalCost: number;
559
+ modelsUsed: string[];
560
+ modelBreakdowns: {
561
+ modelName: string;
562
+ inputTokens: number;
563
+ outputTokens: number;
564
+ cacheCreationTokens: number;
565
+ cacheReadTokens: number;
566
+ cost: number;
567
+ }[];
568
+ project?: string | undefined;
569
+ }>;
570
+ /**
571
+ * Type definition for bucket usage aggregation
572
+ */
573
+ type BucketUsage = z.infer<typeof bucketUsageSchema>;
421
574
  /**
422
575
  * Formats a date string to YYYY-MM-DD format
423
576
  * @param dateStr - Input date string
@@ -478,6 +631,7 @@ type DateFilter = {
478
631
  since?: string; // YYYYMMDD format
479
632
  until?: string; // YYYYMMDD format
480
633
  };
634
+ type WeekDay = TupleToUnion<typeof WEEK_DAYS>;
481
635
  /**
482
636
  * Configuration options for loading usage data
483
637
  */
@@ -489,6 +643,7 @@ type LoadOptions = {
489
643
  sessionDurationHours?: number; // Session block duration in hours
490
644
  groupByProject?: boolean; // Group data by project instead of aggregating
491
645
  project?: string; // Filter to specific project name
646
+ startOfWeek?: WeekDay; // Start of week for weekly aggregation
492
647
  } & DateFilter;
493
648
  /**
494
649
  * Loads and aggregates Claude usage data by day
@@ -511,6 +666,8 @@ declare function loadSessionData(options?: LoadOptions): Promise<SessionUsage[]>
511
666
  * @returns Array of monthly usage summaries sorted by month
512
667
  */
513
668
  declare function loadMonthlyUsageData(options?: LoadOptions): Promise<MonthlyUsage[]>;
669
+ declare function loadWeeklyUsageData(options?: LoadOptions): Promise<WeeklyUsage[]>;
670
+ declare function loadBucketUsageData(groupingFn: (data: DailyUsage) => Bucket, options?: LoadOptions): Promise<BucketUsage[]>;
514
671
  /**
515
672
  * Loads usage data and organizes it into session blocks (typically 5-hour billing periods)
516
673
  * Processes all usage data and groups it into time-based blocks for billing analysis
@@ -519,4 +676,4 @@ declare function loadMonthlyUsageData(options?: LoadOptions): Promise<MonthlyUsa
519
676
  */
520
677
  declare function loadSessionBlockData(options?: LoadOptions): Promise<SessionBlock[]>;
521
678
  //#endregion
522
- export { DailyUsage, DateFilter, GlobResult, LoadOptions, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData, calculateCostForEntry, createUniqueHash, dailyUsageSchema, extractProjectFromPath, formatDate, formatDateCompact, getClaudePaths, getEarliestTimestamp, getUsageLimitResetTime, globUsageFiles, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema };
679
+ export { BucketUsage, DailyUsage, DateFilter, GlobResult, LoadOptions, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData, WeeklyUsage, bucketUsageSchema as bucketUsageSchema$1, calculateCostForEntry as calculateCostForEntry$1, createUniqueHash as createUniqueHash$1, dailyUsageSchema as dailyUsageSchema$1, extractProjectFromPath as extractProjectFromPath$1, formatDate as formatDate$1, formatDateCompact as formatDateCompact$1, getClaudePaths as getClaudePaths$1, getEarliestTimestamp as getEarliestTimestamp$1, getUsageLimitResetTime as getUsageLimitResetTime$1, globUsageFiles as globUsageFiles$1, loadBucketUsageData as loadBucketUsageData$1, loadDailyUsageData as loadDailyUsageData$1, loadMonthlyUsageData as loadMonthlyUsageData$1, loadSessionBlockData as loadSessionBlockData$1, loadSessionData as loadSessionData$1, loadWeeklyUsageData as loadWeeklyUsageData$1, modelBreakdownSchema as modelBreakdownSchema$1, monthlyUsageSchema as monthlyUsageSchema$1, sessionUsageSchema as sessionUsageSchema$1, sortFilesByTimestamp as sortFilesByTimestamp$1, usageDataSchema as usageDataSchema$1, weeklyUsageSchema as weeklyUsageSchema$1 };