ccusage 14.1.2 → 15.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
@@ -15,6 +15,10 @@
15
15
  <img src="https://cdn.jsdelivr.net/gh/ryoppippi/ccusage@main/docs/screenshot.png">
16
16
  </div>
17
17
 
18
+ <div align="center">
19
+ <img src="https://cdn.jsdelivr.net/gh/ryoppippi/ccusage@main/docs/blocks-live.png">
20
+ </div>
21
+
18
22
  > **ccusage(claude-code-usage)**
19
23
 
20
24
  A CLI tool for analyzing Claude Code usage from local JSONL files.
@@ -48,6 +52,7 @@ This tool helps you understand the value you're getting from your subscription b
48
52
  - 📅 **Monthly Report**: View token usage and costs aggregated by month
49
53
  - 💬 **Session Report**: View usage grouped by conversation sessions
50
54
  - ⏰ **5-Hour Blocks Report**: Track usage within Claude's billing windows with active block monitoring
55
+ - 📈 **Live Monitoring**: Real-time dashboard showing active session progress, token burn rate, and cost projections with `blocks --live`
51
56
  - 🤖 **Model Tracking**: See which Claude models you're using (Opus, Sonnet, etc.)
52
57
  - 📊 **Model Breakdown**: View per-model cost breakdown with `--breakdown` flag
53
58
  - 📅 **Date Filtering**: Filter reports by date range using `--since` and `--until`
@@ -284,6 +289,13 @@ ccusage blocks -t 500000
284
289
  # Use the highest previous block as the token limit
285
290
  ccusage blocks -t max
286
291
 
292
+ # Live monitoring dashboard with real-time updates
293
+ ccusage blocks --live # automatically uses highest previous session as token limit (-t max)
294
+ ccusage blocks --live -t 500000 # with explicit token limit (500,000 tokens)
295
+ ccusage blocks --live -t max # explicitly use highest previous session limit
296
+ ccusage blocks --live --refresh-interval 5 # update every 5 seconds (default: 1s)
297
+ ccusage blocks --live -t 1000000 --refresh-interval 2 # 1M token limit, 2s refresh
298
+
287
299
  # Combine options
288
300
  ccusage blocks --recent -t max
289
301
 
@@ -307,11 +319,23 @@ The blocks report helps you understand Claude Code's 5-hour rolling session wind
307
319
  - Helps track if you're approaching token limits within a session
308
320
  - The `-t max` option automatically uses your highest previous block as the limit
309
321
 
322
+ **Live Monitoring Features:**
323
+
324
+ - Real-time dashboard updates every 1 second (configurable 1-60s)
325
+ - Automatic token limit detection from your usage history
326
+ - Session progress bar with time remaining and burn rate
327
+ - Cost projections based on current usage patterns
328
+ - Colorful progress indicators (green/yellow/red for quota warnings)
329
+ - Graceful shutdown with Ctrl+C
330
+
310
331
  #### Blocks-specific options
311
332
 
312
333
  - `-t, --token-limit <number|max>`: Set token limit for quota warnings (use "max" for highest previous block)
313
334
  - `-a, --active`: Show only active block with detailed projections
314
335
  - `-r, --recent`: Show blocks from last 3 days (including active)
336
+ - `-l, --session-length <hours>`: Session block duration in hours (default: 5)
337
+ - `--live`: Live monitoring mode with real-time dashboard (automatically uses `-t max` and shows active block only)
338
+ - `--refresh-interval <seconds>`: Refresh interval for live mode (default: 1, range: 1-60)
315
339
 
316
340
  ### Options
317
341
 
@@ -565,6 +589,8 @@ some projects use `ccusage` internally and provide additional features:
565
589
 
566
590
  - [claude-usage-tracker-for-mac](https://github.com/penicillin0/claude-usage-tracker-for-mac) – macOS menu bar app to visualize Claude Code usage costs by [@penicillin0](https://github.com/penicillin0).
567
591
  - [ccusage Raycast Extension](https://www.raycast.com/nyatinte/ccusage) – Raycast extension to view Claude Code usage reports in Raycast by [@nyatinte](https://github.com/nyatinte).
592
+ - [claude-code-usage-monitor](https://github.com/Maciek-roboblog/Claude-Code-Usage-Monitor) – A real-time terminal-based tool for monitoring Claude Code token usage. It displays live token consumption, burn rate, and depletion predictions, with session-aware analytics, visual progress bars, and support for multiple subscription plans by [@Maciek-roboblog](https://github.com/Maciek-roboblog).
593
+ - [ClaudeCode_Dashboard](https://github.com/m-sigepon/ClaudeCode_Dashboard) – Web dashboard to visualize Claude Code usage with charts and USD/JPY conversion by [@m-sigepon](https://github.com/m-sigepon).
568
594
 
569
595
  ## Acknowledgments
570
596
 
@@ -1,5 +1,5 @@
1
1
  import "./pricing-fetcher-CXnYw4TA.js";
2
- import { DailyUsage, MonthlyUsage, SessionUsage } from "./data-loader-C1n0ww95.js";
2
+ import { DailyUsage, MonthlyUsage, SessionUsage } from "./data-loader-Bll0wMdK.js";
3
3
 
4
4
  //#region src/calculate-cost.d.ts
5
5
  /**
@@ -1,6 +1,6 @@
1
- import { CLAUDE_CONFIG_DIR_ENV, CLAUDE_PROJECTS_DIR_NAME, DEFAULT_CLAUDE_CODE_PATH, DEFAULT_CLAUDE_CONFIG_PATH, DEFAULT_RECENT_DAYS, PricingFetcher, USAGE_DATA_GLOB_PATTERN, USER_HOME_DIR, __commonJSMin, __require, __toESM, require_usingCtx } from "./pricing-fetcher-XdvIG0to.js";
1
+ import { CLAUDE_CONFIG_DIR_ENV, CLAUDE_PROJECTS_DIR_NAME, DEFAULT_CLAUDE_CODE_PATH, DEFAULT_CLAUDE_CONFIG_PATH, DEFAULT_RECENT_DAYS, PricingFetcher, USAGE_DATA_GLOB_PATTERN, USER_HOME_DIR, __commonJSMin, __require, __toESM, require_usingCtx } from "./pricing-fetcher-CMyU5JUX.js";
2
2
  import { activityDateSchema, arrayType, createDailyDate, createMonthlyDate, createProjectPath, createSessionId, dailyDateSchema, isoTimestampSchema, messageIdSchema, modelNameSchema, monthlyDateSchema, numberType, objectType, projectPathSchema, requestIdSchema, sessionIdSchema, versionSchema } from "./_types-Cr2YEzKm.js";
3
- import { logger } from "./logger-CIYlc309.js";
3
+ import { logger } from "./logger-6I_KKR-u.js";
4
4
  import a, { readFile } from "node:fs/promises";
5
5
  import F, { homedir } from "node:os";
6
6
  import path, { posix } from "node:path";
@@ -3869,4 +3869,4 @@ async function loadSessionBlockData(options) {
3869
3869
  _usingCtx4.d();
3870
3870
  }
3871
3871
  }
3872
- export { DEFAULT_SESSION_DURATION_HOURS, calculateBurnRate, calculateCostForEntry, createUniqueHash, dailyUsageSchema, filterRecentBlocks, formatDate, formatDateCompact, getClaudePaths, getDefaultClaudePath, getEarliestTimestamp, glob, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, projectBlockUsage, sessionUsageSchema, sortFilesByTimestamp, uniq, usageDataSchema };
3872
+ export { DEFAULT_SESSION_DURATION_HOURS, calculateBurnRate, calculateCostForEntry, createUniqueHash, dailyUsageSchema, filterRecentBlocks, formatDate, formatDateCompact, getClaudePaths, getDefaultClaudePath, getEarliestTimestamp, glob, identifySessionBlocks, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, projectBlockUsage, sessionUsageSchema, sortFilesByTimestamp, uniq, usageDataSchema };
@@ -91,8 +91,8 @@ declare const usageDataSchema: z.ZodObject<{
91
91
  cache_creation_input_tokens?: number | undefined;
92
92
  cache_read_input_tokens?: number | undefined;
93
93
  };
94
- model?: string & z.BRAND<"ModelName"> | undefined;
95
- id?: string & z.BRAND<"MessageId"> | undefined;
94
+ model?: (string & z.BRAND<"ModelName">) | undefined;
95
+ id?: (string & z.BRAND<"MessageId">) | undefined;
96
96
  }, {
97
97
  usage: {
98
98
  input_tokens: number;
@@ -107,7 +107,7 @@ declare const usageDataSchema: z.ZodObject<{
107
107
  requestId: z.ZodOptional<z.ZodBranded<z.ZodString, "RequestId">>;
108
108
  }, "strip", z.ZodTypeAny, {
109
109
  timestamp: string & z.BRAND<"ISOTimestamp">;
110
- version?: string & z.BRAND<"Version"> | undefined;
110
+ version?: (string & z.BRAND<"Version">) | undefined;
111
111
  message: {
112
112
  usage: {
113
113
  input_tokens: number;
@@ -115,11 +115,11 @@ declare const usageDataSchema: z.ZodObject<{
115
115
  cache_creation_input_tokens?: number | undefined;
116
116
  cache_read_input_tokens?: number | undefined;
117
117
  };
118
- model?: string & z.BRAND<"ModelName"> | undefined;
119
- id?: string & z.BRAND<"MessageId"> | undefined;
118
+ model?: (string & z.BRAND<"ModelName">) | undefined;
119
+ id?: (string & z.BRAND<"MessageId">) | undefined;
120
120
  };
121
121
  costUSD?: number | undefined;
122
- requestId?: string & z.BRAND<"RequestId"> | undefined;
122
+ requestId?: (string & z.BRAND<"RequestId">) | undefined;
123
123
  }, {
124
124
  timestamp: string;
125
125
  version?: string | undefined;
@@ -1,3 +1,3 @@
1
1
  import "./pricing-fetcher-CXnYw4TA.js";
2
- import { DailyUsage, DateFilter, LoadOptions, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData, calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema } from "./data-loader-C1n0ww95.js";
2
+ import { DailyUsage, DateFilter, LoadOptions, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData, calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema } from "./data-loader-Bll0wMdK.js";
3
3
  export { DailyUsage, DateFilter, LoadOptions, ModelBreakdown, MonthlyUsage, SessionUsage, UsageData, calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema };
@@ -1,5 +1,5 @@
1
- import "./pricing-fetcher-XdvIG0to.js";
1
+ import "./pricing-fetcher-CMyU5JUX.js";
2
2
  import "./_types-Cr2YEzKm.js";
3
- import { calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema } from "./data-loader-BFS_JSqd.js";
4
- import "./logger-CIYlc309.js";
3
+ import { calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema } from "./data-loader-B_ymgjWR.js";
4
+ import "./logger-6I_KKR-u.js";
5
5
  export { calculateCostForEntry, createUniqueHash, dailyUsageSchema, formatDate, formatDateCompact, getClaudePaths, getDefaultClaudePath, getEarliestTimestamp, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, modelBreakdownSchema, monthlyUsageSchema, sessionUsageSchema, sortFilesByTimestamp, usageDataSchema };
@@ -1,6 +1,6 @@
1
- import { CLAUDE_PROJECTS_DIR_NAME, DEBUG_MATCH_THRESHOLD_PERCENT, PricingFetcher, USAGE_DATA_GLOB_PATTERN, __toESM, require_usingCtx } from "./pricing-fetcher-XdvIG0to.js";
2
- import { getDefaultClaudePath, glob, usageDataSchema } from "./data-loader-BFS_JSqd.js";
3
- import { logger } from "./logger-CIYlc309.js";
1
+ import { CLAUDE_PROJECTS_DIR_NAME, DEBUG_MATCH_THRESHOLD_PERCENT, PricingFetcher, USAGE_DATA_GLOB_PATTERN, __toESM, require_usingCtx } from "./pricing-fetcher-CMyU5JUX.js";
2
+ import { getDefaultClaudePath, glob, usageDataSchema } from "./data-loader-B_ymgjWR.js";
3
+ import { logger } from "./logger-6I_KKR-u.js";
4
4
  import { readFile } from "node:fs/promises";
5
5
  import path from "node:path";
6
6
  var import_usingCtx = __toESM(require_usingCtx(), 1);
package/dist/debug.js CHANGED
@@ -1,6 +1,6 @@
1
- import "./pricing-fetcher-XdvIG0to.js";
1
+ import "./pricing-fetcher-CMyU5JUX.js";
2
2
  import "./_types-Cr2YEzKm.js";
3
- import "./data-loader-BFS_JSqd.js";
4
- import "./logger-CIYlc309.js";
5
- import { detectMismatches, printMismatchReport } from "./debug-DIfzQVQt.js";
3
+ import "./data-loader-B_ymgjWR.js";
4
+ import "./logger-6I_KKR-u.js";
5
+ import { detectMismatches, printMismatchReport } from "./debug-2jwq04IT.js";
6
6
  export { detectMismatches, printMismatchReport };
package/dist/index.js CHANGED
@@ -1,11 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import { BLOCKS_COMPACT_WIDTH_THRESHOLD, BLOCKS_DEFAULT_TERMINAL_WIDTH, BLOCKS_WARNING_THRESHOLD, DEFAULT_RECENT_DAYS, MCP_DEFAULT_PORT, __commonJSMin, __require, __toESM } from "./pricing-fetcher-XdvIG0to.js";
2
+ import { BLOCKS_COMPACT_WIDTH_THRESHOLD, BLOCKS_DEFAULT_TERMINAL_WIDTH, BLOCKS_WARNING_THRESHOLD, CLAUDE_PROJECTS_DIR_NAME, DEFAULT_RECENT_DAYS, DEFAULT_REFRESH_INTERVAL_SECONDS, MAX_REFRESH_INTERVAL_SECONDS, MCP_DEFAULT_PORT, MIN_REFRESH_INTERVAL_SECONDS, PricingFetcher, USAGE_DATA_GLOB_PATTERN, __commonJSMin, __require, __toESM, require_usingCtx } from "./pricing-fetcher-CMyU5JUX.js";
3
3
  import { CostModes, SortOrders, dateSchema } from "./_types-Cr2YEzKm.js";
4
4
  import { calculateTotals, createTotalsObject, getTotalTokens } from "./calculate-cost-CoS7we68.js";
5
- import { DEFAULT_SESSION_DURATION_HOURS, calculateBurnRate, filterRecentBlocks, formatDateCompact, getDefaultClaudePath, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, projectBlockUsage, uniq } from "./data-loader-BFS_JSqd.js";
6
- import { description, log, logger, name, version } from "./logger-CIYlc309.js";
7
- import { detectMismatches, printMismatchReport } from "./debug-DIfzQVQt.js";
8
- import { createMcpHttpApp, createMcpServer, startMcpServerStdio } from "./mcp-BI0xh4oM.js";
5
+ import { DEFAULT_SESSION_DURATION_HOURS, calculateBurnRate, calculateCostForEntry, createUniqueHash, filterRecentBlocks, formatDateCompact, getDefaultClaudePath, getEarliestTimestamp, glob, identifySessionBlocks, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData, projectBlockUsage, sortFilesByTimestamp, uniq, usageDataSchema } from "./data-loader-B_ymgjWR.js";
6
+ import { description, log, logger, name, version } from "./logger-6I_KKR-u.js";
7
+ import { detectMismatches, printMismatchReport } from "./debug-2jwq04IT.js";
8
+ import { createMcpHttpApp, createMcpServer, startMcpServerStdio } from "./mcp-COnbPauf.js";
9
+ import { readFile } from "node:fs/promises";
10
+ import path from "node:path";
9
11
  import process$1 from "node:process";
10
12
  import { createServer } from "node:http";
11
13
  import { Http2ServerRequest } from "node:http2";
@@ -1589,19 +1591,19 @@ var require_utils = __commonJSMin((exports, module) => {
1589
1591
  function hyperlink(url, text) {
1590
1592
  const OSC = "\x1B]";
1591
1593
  const BEL = "\x07";
1592
- const SEP = ";";
1594
+ const SEP$1 = ";";
1593
1595
  return [
1594
1596
  OSC,
1595
1597
  "8",
1596
- SEP,
1597
- SEP,
1598
+ SEP$1,
1599
+ SEP$1,
1598
1600
  url || text,
1599
1601
  BEL,
1600
1602
  text,
1601
1603
  OSC,
1602
1604
  "8",
1603
- SEP,
1604
- SEP,
1605
+ SEP$1,
1606
+ SEP$1,
1605
1607
  BEL
1606
1608
  ].join("");
1607
1609
  }
@@ -2558,8 +2560,8 @@ var require_cell = __commonJSMin((exports, module) => {
2558
2560
  for (let i = 1; i < span; i++) ret += 1 + dimensionTable[startingIndex + i];
2559
2561
  return ret;
2560
2562
  }
2561
- function sumPlusOne(a, b) {
2562
- return a + b + 1;
2563
+ function sumPlusOne(a$1, b) {
2564
+ return a$1 + b + 1;
2563
2565
  }
2564
2566
  let CHAR_NAMES = [
2565
2567
  "top",
@@ -2929,7 +2931,7 @@ function stringWidth(string, options = {}) {
2929
2931
  return width;
2930
2932
  }
2931
2933
  var import_cli_table3 = __toESM(require_cli_table3(), 1);
2932
- var import_picocolors$4 = __toESM(require_picocolors(), 1);
2934
+ var import_picocolors$5 = __toESM(require_picocolors(), 1);
2933
2935
  /**
2934
2936
  * Responsive table class that adapts column widths based on terminal size
2935
2937
  * Automatically adjusts formatting and layout for different screen sizes
@@ -3133,6 +3135,16 @@ function formatModelName(modelName) {
3133
3135
  return modelName;
3134
3136
  }
3135
3137
  /**
3138
+ * Formats an array of model names for display as a comma-separated string
3139
+ * Removes duplicates and sorts alphabetically
3140
+ * @param models - Array of model names
3141
+ * @returns Formatted string with unique, sorted model names separated by commas
3142
+ */
3143
+ function formatModelsDisplay(models) {
3144
+ const uniqueModels = uniq(models.map(formatModelName));
3145
+ return uniqueModels.sort().join(", ");
3146
+ }
3147
+ /**
3136
3148
  * Formats an array of model names for display with each model on a new line
3137
3149
  * Removes duplicates and sorts alphabetically
3138
3150
  * @param models - Array of model names
@@ -3155,11 +3167,727 @@ function pushBreakdownRows(table, breakdowns, extraColumns = 1, trailingColumns
3155
3167
  const row = [` └─ ${formatModelName(breakdown.modelName)}`];
3156
3168
  for (let i = 0; i < extraColumns; i++) row.push("");
3157
3169
  const totalTokens = breakdown.inputTokens + breakdown.outputTokens + breakdown.cacheCreationTokens + breakdown.cacheReadTokens;
3158
- 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)));
3170
+ row.push(import_picocolors$5.default.gray(formatNumber(breakdown.inputTokens)), import_picocolors$5.default.gray(formatNumber(breakdown.outputTokens)), import_picocolors$5.default.gray(formatNumber(breakdown.cacheCreationTokens)), import_picocolors$5.default.gray(formatNumber(breakdown.cacheReadTokens)), import_picocolors$5.default.gray(formatNumber(totalTokens)), import_picocolors$5.default.gray(formatCurrency(breakdown.cost)));
3159
3171
  for (let i = 0; i < trailingColumns; i++) row.push("");
3160
3172
  table.push(row);
3161
3173
  }
3162
3174
  }
3175
+ /** Options for {@linkcode delay}. */
3176
+ /**
3177
+ * Resolve a {@linkcode Promise} after a given amount of milliseconds.
3178
+ *
3179
+ * @throws {DOMException} If the optional signal is aborted before the delay
3180
+ * duration, and `signal.reason` is undefined.
3181
+ * @param ms Duration in milliseconds for how long the delay should last.
3182
+ * @param options Additional options.
3183
+ *
3184
+ * @example Basic usage
3185
+ * ```ts no-assert
3186
+ * import { delay } from "@std/async/delay";
3187
+ *
3188
+ * // ...
3189
+ * const delayedPromise = delay(100);
3190
+ * const result = await delayedPromise;
3191
+ * // ...
3192
+ * ```
3193
+ *
3194
+ * @example Disable persistence
3195
+ *
3196
+ * Setting `persistent` to `false` will allow the process to continue to run as
3197
+ * long as the timer exists.
3198
+ *
3199
+ * ```ts no-assert ignore
3200
+ * import { delay } from "@std/async/delay";
3201
+ *
3202
+ * // ...
3203
+ * await delay(100, { persistent: false });
3204
+ * // ...
3205
+ * ```
3206
+ */ function delay(ms, options = {}) {
3207
+ const { signal, persistent = true } = options;
3208
+ if (signal?.aborted) return Promise.reject(signal.reason);
3209
+ return new Promise((resolve, reject) => {
3210
+ const abort = () => {
3211
+ clearTimeout(i);
3212
+ reject(signal?.reason);
3213
+ };
3214
+ const done = () => {
3215
+ signal?.removeEventListener("abort", abort);
3216
+ resolve();
3217
+ };
3218
+ const i = setTimeout(done, ms);
3219
+ signal?.addEventListener("abort", abort, { once: true });
3220
+ if (persistent === false) try {
3221
+ Deno.unrefTimer(i);
3222
+ } catch (error) {
3223
+ if (!(error instanceof ReferenceError)) throw error;
3224
+ console.error("`persistent` option is only available in Deno");
3225
+ }
3226
+ });
3227
+ }
3228
+ const toZeroIfInfinity = (value) => Number.isFinite(value) ? value : 0;
3229
+ function parseNumber(milliseconds) {
3230
+ return {
3231
+ days: Math.trunc(milliseconds / 864e5),
3232
+ hours: Math.trunc(milliseconds / 36e5 % 24),
3233
+ minutes: Math.trunc(milliseconds / 6e4 % 60),
3234
+ seconds: Math.trunc(milliseconds / 1e3 % 60),
3235
+ milliseconds: Math.trunc(milliseconds % 1e3),
3236
+ microseconds: Math.trunc(toZeroIfInfinity(milliseconds * 1e3) % 1e3),
3237
+ nanoseconds: Math.trunc(toZeroIfInfinity(milliseconds * 1e6) % 1e3)
3238
+ };
3239
+ }
3240
+ function parseBigint(milliseconds) {
3241
+ return {
3242
+ days: milliseconds / 86400000n,
3243
+ hours: milliseconds / 3600000n % 24n,
3244
+ minutes: milliseconds / 60000n % 60n,
3245
+ seconds: milliseconds / 1000n % 60n,
3246
+ milliseconds: milliseconds % 1000n,
3247
+ microseconds: 0n,
3248
+ nanoseconds: 0n
3249
+ };
3250
+ }
3251
+ function parseMilliseconds(milliseconds) {
3252
+ switch (typeof milliseconds) {
3253
+ case "number": {
3254
+ if (Number.isFinite(milliseconds)) return parseNumber(milliseconds);
3255
+ break;
3256
+ }
3257
+ case "bigint": return parseBigint(milliseconds);
3258
+ }
3259
+ throw new TypeError("Expected a finite number or bigint");
3260
+ }
3261
+ const isZero = (value) => value === 0 || value === 0n;
3262
+ const pluralize = (word, count) => count === 1 || count === 1n ? word : `${word}s`;
3263
+ const SECOND_ROUNDING_EPSILON = 1e-7;
3264
+ const ONE_DAY_IN_MILLISECONDS = 24n * 60n * 60n * 1000n;
3265
+ function prettyMilliseconds(milliseconds, options) {
3266
+ const isBigInt = typeof milliseconds === "bigint";
3267
+ if (!isBigInt && !Number.isFinite(milliseconds)) throw new TypeError("Expected a finite number or bigint");
3268
+ options = { ...options };
3269
+ const sign = milliseconds < 0 ? "-" : "";
3270
+ milliseconds = milliseconds < 0 ? -milliseconds : milliseconds;
3271
+ if (options.colonNotation) {
3272
+ options.compact = false;
3273
+ options.formatSubMilliseconds = false;
3274
+ options.separateMilliseconds = false;
3275
+ options.verbose = false;
3276
+ }
3277
+ if (options.compact) {
3278
+ options.unitCount = 1;
3279
+ options.secondsDecimalDigits = 0;
3280
+ options.millisecondsDecimalDigits = 0;
3281
+ }
3282
+ let result = [];
3283
+ const floorDecimals = (value, decimalDigits) => {
3284
+ const flooredInterimValue = Math.floor(value * 10 ** decimalDigits + SECOND_ROUNDING_EPSILON);
3285
+ const flooredValue = Math.round(flooredInterimValue) / 10 ** decimalDigits;
3286
+ return flooredValue.toFixed(decimalDigits);
3287
+ };
3288
+ const add = (value, long, short, valueString) => {
3289
+ if ((result.length === 0 || !options.colonNotation) && isZero(value) && !(options.colonNotation && short === "m")) return;
3290
+ valueString ??= String(value);
3291
+ if (options.colonNotation) {
3292
+ const wholeDigits = valueString.includes(".") ? valueString.split(".")[0].length : valueString.length;
3293
+ const minLength = result.length > 0 ? 2 : 1;
3294
+ valueString = "0".repeat(Math.max(0, minLength - wholeDigits)) + valueString;
3295
+ } else valueString += options.verbose ? " " + pluralize(long, value) : short;
3296
+ result.push(valueString);
3297
+ };
3298
+ const parsed = parseMilliseconds(milliseconds);
3299
+ const days = BigInt(parsed.days);
3300
+ if (options.hideYearAndDays) add(BigInt(days) * 24n + BigInt(parsed.hours), "hour", "h");
3301
+ else {
3302
+ if (options.hideYear) add(days, "day", "d");
3303
+ else {
3304
+ add(days / 365n, "year", "y");
3305
+ add(days % 365n, "day", "d");
3306
+ }
3307
+ add(Number(parsed.hours), "hour", "h");
3308
+ }
3309
+ add(Number(parsed.minutes), "minute", "m");
3310
+ if (!options.hideSeconds) if (options.separateMilliseconds || options.formatSubMilliseconds || !options.colonNotation && milliseconds < 1e3) {
3311
+ const seconds = Number(parsed.seconds);
3312
+ const milliseconds$1 = Number(parsed.milliseconds);
3313
+ const microseconds = Number(parsed.microseconds);
3314
+ const nanoseconds = Number(parsed.nanoseconds);
3315
+ add(seconds, "second", "s");
3316
+ if (options.formatSubMilliseconds) {
3317
+ add(milliseconds$1, "millisecond", "ms");
3318
+ add(microseconds, "microsecond", "µs");
3319
+ add(nanoseconds, "nanosecond", "ns");
3320
+ } else {
3321
+ const millisecondsAndBelow = milliseconds$1 + microseconds / 1e3 + nanoseconds / 1e6;
3322
+ const millisecondsDecimalDigits = typeof options.millisecondsDecimalDigits === "number" ? options.millisecondsDecimalDigits : 0;
3323
+ const roundedMilliseconds = millisecondsAndBelow >= 1 ? Math.round(millisecondsAndBelow) : Math.ceil(millisecondsAndBelow);
3324
+ const millisecondsString = millisecondsDecimalDigits ? millisecondsAndBelow.toFixed(millisecondsDecimalDigits) : roundedMilliseconds;
3325
+ add(Number.parseFloat(millisecondsString), "millisecond", "ms", millisecondsString);
3326
+ }
3327
+ } else {
3328
+ const seconds = (isBigInt ? Number(milliseconds % ONE_DAY_IN_MILLISECONDS) : milliseconds) / 1e3 % 60;
3329
+ const secondsDecimalDigits = typeof options.secondsDecimalDigits === "number" ? options.secondsDecimalDigits : 1;
3330
+ const secondsFixed = floorDecimals(seconds, secondsDecimalDigits);
3331
+ const secondsString = options.keepDecimalsOnWholeSeconds ? secondsFixed : secondsFixed.replace(/\.0+$/, "");
3332
+ add(Number.parseFloat(secondsString), "second", "s", secondsString);
3333
+ }
3334
+ if (result.length === 0) return sign + "0" + (options.verbose ? " milliseconds" : "ms");
3335
+ const separator = options.colonNotation ? ":" : " ";
3336
+ if (typeof options.unitCount === "number") result = result.slice(0, Math.max(options.unitCount, 1));
3337
+ return sign + result.join(separator);
3338
+ }
3339
+ /**
3340
+ * Manages live monitoring of Claude usage with efficient data reloading
3341
+ */
3342
+ var LiveMonitor = class {
3343
+ config;
3344
+ fetcher = null;
3345
+ lastFileTimestamps = /* @__PURE__ */ new Map();
3346
+ processedHashes = /* @__PURE__ */ new Set();
3347
+ allEntries = [];
3348
+ constructor(config) {
3349
+ this.config = config;
3350
+ if (config.mode !== "display") this.fetcher = new PricingFetcher();
3351
+ }
3352
+ /**
3353
+ * Implements Disposable interface
3354
+ */
3355
+ [Symbol.dispose]() {
3356
+ this.fetcher?.[Symbol.dispose]();
3357
+ }
3358
+ /**
3359
+ * Gets the current active session block with minimal file reading
3360
+ * Only reads new or modified files since last check
3361
+ */
3362
+ async getActiveBlock() {
3363
+ const claudeDir = path.join(this.config.claudePath, CLAUDE_PROJECTS_DIR_NAME);
3364
+ const files = await glob([USAGE_DATA_GLOB_PATTERN], {
3365
+ cwd: claudeDir,
3366
+ absolute: true
3367
+ });
3368
+ if (files.length === 0) return null;
3369
+ const filesToRead = [];
3370
+ for (const file of files) {
3371
+ const timestamp = await getEarliestTimestamp(file);
3372
+ const lastTimestamp = this.lastFileTimestamps.get(file);
3373
+ if (timestamp != null && (lastTimestamp == null || timestamp.getTime() > lastTimestamp)) {
3374
+ filesToRead.push(file);
3375
+ this.lastFileTimestamps.set(file, timestamp.getTime());
3376
+ }
3377
+ }
3378
+ if (filesToRead.length > 0) {
3379
+ const sortedFiles = await sortFilesByTimestamp(filesToRead);
3380
+ for (const file of sortedFiles) {
3381
+ const content = await readFile(file, "utf-8");
3382
+ const lines = content.trim().split("\n").filter((line) => line.length > 0);
3383
+ for (const line of lines) try {
3384
+ const parsed = JSON.parse(line);
3385
+ const result = usageDataSchema.safeParse(parsed);
3386
+ if (!result.success) continue;
3387
+ const data = result.data;
3388
+ const uniqueHash = createUniqueHash(data);
3389
+ if (uniqueHash != null && this.processedHashes.has(uniqueHash)) continue;
3390
+ if (uniqueHash != null) this.processedHashes.add(uniqueHash);
3391
+ const costUSD = await (this.config.mode === "display" ? Promise.resolve(data.costUSD ?? 0) : calculateCostForEntry(data, this.config.mode, this.fetcher));
3392
+ this.allEntries.push({
3393
+ timestamp: new Date(data.timestamp),
3394
+ usage: {
3395
+ inputTokens: data.message.usage.input_tokens ?? 0,
3396
+ outputTokens: data.message.usage.output_tokens ?? 0,
3397
+ cacheCreationInputTokens: data.message.usage.cache_creation_input_tokens ?? 0,
3398
+ cacheReadInputTokens: data.message.usage.cache_read_input_tokens ?? 0
3399
+ },
3400
+ costUSD,
3401
+ model: data.message.model ?? "<synthetic>",
3402
+ version: data.version
3403
+ });
3404
+ } catch {}
3405
+ }
3406
+ }
3407
+ const blocks = identifySessionBlocks(this.allEntries, this.config.sessionDurationHours);
3408
+ const sortedBlocks = this.config.order === "asc" ? blocks : blocks.reverse();
3409
+ return sortedBlocks.find((block) => block.isActive) ?? null;
3410
+ }
3411
+ /**
3412
+ * Clears all cached data to force a full reload
3413
+ */
3414
+ clearCache() {
3415
+ this.lastFileTimestamps.clear();
3416
+ this.processedHashes.clear();
3417
+ this.allEntries = [];
3418
+ }
3419
+ };
3420
+ const isBrowser = globalThis.window?.document !== void 0;
3421
+ const isNode = globalThis.process?.versions?.node !== void 0;
3422
+ const isBun = globalThis.process?.versions?.bun !== void 0;
3423
+ const isDeno = globalThis.Deno?.version?.deno !== void 0;
3424
+ const isElectron = globalThis.process?.versions?.electron !== void 0;
3425
+ const isJsDom = globalThis.navigator?.userAgent?.includes("jsdom") === true;
3426
+ const isWebWorker = typeof WorkerGlobalScope !== "undefined" && globalThis instanceof WorkerGlobalScope;
3427
+ const isDedicatedWorker = typeof DedicatedWorkerGlobalScope !== "undefined" && globalThis instanceof DedicatedWorkerGlobalScope;
3428
+ const isSharedWorker = typeof SharedWorkerGlobalScope !== "undefined" && globalThis instanceof SharedWorkerGlobalScope;
3429
+ const isServiceWorker = typeof ServiceWorkerGlobalScope !== "undefined" && globalThis instanceof ServiceWorkerGlobalScope;
3430
+ const platform = globalThis.navigator?.userAgentData?.platform;
3431
+ const isMacOs = platform === "macOS" || globalThis.navigator?.platform === "MacIntel" || globalThis.navigator?.userAgent?.includes(" Mac ") === true || globalThis.process?.platform === "darwin";
3432
+ const isWindows$1 = platform === "Windows" || globalThis.navigator?.platform === "Win32" || globalThis.process?.platform === "win32";
3433
+ const isLinux = platform === "Linux" || globalThis.navigator?.platform?.startsWith("Linux") === true || globalThis.navigator?.userAgent?.includes(" Linux ") === true || globalThis.process?.platform === "linux";
3434
+ const isIos = platform === "iOS" || globalThis.navigator?.platform === "MacIntel" && globalThis.navigator?.maxTouchPoints > 1 || /iPad|iPhone|iPod/.test(globalThis.navigator?.platform);
3435
+ const isAndroid = platform === "Android" || globalThis.navigator?.platform === "Android" || globalThis.navigator?.userAgent?.includes(" Android ") === true || globalThis.process?.platform === "android";
3436
+ const ESC = "\x1B[";
3437
+ const SEP = ";";
3438
+ const isTerminalApp = !isBrowser && process$1.env.TERM_PROGRAM === "Apple_Terminal";
3439
+ const isWindows = !isBrowser && process$1.platform === "win32";
3440
+ const cwdFunction = isBrowser ? () => {
3441
+ throw new Error("`process.cwd()` only works in Node.js, not the browser.");
3442
+ } : process$1.cwd;
3443
+ const cursorTo = (x, y) => {
3444
+ if (typeof x !== "number") throw new TypeError("The `x` argument is required");
3445
+ if (typeof y !== "number") return ESC + (x + 1) + "G";
3446
+ return ESC + (y + 1) + SEP + (x + 1) + "H";
3447
+ };
3448
+ const cursorUp = (count = 1) => ESC + count + "A";
3449
+ const cursorDown = (count = 1) => ESC + count + "B";
3450
+ const cursorLeft = ESC + "G";
3451
+ const cursorSavePosition = isTerminalApp ? "\x1B7" : ESC + "s";
3452
+ const cursorRestorePosition = isTerminalApp ? "\x1B8" : ESC + "u";
3453
+ const cursorGetPosition = ESC + "6n";
3454
+ const cursorNextLine = ESC + "E";
3455
+ const cursorPrevLine = ESC + "F";
3456
+ const cursorHide = ESC + "?25l";
3457
+ const cursorShow = ESC + "?25h";
3458
+ const eraseEndLine = ESC + "K";
3459
+ const eraseStartLine = ESC + "1K";
3460
+ const eraseLine = ESC + "2K";
3461
+ const eraseDown = ESC + "J";
3462
+ const eraseUp = ESC + "1J";
3463
+ const eraseScreen = ESC + "2J";
3464
+ const scrollUp = ESC + "S";
3465
+ const scrollDown = ESC + "T";
3466
+ const clearScreen = "\x1Bc";
3467
+ const clearTerminal = isWindows ? `${eraseScreen}${ESC}0f` : `${eraseScreen}${ESC}3J${ESC}H`;
3468
+ const enterAlternativeScreen = ESC + "?1049h";
3469
+ const exitAlternativeScreen = ESC + "?1049l";
3470
+ /**
3471
+ * Terminal control sequences for live display updates
3472
+ */
3473
+ const TERMINAL_CONTROL = {
3474
+ HIDE_CURSOR: cursorHide,
3475
+ SHOW_CURSOR: cursorShow,
3476
+ CLEAR_SCREEN: clearScreen,
3477
+ CLEAR_LINE: eraseLine,
3478
+ MOVE_TO_TOP: cursorTo(0, 0),
3479
+ MOVE_UP: (n) => cursorUp(n),
3480
+ MOVE_DOWN: (n) => cursorDown(n),
3481
+ MOVE_TO_COLUMN: (n) => cursorTo(n - 1, void 0)
3482
+ };
3483
+ /**
3484
+ * Manages terminal state for live updates
3485
+ */
3486
+ var TerminalManager = class {
3487
+ stream;
3488
+ cursorHidden = false;
3489
+ originalWrite;
3490
+ constructor(stream = process$1.stdout) {
3491
+ this.stream = stream;
3492
+ this.originalWrite = stream.write.bind(stream);
3493
+ }
3494
+ /**
3495
+ * Hides the terminal cursor
3496
+ */
3497
+ hideCursor() {
3498
+ if (!this.cursorHidden && this.stream.isTTY) {
3499
+ this.stream.write(TERMINAL_CONTROL.HIDE_CURSOR);
3500
+ this.cursorHidden = true;
3501
+ }
3502
+ }
3503
+ /**
3504
+ * Shows the terminal cursor
3505
+ */
3506
+ showCursor() {
3507
+ if (this.cursorHidden && this.stream.isTTY) {
3508
+ this.stream.write(TERMINAL_CONTROL.SHOW_CURSOR);
3509
+ this.cursorHidden = false;
3510
+ }
3511
+ }
3512
+ /**
3513
+ * Clears the entire screen and moves cursor to top
3514
+ */
3515
+ clearScreen() {
3516
+ if (this.stream.isTTY) {
3517
+ this.stream.write(TERMINAL_CONTROL.CLEAR_SCREEN);
3518
+ this.stream.write(TERMINAL_CONTROL.MOVE_TO_TOP);
3519
+ }
3520
+ }
3521
+ /**
3522
+ * Clears the current line
3523
+ */
3524
+ clearLine() {
3525
+ if (this.stream.isTTY) this.stream.write(TERMINAL_CONTROL.CLEAR_LINE);
3526
+ }
3527
+ /**
3528
+ * Moves cursor up by n lines
3529
+ */
3530
+ moveUp(lines) {
3531
+ if (this.stream.isTTY && lines > 0) this.stream.write(TERMINAL_CONTROL.MOVE_UP(lines));
3532
+ }
3533
+ /**
3534
+ * Moves cursor to beginning of line
3535
+ */
3536
+ moveToLineStart() {
3537
+ if (this.stream.isTTY) this.stream.write(TERMINAL_CONTROL.MOVE_TO_COLUMN(1));
3538
+ }
3539
+ /**
3540
+ * Writes text to the stream
3541
+ */
3542
+ write(text) {
3543
+ this.stream.write(text);
3544
+ }
3545
+ /**
3546
+ * Gets terminal width
3547
+ */
3548
+ get width() {
3549
+ return this.stream.columns || 80;
3550
+ }
3551
+ /**
3552
+ * Gets terminal height
3553
+ */
3554
+ get height() {
3555
+ return this.stream.rows || 24;
3556
+ }
3557
+ /**
3558
+ * Checks if the stream is a TTY
3559
+ */
3560
+ get isTTY() {
3561
+ return this.stream.isTTY ?? false;
3562
+ }
3563
+ /**
3564
+ * Ensures cursor is shown on cleanup
3565
+ */
3566
+ cleanup() {
3567
+ this.showCursor();
3568
+ }
3569
+ };
3570
+ /**
3571
+ * Creates a progress bar string
3572
+ * @param value - Current value
3573
+ * @param max - Maximum value
3574
+ * @param width - Width of the progress bar
3575
+ * @param options - Display options
3576
+ * @param options.showPercentage - Whether to show percentage
3577
+ * @param options.showValues - Whether to show current/max values
3578
+ * @param options.fillChar - Character for filled portion
3579
+ * @param options.emptyChar - Character for empty portion
3580
+ * @param options.leftBracket - Left bracket character
3581
+ * @param options.rightBracket - Right bracket character
3582
+ * @param options.colors - Color configuration
3583
+ * @param options.colors.low - Color for low percentage
3584
+ * @param options.colors.medium - Color for medium percentage
3585
+ * @param options.colors.high - Color for high percentage
3586
+ * @param options.colors.critical - Color for critical percentage
3587
+ * @returns Formatted progress bar string
3588
+ */
3589
+ function createProgressBar(value, max, width, options = {}) {
3590
+ const { showPercentage = true, showValues = false, fillChar = "█", emptyChar = "░", leftBracket = "[", rightBracket = "]", colors: colors$2 = {} } = options;
3591
+ const percentage = max > 0 ? Math.min(100, value / max * 100) : 0;
3592
+ const fillWidth = Math.round(percentage / 100 * width);
3593
+ const emptyWidth = width - fillWidth;
3594
+ let color = "";
3595
+ if (colors$2.critical != null && percentage >= 90) color = colors$2.critical;
3596
+ else if (colors$2.high != null && percentage >= 80) color = colors$2.high;
3597
+ else if (colors$2.medium != null && percentage >= 50) color = colors$2.medium;
3598
+ else if (colors$2.low != null) color = colors$2.low;
3599
+ let bar = leftBracket;
3600
+ if (color !== "") bar += color;
3601
+ bar += fillChar.repeat(fillWidth);
3602
+ bar += emptyChar.repeat(emptyWidth);
3603
+ if (color !== "") bar += "\x1B[0m";
3604
+ bar += rightBracket;
3605
+ if (showPercentage) bar += ` ${percentage.toFixed(1)}%`;
3606
+ if (showValues) bar += ` (${value}/${max})`;
3607
+ return bar;
3608
+ }
3609
+ /**
3610
+ * Centers text within a given width
3611
+ * @param text - Text to center
3612
+ * @param width - Total width
3613
+ * @returns Centered text with padding
3614
+ */
3615
+ function centerText(text, width) {
3616
+ const textLength = stringWidth(text);
3617
+ if (textLength >= width) return text;
3618
+ const leftPadding = Math.floor((width - textLength) / 2);
3619
+ const rightPadding = width - textLength - leftPadding;
3620
+ return " ".repeat(leftPadding) + text + " ".repeat(rightPadding);
3621
+ }
3622
+ var import_picocolors$4 = __toESM(require_picocolors(), 1);
3623
+ var import_usingCtx = __toESM(require_usingCtx(), 1);
3624
+ /**
3625
+ * Format token counts with K suffix for display
3626
+ * @param num - Number of tokens
3627
+ * @returns Formatted string like "12.3k" or "999"
3628
+ */
3629
+ function formatTokensShort(num) {
3630
+ if (num >= 1e3) return `${(num / 1e3).toFixed(1)}k`;
3631
+ return num.toString();
3632
+ }
3633
+ /**
3634
+ * Column layout constants for detail rows
3635
+ */
3636
+ const DETAIL_COLUMN_WIDTHS = {
3637
+ col1: 46,
3638
+ col2: 37
3639
+ };
3640
+ /**
3641
+ * Starts live monitoring of the active session block
3642
+ */
3643
+ async function startLiveMonitoring(config) {
3644
+ try {
3645
+ var _usingCtx = (0, import_usingCtx.default)();
3646
+ const terminal = new TerminalManager();
3647
+ const abortController = new AbortController();
3648
+ const cleanup = () => {
3649
+ abortController.abort();
3650
+ terminal.cleanup();
3651
+ terminal.clearScreen();
3652
+ logger.info("Live monitoring stopped.");
3653
+ if (process$1.exitCode == null) process$1.exit(0);
3654
+ };
3655
+ process$1.on("SIGINT", cleanup);
3656
+ process$1.on("SIGTERM", cleanup);
3657
+ terminal.hideCursor();
3658
+ const monitor = _usingCtx.u(new LiveMonitor({
3659
+ claudePath: config.claudePath,
3660
+ sessionDurationHours: config.sessionDurationHours,
3661
+ mode: config.mode,
3662
+ order: config.order
3663
+ }));
3664
+ try {
3665
+ while (!abortController.signal.aborted) {
3666
+ const activeBlock = await monitor.getActiveBlock();
3667
+ if (activeBlock == null) {
3668
+ terminal.clearScreen();
3669
+ terminal.write(import_picocolors$4.default.yellow("No active session block found. Waiting...\n"));
3670
+ try {
3671
+ await delay(config.refreshInterval, { signal: abortController.signal });
3672
+ } catch (error) {
3673
+ if ((error instanceof DOMException || error instanceof Error) && error.name === "AbortError") break;
3674
+ throw error;
3675
+ }
3676
+ continue;
3677
+ }
3678
+ terminal.clearScreen();
3679
+ renderLiveDisplay(terminal, activeBlock, config);
3680
+ try {
3681
+ await delay(config.refreshInterval, { signal: abortController.signal });
3682
+ } catch (error) {
3683
+ if ((error instanceof DOMException || error instanceof Error) && error.name === "AbortError") break;
3684
+ throw error;
3685
+ }
3686
+ }
3687
+ } catch (error) {
3688
+ if ((error instanceof DOMException || error instanceof Error) && error.name === "AbortError") return;
3689
+ terminal.clearScreen();
3690
+ const errorMessage = error instanceof Error ? error.message : String(error);
3691
+ terminal.write(import_picocolors$4.default.red(`Error: ${errorMessage}\n`));
3692
+ logger.error(`Live monitoring error: ${errorMessage}`);
3693
+ try {
3694
+ await delay(config.refreshInterval, { signal: abortController.signal });
3695
+ } catch {}
3696
+ }
3697
+ } catch (_) {
3698
+ _usingCtx.e = _;
3699
+ } finally {
3700
+ _usingCtx.d();
3701
+ }
3702
+ }
3703
+ /**
3704
+ * Renders the live display for an active session block
3705
+ */
3706
+ function renderLiveDisplay(terminal, block, config) {
3707
+ const width = terminal.width;
3708
+ const now = /* @__PURE__ */ new Date();
3709
+ const totalTokens = block.tokenCounts.inputTokens + block.tokenCounts.outputTokens;
3710
+ const elapsed = (now.getTime() - block.startTime.getTime()) / (1e3 * 60);
3711
+ const remaining = (block.endTime.getTime() - now.getTime()) / (1e3 * 60);
3712
+ if (width < 60) {
3713
+ renderCompactLiveDisplay(terminal, block, config, totalTokens, elapsed, remaining);
3714
+ return;
3715
+ }
3716
+ terminal.clearScreen();
3717
+ const boxWidth = Math.min(120, width - 2);
3718
+ const boxMargin = Math.floor((width - boxWidth) / 2);
3719
+ const marginStr = " ".repeat(boxMargin);
3720
+ const labelWidth = 14;
3721
+ const percentWidth = 7;
3722
+ const shortLabelWidth = 20;
3723
+ const barWidth = boxWidth - labelWidth - percentWidth - shortLabelWidth - 4;
3724
+ const sessionDuration = elapsed + remaining;
3725
+ const sessionPercent = elapsed / sessionDuration * 100;
3726
+ const sessionProgressBar = createProgressBar(elapsed, sessionDuration, barWidth, {
3727
+ showPercentage: false,
3728
+ fillChar: import_picocolors$4.default.cyan("█"),
3729
+ emptyChar: import_picocolors$4.default.gray("░"),
3730
+ leftBracket: "[",
3731
+ rightBracket: "]"
3732
+ });
3733
+ const startTime = block.startTime.toLocaleTimeString(void 0, {
3734
+ hour: "2-digit",
3735
+ minute: "2-digit",
3736
+ second: "2-digit",
3737
+ hour12: true
3738
+ });
3739
+ const endTime = block.endTime.toLocaleTimeString(void 0, {
3740
+ hour: "2-digit",
3741
+ minute: "2-digit",
3742
+ second: "2-digit",
3743
+ hour12: true
3744
+ });
3745
+ terminal.write(`${marginStr}┌${"─".repeat(boxWidth - 2)}┐\n`);
3746
+ terminal.write(`${marginStr}│${import_picocolors$4.default.bold(centerText("CLAUDE CODE - LIVE TOKEN USAGE MONITOR", boxWidth - 2))}│\n`);
3747
+ terminal.write(`${marginStr}├${"─".repeat(boxWidth - 2)}┤\n`);
3748
+ terminal.write(`${marginStr}│${" ".repeat(boxWidth - 2)}│\n`);
3749
+ const sessionLabel = import_picocolors$4.default.bold("⏱️ SESSION");
3750
+ const sessionLabelWidth = stringWidth(sessionLabel);
3751
+ const sessionBarStr = `${sessionLabel}${"".padEnd(Math.max(0, labelWidth - sessionLabelWidth))} ${sessionProgressBar} ${sessionPercent.toFixed(1).padStart(6)}%`;
3752
+ const sessionBarPadded = sessionBarStr + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(sessionBarStr)));
3753
+ terminal.write(`${marginStr}│ ${sessionBarPadded}│\n`);
3754
+ const col1 = `${import_picocolors$4.default.gray("Started:")} ${startTime}`;
3755
+ const col2 = `${import_picocolors$4.default.gray("Elapsed:")} ${prettyMilliseconds(elapsed * 60 * 1e3, { compact: true })}`;
3756
+ const col3 = `${import_picocolors$4.default.gray("Remaining:")} ${prettyMilliseconds(remaining * 60 * 1e3, { compact: true })} (${endTime})`;
3757
+ const col1Visible = stringWidth(col1);
3758
+ const col2Visible = stringWidth(col2);
3759
+ const pad1 = " ".repeat(Math.max(0, DETAIL_COLUMN_WIDTHS.col1 - col1Visible));
3760
+ const pad2 = " ".repeat(Math.max(0, DETAIL_COLUMN_WIDTHS.col2 - col2Visible));
3761
+ const sessionDetails = ` ${col1}${pad1}${col2}${pad2}${col3}`;
3762
+ const sessionDetailsPadded = sessionDetails + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(sessionDetails)));
3763
+ terminal.write(`${marginStr}│ ${sessionDetailsPadded}│\n`);
3764
+ terminal.write(`${marginStr}│${" ".repeat(boxWidth - 2)}│\n`);
3765
+ terminal.write(`${marginStr}├${"─".repeat(boxWidth - 2)}┤\n`);
3766
+ terminal.write(`${marginStr}│${" ".repeat(boxWidth - 2)}│\n`);
3767
+ const tokenPercent = config.tokenLimit != null && config.tokenLimit > 0 ? totalTokens / config.tokenLimit * 100 : 0;
3768
+ let barColor = import_picocolors$4.default.green;
3769
+ if (tokenPercent > 100) barColor = import_picocolors$4.default.red;
3770
+ else if (tokenPercent > 80) barColor = import_picocolors$4.default.yellow;
3771
+ const usageBar = config.tokenLimit != null && config.tokenLimit > 0 ? createProgressBar(totalTokens, config.tokenLimit, barWidth, {
3772
+ showPercentage: false,
3773
+ fillChar: barColor("█"),
3774
+ emptyChar: import_picocolors$4.default.gray("░"),
3775
+ leftBracket: "[",
3776
+ rightBracket: "]"
3777
+ }) : `[${import_picocolors$4.default.green("█".repeat(Math.floor(barWidth * .1)))}${import_picocolors$4.default.gray("░".repeat(barWidth - Math.floor(barWidth * .1)))}]`;
3778
+ const burnRate = calculateBurnRate(block);
3779
+ const rateIndicator = burnRate != null ? burnRate.tokensPerMinute > 1e3 ? import_picocolors$4.default.red("⚡ HIGH") : burnRate.tokensPerMinute > 500 ? import_picocolors$4.default.yellow("⚡ MODERATE") : import_picocolors$4.default.green("✓ NORMAL") : "";
3780
+ const rateDisplay = burnRate != null ? `${import_picocolors$4.default.bold("Burn Rate:")} ${Math.round(burnRate.tokensPerMinute)} per min ${rateIndicator}` : `${import_picocolors$4.default.bold("Burn Rate:")} N/A`;
3781
+ const usageLabel = import_picocolors$4.default.bold("🔥 USAGE");
3782
+ const usageLabelWidth = stringWidth(usageLabel);
3783
+ if (config.tokenLimit != null && config.tokenLimit > 0) {
3784
+ const usageBarStr = `${usageLabel}${"".padEnd(Math.max(0, labelWidth - usageLabelWidth))} ${usageBar} ${tokenPercent.toFixed(1).padStart(6)}% (${formatTokensShort(totalTokens)}/${formatTokensShort(config.tokenLimit)})`;
3785
+ const usageBarPadded = usageBarStr + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(usageBarStr)));
3786
+ terminal.write(`${marginStr}│ ${usageBarPadded}│\n`);
3787
+ const col1$1 = `${import_picocolors$4.default.gray("Tokens:")} ${formatNumber(totalTokens)} (${rateDisplay})`;
3788
+ const col2$1 = `${import_picocolors$4.default.gray("Limit:")} ${formatNumber(config.tokenLimit)} tokens`;
3789
+ const col3$1 = `${import_picocolors$4.default.gray("Cost:")} ${formatCurrency(block.costUSD)}`;
3790
+ const col1Visible$1 = stringWidth(col1$1);
3791
+ const col2Visible$1 = stringWidth(col2$1);
3792
+ const pad1$1 = " ".repeat(Math.max(0, DETAIL_COLUMN_WIDTHS.col1 - col1Visible$1));
3793
+ const pad2$1 = " ".repeat(Math.max(0, DETAIL_COLUMN_WIDTHS.col2 - col2Visible$1));
3794
+ const usageDetails = ` ${col1$1}${pad1$1}${col2$1}${pad2$1}${col3$1}`;
3795
+ const usageDetailsPadded = usageDetails + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(usageDetails)));
3796
+ terminal.write(`${marginStr}│ ${usageDetailsPadded}│\n`);
3797
+ } else {
3798
+ const usageBarStr = `${usageLabel}${"".padEnd(Math.max(0, labelWidth - usageLabelWidth))} ${usageBar} (${formatTokensShort(totalTokens)} tokens)`;
3799
+ const usageBarPadded = usageBarStr + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(usageBarStr)));
3800
+ terminal.write(`${marginStr}│ ${usageBarPadded}│\n`);
3801
+ const col1$1 = `${import_picocolors$4.default.gray("Tokens:")} ${formatNumber(totalTokens)} (${rateDisplay})`;
3802
+ const col3$1 = `${import_picocolors$4.default.gray("Cost:")} ${formatCurrency(block.costUSD)}`;
3803
+ const col1Visible$1 = stringWidth(col1$1);
3804
+ const pad1$1 = " ".repeat(Math.max(0, DETAIL_COLUMN_WIDTHS.col1 - col1Visible$1));
3805
+ const pad2$1 = " ".repeat(DETAIL_COLUMN_WIDTHS.col2);
3806
+ const usageDetails = ` ${col1$1}${pad1$1}${pad2$1}${col3$1}`;
3807
+ const usageDetailsPadded = usageDetails + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(usageDetails)));
3808
+ terminal.write(`${marginStr}│ ${usageDetailsPadded}│\n`);
3809
+ }
3810
+ terminal.write(`${marginStr}│${" ".repeat(boxWidth - 2)}│\n`);
3811
+ terminal.write(`${marginStr}├${"─".repeat(boxWidth - 2)}┤\n`);
3812
+ terminal.write(`${marginStr}│${" ".repeat(boxWidth - 2)}│\n`);
3813
+ const projection = projectBlockUsage(block);
3814
+ if (projection != null) {
3815
+ const projectedPercent = config.tokenLimit != null && config.tokenLimit > 0 ? projection.totalTokens / config.tokenLimit * 100 : 0;
3816
+ let projBarColor = import_picocolors$4.default.green;
3817
+ if (projectedPercent > 100) projBarColor = import_picocolors$4.default.red;
3818
+ else if (projectedPercent > 80) projBarColor = import_picocolors$4.default.yellow;
3819
+ const projectionBar = config.tokenLimit != null && config.tokenLimit > 0 ? createProgressBar(projection.totalTokens, config.tokenLimit, barWidth, {
3820
+ showPercentage: false,
3821
+ fillChar: projBarColor("█"),
3822
+ emptyChar: import_picocolors$4.default.gray("░"),
3823
+ leftBracket: "[",
3824
+ rightBracket: "]"
3825
+ }) : `[${import_picocolors$4.default.green("█".repeat(Math.floor(barWidth * .15)))}${import_picocolors$4.default.gray("░".repeat(barWidth - Math.floor(barWidth * .15)))}]`;
3826
+ const limitStatus = config.tokenLimit != null && config.tokenLimit > 0 ? projectedPercent > 100 ? import_picocolors$4.default.red("❌ WILL EXCEED LIMIT") : projectedPercent > 80 ? import_picocolors$4.default.yellow("⚠️ APPROACHING LIMIT") : import_picocolors$4.default.green("✓ WITHIN LIMIT") : import_picocolors$4.default.green("✓ ON TRACK");
3827
+ const projLabel = import_picocolors$4.default.bold("📈 PROJECTION");
3828
+ const projLabelWidth = stringWidth(projLabel);
3829
+ if (config.tokenLimit != null && config.tokenLimit > 0) {
3830
+ const projBarStr = `${projLabel}${"".padEnd(Math.max(0, labelWidth - projLabelWidth))} ${projectionBar} ${projectedPercent.toFixed(1).padStart(6)}% (${formatTokensShort(projection.totalTokens)}/${formatTokensShort(config.tokenLimit)})`;
3831
+ const projBarPadded = projBarStr + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(projBarStr)));
3832
+ terminal.write(`${marginStr}│ ${projBarPadded}│\n`);
3833
+ const col1$1 = `${import_picocolors$4.default.gray("Status:")} ${limitStatus}`;
3834
+ const col2$1 = `${import_picocolors$4.default.gray("Tokens:")} ${formatNumber(projection.totalTokens)}`;
3835
+ const col3$1 = `${import_picocolors$4.default.gray("Cost:")} ${formatCurrency(projection.totalCost)}`;
3836
+ const col1Visible$1 = stringWidth(col1$1);
3837
+ const col2Visible$1 = stringWidth(col2$1);
3838
+ const pad1$1 = " ".repeat(Math.max(0, DETAIL_COLUMN_WIDTHS.col1 - col1Visible$1));
3839
+ const pad2$1 = " ".repeat(Math.max(0, DETAIL_COLUMN_WIDTHS.col2 - col2Visible$1));
3840
+ const projDetails = ` ${col1$1}${pad1$1}${col2$1}${pad2$1}${col3$1}`;
3841
+ const projDetailsPadded = projDetails + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(projDetails)));
3842
+ terminal.write(`${marginStr}│ ${projDetailsPadded}│\n`);
3843
+ } else {
3844
+ const projBarStr = `${projLabel}${"".padEnd(Math.max(0, labelWidth - projLabelWidth))} ${projectionBar} (${formatTokensShort(projection.totalTokens)} tokens)`;
3845
+ const projBarPadded = projBarStr + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(projBarStr)));
3846
+ terminal.write(`${marginStr}│ ${projBarPadded}│\n`);
3847
+ const col1$1 = `${import_picocolors$4.default.gray("Status:")} ${limitStatus}`;
3848
+ const col2$1 = `${import_picocolors$4.default.gray("Tokens:")} ${formatNumber(projection.totalTokens)}`;
3849
+ const col3$1 = `${import_picocolors$4.default.gray("Cost:")} ${formatCurrency(projection.totalCost)}`;
3850
+ const col1Visible$1 = stringWidth(col1$1);
3851
+ const col2Visible$1 = stringWidth(col2$1);
3852
+ const pad1$1 = " ".repeat(Math.max(0, DETAIL_COLUMN_WIDTHS.col1 - col1Visible$1));
3853
+ const pad2$1 = " ".repeat(Math.max(0, DETAIL_COLUMN_WIDTHS.col2 - col2Visible$1));
3854
+ const projDetails = ` ${col1$1}${pad1$1}${col2$1}${pad2$1}${col3$1}`;
3855
+ const projDetailsPadded = projDetails + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(projDetails)));
3856
+ terminal.write(`${marginStr}│ ${projDetailsPadded}│\n`);
3857
+ }
3858
+ terminal.write(`${marginStr}│${" ".repeat(boxWidth - 2)}│\n`);
3859
+ }
3860
+ if (block.models.length > 0) {
3861
+ terminal.write(`${marginStr}├${"─".repeat(boxWidth - 2)}┤\n`);
3862
+ const modelsLine = `⚙️ Models: ${formatModelsDisplay(block.models)}`;
3863
+ const modelsLinePadded = modelsLine + " ".repeat(Math.max(0, boxWidth - 3 - stringWidth(modelsLine)));
3864
+ terminal.write(`${marginStr}│ ${modelsLinePadded}│\n`);
3865
+ }
3866
+ terminal.write(`${marginStr}├${"─".repeat(boxWidth - 2)}┤\n`);
3867
+ const refreshText = `↻ Refreshing every ${config.refreshInterval / 1e3}s • Press Ctrl+C to stop`;
3868
+ terminal.write(`${marginStr}│${import_picocolors$4.default.gray(centerText(refreshText, boxWidth - 2))}│\n`);
3869
+ terminal.write(`${marginStr}└${"─".repeat(boxWidth - 2)}┘\n`);
3870
+ }
3871
+ /**
3872
+ * Renders a compact live display for narrow terminals
3873
+ */
3874
+ function renderCompactLiveDisplay(terminal, block, config, totalTokens, elapsed, remaining) {
3875
+ const width = terminal.width;
3876
+ terminal.write(`${import_picocolors$4.default.bold(centerText("LIVE MONITOR", width))}\n`);
3877
+ terminal.write(`${"─".repeat(width)}\n`);
3878
+ const sessionPercent = elapsed / (elapsed + remaining) * 100;
3879
+ terminal.write(`Session: ${sessionPercent.toFixed(1)}% (${Math.floor(elapsed / 60)}h ${Math.floor(elapsed % 60)}m)\n`);
3880
+ if (config.tokenLimit != null && config.tokenLimit > 0) {
3881
+ const tokenPercent = totalTokens / config.tokenLimit * 100;
3882
+ const status = tokenPercent > 100 ? import_picocolors$4.default.red("OVER") : tokenPercent > 80 ? import_picocolors$4.default.yellow("WARN") : import_picocolors$4.default.green("OK");
3883
+ terminal.write(`Tokens: ${formatNumber(totalTokens)}/${formatNumber(config.tokenLimit)} ${status}\n`);
3884
+ } else terminal.write(`Tokens: ${formatNumber(totalTokens)}\n`);
3885
+ terminal.write(`Cost: ${formatCurrency(block.costUSD)}\n`);
3886
+ const burnRate = calculateBurnRate(block);
3887
+ if (burnRate != null) terminal.write(`Rate: ${formatNumber(burnRate.tokensPerMinute)}/min\n`);
3888
+ terminal.write(`${"─".repeat(width)}\n`);
3889
+ terminal.write(import_picocolors$4.default.gray(`Refresh: ${config.refreshInterval / 1e3}s | Ctrl+C: stop\n`));
3890
+ }
3163
3891
  var import_picocolors$3 = __toESM(require_picocolors(), 1);
3164
3892
  /**
3165
3893
  * Formats the time display for a session block
@@ -3241,13 +3969,23 @@ const blocksCommand = define({
3241
3969
  tokenLimit: {
3242
3970
  type: "string",
3243
3971
  short: "t",
3244
- description: "Token limit for quota warnings (e.g., 500000 or \"max\" for highest previous block)"
3972
+ description: "Token limit for quota warnings (e.g., 500000 or \"max\")"
3245
3973
  },
3246
3974
  sessionLength: {
3247
3975
  type: "number",
3248
3976
  short: "l",
3249
3977
  description: `Session block duration in hours (default: ${DEFAULT_SESSION_DURATION_HOURS})`,
3250
3978
  default: DEFAULT_SESSION_DURATION_HOURS
3979
+ },
3980
+ live: {
3981
+ type: "boolean",
3982
+ description: "Live monitoring mode with real-time updates",
3983
+ default: false
3984
+ },
3985
+ refreshInterval: {
3986
+ type: "number",
3987
+ description: `Refresh interval in seconds for live mode (default: ${DEFAULT_REFRESH_INTERVAL_SECONDS})`,
3988
+ default: DEFAULT_REFRESH_INTERVAL_SECONDS
3251
3989
  }
3252
3990
  },
3253
3991
  toKebab: true,
@@ -3290,6 +4028,25 @@ const blocksCommand = define({
3290
4028
  process$1.exit(0);
3291
4029
  }
3292
4030
  }
4031
+ if (ctx.values.live && !ctx.values.json) {
4032
+ if (!ctx.values.active) logger.info("Live mode automatically shows only active blocks.");
4033
+ let tokenLimitValue = ctx.values.tokenLimit;
4034
+ if (tokenLimitValue == null || tokenLimitValue === "") {
4035
+ tokenLimitValue = "max";
4036
+ if (maxTokensFromAll > 0) logger.info(`No token limit specified, using max from previous sessions: ${formatNumber(maxTokensFromAll)}`);
4037
+ }
4038
+ const refreshInterval = Math.max(MIN_REFRESH_INTERVAL_SECONDS, Math.min(MAX_REFRESH_INTERVAL_SECONDS, ctx.values.refreshInterval));
4039
+ if (refreshInterval !== ctx.values.refreshInterval) logger.warn(`Refresh interval adjusted to ${refreshInterval} seconds (valid range: ${MIN_REFRESH_INTERVAL_SECONDS}-${MAX_REFRESH_INTERVAL_SECONDS})`);
4040
+ await startLiveMonitoring({
4041
+ claudePath: getDefaultClaudePath(),
4042
+ tokenLimit: parseTokenLimit(tokenLimitValue, maxTokensFromAll),
4043
+ refreshInterval: refreshInterval * 1e3,
4044
+ sessionDurationHours: ctx.values.sessionLength,
4045
+ mode: ctx.values.mode,
4046
+ order: ctx.values.order
4047
+ });
4048
+ return;
4049
+ }
3293
4050
  if (ctx.values.json) {
3294
4051
  const jsonOutput = { blocks: blocks.map((block) => {
3295
4052
  const burnRate = block.isActive ? calculateBurnRate(block) : null;
@@ -951,7 +951,7 @@ function _getDefaultLogLevel() {
951
951
  }
952
952
  const consola = createConsola$1();
953
953
  var name = "ccusage";
954
- var version = "14.1.2";
954
+ var version = "15.0.0";
955
955
  var description = "Usage analysis tool for Claude Code";
956
956
  /**
957
957
  * Application logger instance with package name tag
package/dist/logger.js CHANGED
@@ -1,2 +1,2 @@
1
- import { log, logger } from "./logger-CIYlc309.js";
1
+ import { log, logger } from "./logger-6I_KKR-u.js";
2
2
  export { log, logger };
@@ -1,7 +1,7 @@
1
- import { __commonJSMin, __toESM, require_usingCtx } from "./pricing-fetcher-XdvIG0to.js";
1
+ import { __commonJSMin, __toESM, require_usingCtx } from "./pricing-fetcher-CMyU5JUX.js";
2
2
  import { ZodFirstPartyTypeKind, ZodOptional, ZodType, arrayType, booleanType, dateSchema, discriminatedUnionType, enumType, literalType, numberType, objectType, optionalType, recordType, stringType, unionType, unknownType } from "./_types-Cr2YEzKm.js";
3
- import { getDefaultClaudePath, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData } from "./data-loader-BFS_JSqd.js";
4
- import { name, version } from "./logger-CIYlc309.js";
3
+ import { getDefaultClaudePath, loadDailyUsageData, loadMonthlyUsageData, loadSessionBlockData, loadSessionData } from "./data-loader-B_ymgjWR.js";
4
+ import { name, version } from "./logger-6I_KKR-u.js";
5
5
  import process from "node:process";
6
6
  const LATEST_PROTOCOL_VERSION = "2025-03-26";
7
7
  const SUPPORTED_PROTOCOL_VERSIONS = [
package/dist/mcp.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import "./pricing-fetcher-CXnYw4TA.js";
2
- import { LoadOptions } from "./data-loader-C1n0ww95.js";
2
+ import { LoadOptions } from "./data-loader-Bll0wMdK.js";
3
3
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
4
  import { Hono } from "hono/tiny";
5
5
 
package/dist/mcp.js CHANGED
@@ -1,6 +1,6 @@
1
- import "./pricing-fetcher-XdvIG0to.js";
1
+ import "./pricing-fetcher-CMyU5JUX.js";
2
2
  import "./_types-Cr2YEzKm.js";
3
- import "./data-loader-BFS_JSqd.js";
4
- import "./logger-CIYlc309.js";
5
- import { createMcpHttpApp, createMcpServer, startMcpServerStdio } from "./mcp-BI0xh4oM.js";
3
+ import "./data-loader-B_ymgjWR.js";
4
+ import "./logger-6I_KKR-u.js";
5
+ import { createMcpHttpApp, createMcpServer, startMcpServerStdio } from "./mcp-COnbPauf.js";
6
6
  export { createMcpHttpApp, createMcpServer, startMcpServerStdio };
@@ -1,5 +1,5 @@
1
1
  import { modelPricingSchema } from "./_types-Cr2YEzKm.js";
2
- import { logger } from "./logger-CIYlc309.js";
2
+ import { logger } from "./logger-6I_KKR-u.js";
3
3
  import { createRequire } from "node:module";
4
4
  import F, { homedir } from "node:os";
5
5
  import path from "node:path";
@@ -105,6 +105,21 @@ const USAGE_DATA_GLOB_PATTERN = "**/*.jsonl";
105
105
  * Used when no port is specified for MCP server communication
106
106
  */
107
107
  const MCP_DEFAULT_PORT = 8080;
108
+ /**
109
+ * Default refresh interval in seconds for live monitoring mode
110
+ * Used in blocks command for real-time updates
111
+ */
112
+ const DEFAULT_REFRESH_INTERVAL_SECONDS = 1;
113
+ /**
114
+ * Minimum refresh interval in seconds for live monitoring mode
115
+ * Prevents too-frequent updates that could impact performance
116
+ */
117
+ const MIN_REFRESH_INTERVAL_SECONDS = 1;
118
+ /**
119
+ * Maximum refresh interval in seconds for live monitoring mode
120
+ * Prevents too-slow updates that reduce monitoring effectiveness
121
+ */
122
+ const MAX_REFRESH_INTERVAL_SECONDS = 60;
108
123
  var require_usingCtx = __commonJSMin((exports, module) => {
109
124
  function _usingCtx() {
110
125
  var r = "function" == typeof SuppressedError ? SuppressedError : function(r$1, e$1) {
@@ -389,4 +404,4 @@ var PricingFetcher = class {
389
404
  return cost;
390
405
  }
391
406
  };
392
- export { BLOCKS_COMPACT_WIDTH_THRESHOLD, BLOCKS_DEFAULT_TERMINAL_WIDTH, BLOCKS_WARNING_THRESHOLD, CLAUDE_CONFIG_DIR_ENV, CLAUDE_PROJECTS_DIR_NAME, DEBUG_MATCH_THRESHOLD_PERCENT, DEFAULT_CLAUDE_CODE_PATH, DEFAULT_CLAUDE_CONFIG_PATH, DEFAULT_RECENT_DAYS, MCP_DEFAULT_PORT, PricingFetcher, USAGE_DATA_GLOB_PATTERN, USER_HOME_DIR, __commonJSMin, __require, __toESM, require_usingCtx };
407
+ export { BLOCKS_COMPACT_WIDTH_THRESHOLD, BLOCKS_DEFAULT_TERMINAL_WIDTH, BLOCKS_WARNING_THRESHOLD, CLAUDE_CONFIG_DIR_ENV, CLAUDE_PROJECTS_DIR_NAME, DEBUG_MATCH_THRESHOLD_PERCENT, DEFAULT_CLAUDE_CODE_PATH, DEFAULT_CLAUDE_CONFIG_PATH, DEFAULT_RECENT_DAYS, DEFAULT_REFRESH_INTERVAL_SECONDS, MAX_REFRESH_INTERVAL_SECONDS, MCP_DEFAULT_PORT, MIN_REFRESH_INTERVAL_SECONDS, PricingFetcher, USAGE_DATA_GLOB_PATTERN, USER_HOME_DIR, __commonJSMin, __require, __toESM, require_usingCtx };
@@ -1,4 +1,4 @@
1
- import { PricingFetcher } from "./pricing-fetcher-XdvIG0to.js";
1
+ import { PricingFetcher } from "./pricing-fetcher-CMyU5JUX.js";
2
2
  import "./_types-Cr2YEzKm.js";
3
- import "./logger-CIYlc309.js";
3
+ import "./logger-6I_KKR-u.js";
4
4
  export { PricingFetcher };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ccusage",
3
3
  "type": "module",
4
- "version": "14.1.2",
4
+ "version": "15.0.0",
5
5
  "description": "Usage analysis tool for Claude Code",
6
6
  "author": "ryoppippi",
7
7
  "license": "MIT",