claude-scope 0.8.34 → 0.8.35

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 +137 -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,63 @@ 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
+ * Sums ALL cache_read and cache_creation tokens across the entire session
2497
+ *
2498
+ * @param transcriptPath - Path to the JSONL transcript file
2499
+ * @returns Object with cumulative cacheRead and cacheCreation, or null if not found
2500
+ */
2501
+ async parseCumulativeCache(transcriptPath) {
2502
+ if (!(0, import_fs.existsSync)(transcriptPath)) {
2503
+ return null;
2504
+ }
2505
+ try {
2506
+ const lines = await this.readAllLines(transcriptPath);
2507
+ let cumulativeCacheRead = 0;
2508
+ let cumulativeCacheCreation = 0;
2509
+ for (const line of lines) {
2510
+ const entry = this.parseLineForCache(line);
2511
+ if (entry) {
2512
+ cumulativeCacheRead += entry.cacheRead;
2513
+ cumulativeCacheCreation += entry.cacheCreation;
2514
+ }
2515
+ }
2516
+ if (cumulativeCacheRead === 0 && cumulativeCacheCreation === 0) {
2517
+ return null;
2518
+ }
2519
+ return {
2520
+ cacheRead: cumulativeCacheRead,
2521
+ cacheCreation: cumulativeCacheCreation
2522
+ };
2523
+ } catch {
2524
+ return null;
2525
+ }
2526
+ }
2527
+ /**
2528
+ * Parse a single transcript line for cache data
2529
+ * Returns null if line is not an assistant message with usage
2530
+ */
2531
+ parseLineForCache(line) {
2532
+ try {
2533
+ const entry = JSON.parse(line);
2534
+ if (entry.type !== "assistant") {
2535
+ return null;
2536
+ }
2537
+ const usage = entry.message?.usage;
2538
+ if (!usage) {
2539
+ return null;
2540
+ }
2541
+ const cacheRead = usage.cache_read_input_tokens ?? 0;
2542
+ const cacheCreation = usage.cache_creation_input_tokens ?? 0;
2543
+ if (cacheRead === 0 && cacheCreation === 0) {
2544
+ return null;
2545
+ }
2546
+ return { cacheRead, cacheCreation };
2547
+ } catch {
2548
+ return null;
2549
+ }
2550
+ }
2469
2551
  /**
2470
2552
  * Parse a single transcript line for usage data
2471
2553
  * Returns null if line is not an assistant message with usage
@@ -2480,14 +2562,15 @@ var init_usage_parser = __esm({
2480
2562
  if (!usage) {
2481
2563
  return null;
2482
2564
  }
2483
- if (typeof usage.input_tokens !== "number" || typeof usage.output_tokens !== "number" || typeof usage.cache_read_input_tokens !== "number") {
2565
+ if (typeof usage.input_tokens !== "number" || typeof usage.output_tokens !== "number") {
2484
2566
  return null;
2485
2567
  }
2486
2568
  return {
2487
2569
  input_tokens: usage.input_tokens,
2488
2570
  output_tokens: usage.output_tokens,
2489
2571
  cache_creation_input_tokens: usage.cache_creation_input_tokens ?? 0,
2490
- cache_read_input_tokens: usage.cache_read_input_tokens
2572
+ cache_read_input_tokens: usage.cache_read_input_tokens ?? 0
2573
+ // Default to 0 if missing
2491
2574
  };
2492
2575
  } catch {
2493
2576
  return null;
@@ -2498,11 +2581,11 @@ var init_usage_parser = __esm({
2498
2581
  });
2499
2582
 
2500
2583
  // src/storage/cache-manager.ts
2501
- var import_node_fs2, import_node_os2, import_node_path2, DEFAULT_CACHE_PATH, DEFAULT_EXPIRY_MS, CacheManager;
2584
+ var import_node_fs, import_node_os2, import_node_path2, DEFAULT_CACHE_PATH, DEFAULT_EXPIRY_MS, CacheManager;
2502
2585
  var init_cache_manager = __esm({
2503
2586
  "src/storage/cache-manager.ts"() {
2504
2587
  "use strict";
2505
- import_node_fs2 = require("node:fs");
2588
+ import_node_fs = require("node:fs");
2506
2589
  import_node_os2 = require("node:os");
2507
2590
  import_node_path2 = require("node:path");
2508
2591
  DEFAULT_CACHE_PATH = `${(0, import_node_os2.homedir)()}/.config/claude-scope/cache.json`;
@@ -2575,11 +2658,11 @@ var init_cache_manager = __esm({
2575
2658
  * Load cache from file
2576
2659
  */
2577
2660
  loadCache() {
2578
- if (!(0, import_node_fs2.existsSync)(this.cachePath)) {
2661
+ if (!(0, import_node_fs.existsSync)(this.cachePath)) {
2579
2662
  return { sessions: {}, version: 1 };
2580
2663
  }
2581
2664
  try {
2582
- const content = (0, import_node_fs2.readFileSync)(this.cachePath, "utf-8");
2665
+ const content = (0, import_node_fs.readFileSync)(this.cachePath, "utf-8");
2583
2666
  return JSON.parse(content);
2584
2667
  } catch {
2585
2668
  return { sessions: {}, version: 1 };
@@ -2590,7 +2673,7 @@ var init_cache_manager = __esm({
2590
2673
  */
2591
2674
  saveCache(cache) {
2592
2675
  try {
2593
- (0, import_node_fs2.writeFileSync)(this.cachePath, JSON.stringify(cache, null, 2), "utf-8");
2676
+ (0, import_node_fs.writeFileSync)(this.cachePath, JSON.stringify(cache, null, 2), "utf-8");
2594
2677
  } catch {
2595
2678
  }
2596
2679
  }
@@ -2600,8 +2683,8 @@ var init_cache_manager = __esm({
2600
2683
  ensureCacheDir() {
2601
2684
  try {
2602
2685
  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 });
2686
+ if (!(0, import_node_fs.existsSync)(dir)) {
2687
+ (0, import_node_fs.mkdirSync)(dir, { recursive: true });
2605
2688
  }
2606
2689
  } catch {
2607
2690
  }
@@ -2773,6 +2856,8 @@ var init_cache_metrics_widget = __esm({
2773
2856
  lastSessionId;
2774
2857
  cachedUsage;
2775
2858
  // Cache parsed usage within render cycle
2859
+ cachedCumulativeCache;
2860
+ // Cumulative cache for session
2776
2861
  constructor(theme) {
2777
2862
  super();
2778
2863
  this.theme = theme ?? DEFAULT_THEME;
@@ -2794,9 +2879,14 @@ var init_cache_metrics_widget = __esm({
2794
2879
  /**
2795
2880
  * Calculate cache metrics from context usage data
2796
2881
  * Returns zero metrics if no usage data is available (widget should always be visible)
2882
+ *
2883
+ * @param usage - Current usage data (for total tokens calculation)
2884
+ * @param cumulativeCache - Cumulative cache data for the session (optional)
2797
2885
  */
2798
- calculateMetrics(usage) {
2799
- if (!usage) {
2886
+ calculateMetrics(usage, cumulativeCache) {
2887
+ const cacheRead = cumulativeCache?.cacheRead ?? usage?.cache_read_input_tokens ?? 0;
2888
+ const cacheWrite = cumulativeCache?.cacheCreation ?? usage?.cache_creation_input_tokens ?? 0;
2889
+ if (!usage && !cumulativeCache) {
2800
2890
  return {
2801
2891
  cacheRead: 0,
2802
2892
  cacheWrite: 0,
@@ -2805,10 +2895,8 @@ var init_cache_metrics_widget = __esm({
2805
2895
  savings: 0
2806
2896
  };
2807
2897
  }
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;
2898
+ const inputTokens = usage?.input_tokens ?? 0;
2899
+ const outputTokens = usage?.output_tokens ?? 0;
2812
2900
  const totalInputTokens = cacheRead + cacheWrite + inputTokens;
2813
2901
  const totalTokens = totalInputTokens + outputTokens;
2814
2902
  const hitRate = totalInputTokens > 0 ? Math.min(100, Math.round(cacheRead / totalInputTokens * 100)) : 0;
@@ -2845,18 +2933,21 @@ var init_cache_metrics_widget = __esm({
2845
2933
  });
2846
2934
  }
2847
2935
  }
2848
- if (!usage) {
2936
+ 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);
2937
+ if (!usage || !hasRealUsage) {
2849
2938
  this.cachedUsage = await this.usageParser.parseLastUsage(data.transcript_path);
2850
2939
  } else {
2851
2940
  this.cachedUsage = void 0;
2852
2941
  }
2942
+ this.cachedCumulativeCache = await this.usageParser.parseCumulativeCache(data.transcript_path);
2853
2943
  }
2854
2944
  /**
2855
2945
  * Render the cache metrics display
2856
2946
  */
2857
2947
  renderWithData(data, _context) {
2858
2948
  let usage = data.context_window?.current_usage;
2859
- if (!usage && this.cachedUsage) {
2949
+ 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);
2950
+ if ((!usage || !hasRealUsage) && this.cachedUsage) {
2860
2951
  usage = this.cachedUsage;
2861
2952
  }
2862
2953
  if (!usage) {
@@ -2865,7 +2956,8 @@ var init_cache_metrics_widget = __esm({
2865
2956
  usage = cached.usage;
2866
2957
  }
2867
2958
  }
2868
- const metrics = this.calculateMetrics(usage);
2959
+ const cumulativeCacheToUse = this.cachedCumulativeCache;
2960
+ const metrics = this.calculateMetrics(usage, cumulativeCacheToUse);
2869
2961
  if (!metrics) {
2870
2962
  return null;
2871
2963
  }
@@ -3416,7 +3508,8 @@ var init_context_widget = __esm({
3416
3508
  });
3417
3509
  }
3418
3510
  }
3419
- if (!current_usage) {
3511
+ 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);
3512
+ if (!current_usage || !hasRealUsage) {
3420
3513
  this.cachedUsage = await this.usageParser.parseLastUsage(data.transcript_path);
3421
3514
  } else {
3422
3515
  this.cachedUsage = void 0;
@@ -3425,7 +3518,8 @@ var init_context_widget = __esm({
3425
3518
  renderWithData(data, _context) {
3426
3519
  const { current_usage, context_window_size } = data.context_window;
3427
3520
  let usage = current_usage;
3428
- if (!usage && this.cachedUsage) {
3521
+ 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);
3522
+ if ((!usage || !hasRealUsage) && this.cachedUsage) {
3429
3523
  usage = this.cachedUsage;
3430
3524
  }
3431
3525
  if (!usage) {
@@ -26259,8 +26353,8 @@ init_renderer();
26259
26353
  init_widget_registry();
26260
26354
 
26261
26355
  // src/providers/transcript-provider.ts
26262
- var import_node_fs3 = require("node:fs");
26263
- var import_node_readline2 = require("node:readline");
26356
+ var import_node_fs2 = require("node:fs");
26357
+ var import_node_readline = require("node:readline");
26264
26358
  var TranscriptProvider = class {
26265
26359
  MAX_TOOLS = 20;
26266
26360
  /**
@@ -26269,13 +26363,13 @@ var TranscriptProvider = class {
26269
26363
  * @returns Array of tool entries, limited to last 20
26270
26364
  */
26271
26365
  async parseTools(transcriptPath) {
26272
- if (!(0, import_node_fs3.existsSync)(transcriptPath)) {
26366
+ if (!(0, import_node_fs2.existsSync)(transcriptPath)) {
26273
26367
  return [];
26274
26368
  }
26275
26369
  const toolMap = /* @__PURE__ */ new Map();
26276
26370
  try {
26277
- const fileStream = (0, import_node_fs3.createReadStream)(transcriptPath, { encoding: "utf-8" });
26278
- const rl = (0, import_node_readline2.createInterface)({
26371
+ const fileStream = (0, import_node_fs2.createReadStream)(transcriptPath, { encoding: "utf-8" });
26372
+ const rl = (0, import_node_readline.createInterface)({
26279
26373
  input: fileStream,
26280
26374
  crlfDelay: Infinity
26281
26375
  });
@@ -26554,13 +26648,13 @@ async function routeCommand(command) {
26554
26648
  }
26555
26649
 
26556
26650
  // src/config/config-loader.ts
26557
- var import_node_fs5 = require("node:fs");
26651
+ var import_node_fs4 = require("node:fs");
26558
26652
  var import_promises2 = require("node:fs/promises");
26559
26653
  var import_node_os4 = require("node:os");
26560
26654
  var import_node_path4 = require("node:path");
26561
26655
 
26562
26656
  // src/config/default-config-generator.ts
26563
- var import_node_fs4 = require("node:fs");
26657
+ var import_node_fs3 = require("node:fs");
26564
26658
  var import_node_os3 = require("node:os");
26565
26659
  var import_node_path3 = require("node:path");
26566
26660
  function getDefaultConfigPath() {
@@ -26568,15 +26662,15 @@ function getDefaultConfigPath() {
26568
26662
  }
26569
26663
  async function ensureDefaultConfig() {
26570
26664
  const configPath = getDefaultConfigPath();
26571
- if ((0, import_node_fs4.existsSync)(configPath)) {
26665
+ if ((0, import_node_fs3.existsSync)(configPath)) {
26572
26666
  return;
26573
26667
  }
26574
26668
  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 });
26669
+ if (!(0, import_node_fs3.existsSync)(configDir)) {
26670
+ (0, import_node_fs3.mkdirSync)(configDir, { recursive: true });
26577
26671
  }
26578
26672
  const defaultConfig = generateRichLayout("balanced", "dracula");
26579
- (0, import_node_fs4.writeFileSync)(configPath, JSON.stringify(defaultConfig, null, 2), "utf-8");
26673
+ (0, import_node_fs3.writeFileSync)(configPath, JSON.stringify(defaultConfig, null, 2), "utf-8");
26580
26674
  }
26581
26675
 
26582
26676
  // src/config/config-loader.ts
@@ -26589,7 +26683,7 @@ function getConfigPath() {
26589
26683
  async function loadWidgetConfig() {
26590
26684
  const configPath = getConfigPath();
26591
26685
  await ensureDefaultConfig();
26592
- if (!(0, import_node_fs5.existsSync)(configPath)) {
26686
+ if (!(0, import_node_fs4.existsSync)(configPath)) {
26593
26687
  return null;
26594
26688
  }
26595
26689
  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.35",
4
4
  "description": "Claude Code plugin for session status and analytics",
5
5
  "license": "MIT",
6
6
  "type": "module",