claude-scope 0.8.34 → 0.8.36

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.
Files changed (2) hide show
  1. package/dist/claude-scope.cjs +166 -43
  2. package/package.json +1 -1
@@ -2414,12 +2414,12 @@ var init_widget_types = __esm({
2414
2414
  });
2415
2415
 
2416
2416
  // src/providers/usage-parser.ts
2417
- var import_node_fs, import_node_readline, UsageParser;
2417
+ var import_fs, import_readline, UsageParser;
2418
2418
  var init_usage_parser = __esm({
2419
2419
  "src/providers/usage-parser.ts"() {
2420
2420
  "use strict";
2421
- import_node_fs = require("node:fs");
2422
- import_node_readline = require("node:readline");
2421
+ import_fs = require("fs");
2422
+ import_readline = require("readline");
2423
2423
  UsageParser = class {
2424
2424
  /**
2425
2425
  * Parse the last assistant message with usage data from transcript
@@ -2429,30 +2429,55 @@ var init_usage_parser = __esm({
2429
2429
  * @returns ContextUsage or null if not found
2430
2430
  */
2431
2431
  async parseLastUsage(transcriptPath) {
2432
- if (!(0, import_node_fs.existsSync)(transcriptPath)) {
2432
+ if (!(0, import_fs.existsSync)(transcriptPath)) {
2433
2433
  return null;
2434
2434
  }
2435
2435
  try {
2436
2436
  const lines = await this.readAllLines(transcriptPath);
2437
- for (let i = lines.length - 1; i >= 0; i--) {
2438
- const usage = this.parseLineForUsage(lines[i]);
2439
- if (usage) {
2440
- return usage;
2437
+ let mostRecentUsage = null;
2438
+ let mostRecentTimestamp = null;
2439
+ let hasAnyTimestamp = false;
2440
+ for (const line of lines) {
2441
+ const entry = this.parseLineForUsage(line);
2442
+ if (entry) {
2443
+ const entryTime = this.parseTimestamp(line);
2444
+ if (entryTime) {
2445
+ hasAnyTimestamp = true;
2446
+ if (!mostRecentTimestamp || entryTime > mostRecentTimestamp) {
2447
+ mostRecentUsage = entry;
2448
+ mostRecentTimestamp = entryTime;
2449
+ }
2450
+ } else if (!hasAnyTimestamp) {
2451
+ mostRecentUsage = entry;
2452
+ }
2441
2453
  }
2442
2454
  }
2443
- return null;
2455
+ return mostRecentUsage;
2444
2456
  } catch {
2445
2457
  return null;
2446
2458
  }
2447
2459
  }
2460
+ /**
2461
+ * Parse timestamp from a transcript line
2462
+ */
2463
+ parseTimestamp(line) {
2464
+ try {
2465
+ const entry = JSON.parse(line);
2466
+ if (entry.timestamp) {
2467
+ return new Date(entry.timestamp);
2468
+ }
2469
+ } catch {
2470
+ }
2471
+ return null;
2472
+ }
2448
2473
  /**
2449
2474
  * Read all lines from transcript file
2450
2475
  */
2451
2476
  async readAllLines(transcriptPath) {
2452
2477
  const lines = [];
2453
2478
  try {
2454
- const fileStream = (0, import_node_fs.createReadStream)(transcriptPath, { encoding: "utf-8" });
2455
- const rl = (0, import_node_readline.createInterface)({
2479
+ const fileStream = (0, import_fs.createReadStream)(transcriptPath, { encoding: "utf-8" });
2480
+ const rl = (0, import_readline.createInterface)({
2456
2481
  input: fileStream,
2457
2482
  crlfDelay: Infinity
2458
2483
  });
@@ -2466,6 +2491,92 @@ var init_usage_parser = __esm({
2466
2491
  }
2467
2492
  return lines;
2468
2493
  }
2494
+ /**
2495
+ * Parse cumulative cache tokens from all assistant messages in transcript
2496
+ *
2497
+ * Handles two transcript formats:
2498
+ * 1. Per-message format: cache_read varies per message (e.g., 2048, 4096) → sum all
2499
+ * 2. Cumulative format: cache_read is already cumulative (e.g., 165376) → use last
2500
+ *
2501
+ * @param transcriptPath - Path to the JSONL transcript file
2502
+ * @returns Object with cumulative cacheRead and cacheCreation, or null if not found
2503
+ */
2504
+ async parseCumulativeCache(transcriptPath) {
2505
+ if (!(0, import_fs.existsSync)(transcriptPath)) {
2506
+ return null;
2507
+ }
2508
+ try {
2509
+ const lines = await this.readAllLines(transcriptPath);
2510
+ const cacheEntries = [];
2511
+ for (const line of lines) {
2512
+ const entry = this.parseLineForCache(line);
2513
+ if (entry) {
2514
+ const timestamp = this.parseTimestamp(line);
2515
+ cacheEntries.push({
2516
+ cacheRead: entry.cacheRead,
2517
+ cacheCreation: entry.cacheCreation,
2518
+ timestamp: timestamp?.toISOString() || ""
2519
+ });
2520
+ }
2521
+ }
2522
+ if (cacheEntries.length === 0) {
2523
+ return null;
2524
+ }
2525
+ let isCumulativeFormat = false;
2526
+ if (cacheEntries.length > 1) {
2527
+ let nonDecreasingCount = 0;
2528
+ for (let i = 1; i < cacheEntries.length; i++) {
2529
+ if (cacheEntries[i].cacheRead >= cacheEntries[i - 1].cacheRead) {
2530
+ nonDecreasingCount++;
2531
+ }
2532
+ }
2533
+ isCumulativeFormat = nonDecreasingCount / (cacheEntries.length - 1) >= 0.8;
2534
+ }
2535
+ let cumulativeCacheRead;
2536
+ let cumulativeCacheCreation;
2537
+ if (isCumulativeFormat) {
2538
+ const lastEntry = cacheEntries[cacheEntries.length - 1];
2539
+ cumulativeCacheRead = lastEntry.cacheRead;
2540
+ cumulativeCacheCreation = lastEntry.cacheCreation;
2541
+ } else {
2542
+ cumulativeCacheRead = cacheEntries.reduce((sum, e) => sum + e.cacheRead, 0);
2543
+ cumulativeCacheCreation = cacheEntries.reduce((sum, e) => sum + e.cacheCreation, 0);
2544
+ }
2545
+ if (cumulativeCacheRead === 0 && cumulativeCacheCreation === 0) {
2546
+ return null;
2547
+ }
2548
+ return {
2549
+ cacheRead: cumulativeCacheRead,
2550
+ cacheCreation: cumulativeCacheCreation
2551
+ };
2552
+ } catch {
2553
+ return null;
2554
+ }
2555
+ }
2556
+ /**
2557
+ * Parse a single transcript line for cache data
2558
+ * Returns null if line is not an assistant message with usage
2559
+ */
2560
+ parseLineForCache(line) {
2561
+ try {
2562
+ const entry = JSON.parse(line);
2563
+ if (entry.type !== "assistant") {
2564
+ return null;
2565
+ }
2566
+ const usage = entry.message?.usage;
2567
+ if (!usage) {
2568
+ return null;
2569
+ }
2570
+ const cacheRead = usage.cache_read_input_tokens ?? 0;
2571
+ const cacheCreation = usage.cache_creation_input_tokens ?? 0;
2572
+ if (cacheRead === 0 && cacheCreation === 0) {
2573
+ return null;
2574
+ }
2575
+ return { cacheRead, cacheCreation };
2576
+ } catch {
2577
+ return null;
2578
+ }
2579
+ }
2469
2580
  /**
2470
2581
  * Parse a single transcript line for usage data
2471
2582
  * Returns null if line is not an assistant message with usage
@@ -2480,14 +2591,15 @@ var init_usage_parser = __esm({
2480
2591
  if (!usage) {
2481
2592
  return null;
2482
2593
  }
2483
- if (typeof usage.input_tokens !== "number" || typeof usage.output_tokens !== "number" || typeof usage.cache_read_input_tokens !== "number") {
2594
+ if (typeof usage.input_tokens !== "number" || typeof usage.output_tokens !== "number") {
2484
2595
  return null;
2485
2596
  }
2486
2597
  return {
2487
2598
  input_tokens: usage.input_tokens,
2488
2599
  output_tokens: usage.output_tokens,
2489
2600
  cache_creation_input_tokens: usage.cache_creation_input_tokens ?? 0,
2490
- cache_read_input_tokens: usage.cache_read_input_tokens
2601
+ cache_read_input_tokens: usage.cache_read_input_tokens ?? 0
2602
+ // Default to 0 if missing
2491
2603
  };
2492
2604
  } catch {
2493
2605
  return null;
@@ -2498,11 +2610,11 @@ var init_usage_parser = __esm({
2498
2610
  });
2499
2611
 
2500
2612
  // src/storage/cache-manager.ts
2501
- var import_node_fs2, import_node_os2, import_node_path2, DEFAULT_CACHE_PATH, DEFAULT_EXPIRY_MS, CacheManager;
2613
+ var import_node_fs, import_node_os2, import_node_path2, DEFAULT_CACHE_PATH, DEFAULT_EXPIRY_MS, CacheManager;
2502
2614
  var init_cache_manager = __esm({
2503
2615
  "src/storage/cache-manager.ts"() {
2504
2616
  "use strict";
2505
- import_node_fs2 = require("node:fs");
2617
+ import_node_fs = require("node:fs");
2506
2618
  import_node_os2 = require("node:os");
2507
2619
  import_node_path2 = require("node:path");
2508
2620
  DEFAULT_CACHE_PATH = `${(0, import_node_os2.homedir)()}/.config/claude-scope/cache.json`;
@@ -2575,11 +2687,11 @@ var init_cache_manager = __esm({
2575
2687
  * Load cache from file
2576
2688
  */
2577
2689
  loadCache() {
2578
- if (!(0, import_node_fs2.existsSync)(this.cachePath)) {
2690
+ if (!(0, import_node_fs.existsSync)(this.cachePath)) {
2579
2691
  return { sessions: {}, version: 1 };
2580
2692
  }
2581
2693
  try {
2582
- const content = (0, import_node_fs2.readFileSync)(this.cachePath, "utf-8");
2694
+ const content = (0, import_node_fs.readFileSync)(this.cachePath, "utf-8");
2583
2695
  return JSON.parse(content);
2584
2696
  } catch {
2585
2697
  return { sessions: {}, version: 1 };
@@ -2590,7 +2702,7 @@ var init_cache_manager = __esm({
2590
2702
  */
2591
2703
  saveCache(cache) {
2592
2704
  try {
2593
- (0, import_node_fs2.writeFileSync)(this.cachePath, JSON.stringify(cache, null, 2), "utf-8");
2705
+ (0, import_node_fs.writeFileSync)(this.cachePath, JSON.stringify(cache, null, 2), "utf-8");
2594
2706
  } catch {
2595
2707
  }
2596
2708
  }
@@ -2600,8 +2712,8 @@ var init_cache_manager = __esm({
2600
2712
  ensureCacheDir() {
2601
2713
  try {
2602
2714
  const dir = (0, import_node_path2.dirname)(this.cachePath);
2603
- if (!(0, import_node_fs2.existsSync)(dir)) {
2604
- (0, import_node_fs2.mkdirSync)(dir, { recursive: true });
2715
+ if (!(0, import_node_fs.existsSync)(dir)) {
2716
+ (0, import_node_fs.mkdirSync)(dir, { recursive: true });
2605
2717
  }
2606
2718
  } catch {
2607
2719
  }
@@ -2773,6 +2885,8 @@ var init_cache_metrics_widget = __esm({
2773
2885
  lastSessionId;
2774
2886
  cachedUsage;
2775
2887
  // Cache parsed usage within render cycle
2888
+ cachedCumulativeCache;
2889
+ // Cumulative cache for session
2776
2890
  constructor(theme) {
2777
2891
  super();
2778
2892
  this.theme = theme ?? DEFAULT_THEME;
@@ -2794,9 +2908,14 @@ var init_cache_metrics_widget = __esm({
2794
2908
  /**
2795
2909
  * Calculate cache metrics from context usage data
2796
2910
  * Returns zero metrics if no usage data is available (widget should always be visible)
2911
+ *
2912
+ * @param usage - Current usage data (for total tokens calculation)
2913
+ * @param cumulativeCache - Cumulative cache data for the session (optional)
2797
2914
  */
2798
- calculateMetrics(usage) {
2799
- if (!usage) {
2915
+ calculateMetrics(usage, cumulativeCache) {
2916
+ const cacheRead = cumulativeCache?.cacheRead ?? usage?.cache_read_input_tokens ?? 0;
2917
+ const cacheWrite = cumulativeCache?.cacheCreation ?? usage?.cache_creation_input_tokens ?? 0;
2918
+ if (!usage && !cumulativeCache) {
2800
2919
  return {
2801
2920
  cacheRead: 0,
2802
2921
  cacheWrite: 0,
@@ -2805,10 +2924,8 @@ var init_cache_metrics_widget = __esm({
2805
2924
  savings: 0
2806
2925
  };
2807
2926
  }
2808
- const cacheRead = usage.cache_read_input_tokens ?? 0;
2809
- const cacheWrite = usage.cache_creation_input_tokens ?? 0;
2810
- const inputTokens = usage.input_tokens ?? 0;
2811
- const outputTokens = usage.output_tokens ?? 0;
2927
+ const inputTokens = usage?.input_tokens ?? 0;
2928
+ const outputTokens = usage?.output_tokens ?? 0;
2812
2929
  const totalInputTokens = cacheRead + cacheWrite + inputTokens;
2813
2930
  const totalTokens = totalInputTokens + outputTokens;
2814
2931
  const hitRate = totalInputTokens > 0 ? Math.min(100, Math.round(cacheRead / totalInputTokens * 100)) : 0;
@@ -2845,18 +2962,21 @@ var init_cache_metrics_widget = __esm({
2845
2962
  });
2846
2963
  }
2847
2964
  }
2848
- if (!usage) {
2965
+ const hasRealUsage = usage && ((usage.input_tokens ?? 0) > 0 || (usage.output_tokens ?? 0) > 0 || (usage.cache_read_input_tokens ?? 0) > 0 || (usage.cache_creation_input_tokens ?? 0) > 0);
2966
+ if (!usage || !hasRealUsage) {
2849
2967
  this.cachedUsage = await this.usageParser.parseLastUsage(data.transcript_path);
2850
2968
  } else {
2851
2969
  this.cachedUsage = void 0;
2852
2970
  }
2971
+ this.cachedCumulativeCache = await this.usageParser.parseCumulativeCache(data.transcript_path);
2853
2972
  }
2854
2973
  /**
2855
2974
  * Render the cache metrics display
2856
2975
  */
2857
2976
  renderWithData(data, _context) {
2858
2977
  let usage = data.context_window?.current_usage;
2859
- if (!usage && this.cachedUsage) {
2978
+ const hasRealUsage = usage && ((usage.input_tokens ?? 0) > 0 || (usage.output_tokens ?? 0) > 0 || (usage.cache_read_input_tokens ?? 0) > 0 || (usage.cache_creation_input_tokens ?? 0) > 0);
2979
+ if ((!usage || !hasRealUsage) && this.cachedUsage) {
2860
2980
  usage = this.cachedUsage;
2861
2981
  }
2862
2982
  if (!usage) {
@@ -2865,7 +2985,8 @@ var init_cache_metrics_widget = __esm({
2865
2985
  usage = cached.usage;
2866
2986
  }
2867
2987
  }
2868
- const metrics = this.calculateMetrics(usage);
2988
+ const cumulativeCacheToUse = this.cachedCumulativeCache;
2989
+ const metrics = this.calculateMetrics(usage, cumulativeCacheToUse);
2869
2990
  if (!metrics) {
2870
2991
  return null;
2871
2992
  }
@@ -3416,7 +3537,8 @@ var init_context_widget = __esm({
3416
3537
  });
3417
3538
  }
3418
3539
  }
3419
- if (!current_usage) {
3540
+ const hasRealUsage = current_usage && ((current_usage.input_tokens ?? 0) > 0 || (current_usage.output_tokens ?? 0) > 0 || (current_usage.cache_read_input_tokens ?? 0) > 0 || (current_usage.cache_creation_input_tokens ?? 0) > 0);
3541
+ if (!current_usage || !hasRealUsage) {
3420
3542
  this.cachedUsage = await this.usageParser.parseLastUsage(data.transcript_path);
3421
3543
  } else {
3422
3544
  this.cachedUsage = void 0;
@@ -3425,7 +3547,8 @@ var init_context_widget = __esm({
3425
3547
  renderWithData(data, _context) {
3426
3548
  const { current_usage, context_window_size } = data.context_window;
3427
3549
  let usage = current_usage;
3428
- if (!usage && this.cachedUsage) {
3550
+ const hasRealUsage = usage && ((usage.input_tokens ?? 0) > 0 || (usage.output_tokens ?? 0) > 0 || (usage.cache_read_input_tokens ?? 0) > 0 || (usage.cache_creation_input_tokens ?? 0) > 0);
3551
+ if ((!usage || !hasRealUsage) && this.cachedUsage) {
3429
3552
  usage = this.cachedUsage;
3430
3553
  }
3431
3554
  if (!usage) {
@@ -26259,8 +26382,8 @@ init_renderer();
26259
26382
  init_widget_registry();
26260
26383
 
26261
26384
  // src/providers/transcript-provider.ts
26262
- var import_node_fs3 = require("node:fs");
26263
- var import_node_readline2 = require("node:readline");
26385
+ var import_node_fs2 = require("node:fs");
26386
+ var import_node_readline = require("node:readline");
26264
26387
  var TranscriptProvider = class {
26265
26388
  MAX_TOOLS = 20;
26266
26389
  /**
@@ -26269,13 +26392,13 @@ var TranscriptProvider = class {
26269
26392
  * @returns Array of tool entries, limited to last 20
26270
26393
  */
26271
26394
  async parseTools(transcriptPath) {
26272
- if (!(0, import_node_fs3.existsSync)(transcriptPath)) {
26395
+ if (!(0, import_node_fs2.existsSync)(transcriptPath)) {
26273
26396
  return [];
26274
26397
  }
26275
26398
  const toolMap = /* @__PURE__ */ new Map();
26276
26399
  try {
26277
- const fileStream = (0, import_node_fs3.createReadStream)(transcriptPath, { encoding: "utf-8" });
26278
- const rl = (0, import_node_readline2.createInterface)({
26400
+ const fileStream = (0, import_node_fs2.createReadStream)(transcriptPath, { encoding: "utf-8" });
26401
+ const rl = (0, import_node_readline.createInterface)({
26279
26402
  input: fileStream,
26280
26403
  crlfDelay: Infinity
26281
26404
  });
@@ -26554,13 +26677,13 @@ async function routeCommand(command) {
26554
26677
  }
26555
26678
 
26556
26679
  // src/config/config-loader.ts
26557
- var import_node_fs5 = require("node:fs");
26680
+ var import_node_fs4 = require("node:fs");
26558
26681
  var import_promises2 = require("node:fs/promises");
26559
26682
  var import_node_os4 = require("node:os");
26560
26683
  var import_node_path4 = require("node:path");
26561
26684
 
26562
26685
  // src/config/default-config-generator.ts
26563
- var import_node_fs4 = require("node:fs");
26686
+ var import_node_fs3 = require("node:fs");
26564
26687
  var import_node_os3 = require("node:os");
26565
26688
  var import_node_path3 = require("node:path");
26566
26689
  function getDefaultConfigPath() {
@@ -26568,15 +26691,15 @@ function getDefaultConfigPath() {
26568
26691
  }
26569
26692
  async function ensureDefaultConfig() {
26570
26693
  const configPath = getDefaultConfigPath();
26571
- if ((0, import_node_fs4.existsSync)(configPath)) {
26694
+ if ((0, import_node_fs3.existsSync)(configPath)) {
26572
26695
  return;
26573
26696
  }
26574
26697
  const configDir = (0, import_node_path3.join)((0, import_node_os3.homedir)(), ".claude-scope");
26575
- if (!(0, import_node_fs4.existsSync)(configDir)) {
26576
- (0, import_node_fs4.mkdirSync)(configDir, { recursive: true });
26698
+ if (!(0, import_node_fs3.existsSync)(configDir)) {
26699
+ (0, import_node_fs3.mkdirSync)(configDir, { recursive: true });
26577
26700
  }
26578
26701
  const defaultConfig = generateRichLayout("balanced", "dracula");
26579
- (0, import_node_fs4.writeFileSync)(configPath, JSON.stringify(defaultConfig, null, 2), "utf-8");
26702
+ (0, import_node_fs3.writeFileSync)(configPath, JSON.stringify(defaultConfig, null, 2), "utf-8");
26580
26703
  }
26581
26704
 
26582
26705
  // src/config/config-loader.ts
@@ -26589,7 +26712,7 @@ function getConfigPath() {
26589
26712
  async function loadWidgetConfig() {
26590
26713
  const configPath = getConfigPath();
26591
26714
  await ensureDefaultConfig();
26592
- if (!(0, import_node_fs5.existsSync)(configPath)) {
26715
+ if (!(0, import_node_fs4.existsSync)(configPath)) {
26593
26716
  return null;
26594
26717
  }
26595
26718
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-scope",
3
- "version": "0.8.34",
3
+ "version": "0.8.36",
4
4
  "description": "Claude Code plugin for session status and analytics",
5
5
  "license": "MIT",
6
6
  "type": "module",