cc-context-stats 1.9.1 → 1.10.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-context-stats",
3
- "version": "1.9.1",
3
+ "version": "1.10.0",
4
4
  "description": "Monitor your Claude Code session context in real-time - track token usage and never run out of context",
5
5
  "main": "scripts/statusline.js",
6
6
  "bin": {
@@ -22,7 +22,7 @@
22
22
 
23
23
  # === CONFIGURATION ===
24
24
  # shellcheck disable=SC2034
25
- VERSION="1.9.1"
25
+ VERSION="1.10.0"
26
26
  COMMIT_HASH="dev" # Will be replaced during installation
27
27
  STATE_DIR=~/.claude/statusline
28
28
  CONFIG_FILE=~/.claude/statusline.conf
@@ -37,49 +37,48 @@ const ROTATION_THRESHOLD = 10000;
37
37
  const ROTATION_KEEP = 5000;
38
38
 
39
39
  // Model Intelligence constants (hardcoded, not configurable)
40
- const MI_WEIGHT_CPS = 0.60;
41
- const MI_WEIGHT_ES = 0.25;
42
- const MI_WEIGHT_PS = 0.15;
43
- const MI_GREEN_THRESHOLD = 0.65;
44
- const MI_YELLOW_THRESHOLD = 0.35;
45
- const MI_PRODUCTIVITY_TARGET = 0.2;
40
+ const MI_GREEN_THRESHOLD = 0.70;
41
+ const MI_YELLOW_THRESHOLD = 0.40;
42
+
43
+ // Per-model degradation profiles: beta controls curve shape
44
+ // Higher beta = quality retained longer (degradation happens later)
45
+ const MODEL_PROFILES = {
46
+ opus: 1.8,
47
+ sonnet: 1.5,
48
+ haiku: 1.2,
49
+ default: 1.5,
50
+ };
51
+
52
+ /**
53
+ * Match model_id to degradation beta.
54
+ */
55
+ function getModelProfile(modelId) {
56
+ const lower = (modelId || '').toLowerCase();
57
+ for (const family of ['opus', 'sonnet', 'haiku']) {
58
+ if (lower.includes(family)) return MODEL_PROFILES[family];
59
+ }
60
+ return MODEL_PROFILES.default;
61
+ }
46
62
 
47
63
  /**
48
64
  * Compute Model Intelligence score.
49
- * Returns { mi, cps, es, ps }.
65
+ * MI(u) = max(0, 1 - u^beta) where beta is model-specific.
66
+ * Returns { mi }.
50
67
  */
51
- function computeMI(usedTokens, contextWindowSize, cacheRead, totalContext,
52
- deltaLines, deltaOutput, beta = 1.5) {
68
+ function computeMI(usedTokens, contextWindowSize, modelId, betaOverride) {
53
69
  // Guard clause
54
70
  if (contextWindowSize === 0) {
55
- return { mi: 1.0, cps: 1.0, es: 1.0, ps: 0.5 };
71
+ return { mi: 1.0 };
56
72
  }
57
73
 
58
- // CPS
59
- const u = usedTokens / contextWindowSize;
60
- const cps = u > 0 ? Math.max(0, 1 - Math.pow(u, beta)) : 1.0;
61
-
62
- // ES
63
- let es;
64
- if (totalContext === 0) {
65
- es = 1.0;
66
- } else {
67
- const cacheHitRatio = cacheRead / totalContext;
68
- es = 0.3 + 0.7 * cacheHitRatio;
69
- }
74
+ const betaFromProfile = getModelProfile(modelId || '');
75
+ const beta = (betaOverride && betaOverride > 0) ? betaOverride : betaFromProfile;
70
76
 
71
- // PS
72
- let ps;
73
- if (deltaOutput === null || deltaOutput === undefined || deltaOutput <= 0) {
74
- ps = 0.5;
75
- } else {
76
- const ratio = deltaLines / deltaOutput;
77
- const normalized = Math.min(1.0, ratio / MI_PRODUCTIVITY_TARGET);
78
- ps = 0.2 + 0.8 * normalized;
79
- }
77
+ const u = usedTokens / contextWindowSize;
78
+ if (u <= 0) return { mi: 1.0 };
80
79
 
81
- const mi = MI_WEIGHT_CPS * cps + MI_WEIGHT_ES * es + MI_WEIGHT_PS * ps;
82
- return { mi, cps, es, ps };
80
+ const mi = Math.max(0, 1 - Math.pow(u, beta));
81
+ return { mi };
83
82
  }
84
83
 
85
84
  /**
@@ -292,7 +291,7 @@ function readConfig() {
292
291
  showIoTokens: true,
293
292
  reducedMotion: false,
294
293
  showMI: true,
295
- miCurveBeta: 1.5,
294
+ miCurveBeta: 0,
296
295
  colors: {},
297
296
  };
298
297
  const configPath = path.join(os.homedir(), '.claude', 'statusline.conf');
@@ -515,9 +514,6 @@ process.stdin.on('end', () => {
515
514
  const stateFile = path.join(stateDir, stateFileName);
516
515
  let hasPrev = false;
517
516
  let prevTokens = 0;
518
- let prevOutputTokens = 0;
519
- let prevLinesAdded = 0;
520
- let prevLinesRemoved = 0;
521
517
  try {
522
518
  if (fs.existsSync(stateFile)) {
523
519
  hasPrev = true;
@@ -535,9 +531,6 @@ process.stdin.on('end', () => {
535
531
  const prevCacheRead = parseInt(parts[6], 10) || 0;
536
532
  prevTokens = prevCurInput + prevCacheCreation + prevCacheRead;
537
533
  // For MI productivity score
538
- prevOutputTokens = parseInt(parts[2], 10) || 0;
539
- prevLinesAdded = parseInt(parts[8], 10) || 0;
540
- prevLinesRemoved = parseInt(parts[9], 10) || 0;
541
534
  } else {
542
535
  // Old format - single value
543
536
  prevTokens = parseInt(lastLine, 10) || 0;
@@ -563,22 +556,9 @@ process.stdin.on('end', () => {
563
556
 
564
557
  // Calculate and display MI score if enabled
565
558
  if (showMI) {
566
- let deltaLines, deltaOutput;
567
- if (hasPrev) {
568
- const deltaLA = linesAdded - prevLinesAdded;
569
- const deltaLR = linesRemoved - prevLinesRemoved;
570
- deltaLines = deltaLA + deltaLR;
571
- deltaOutput = totalOutputTokens - prevOutputTokens;
572
- } else {
573
- deltaLines = 0;
574
- deltaOutput = null;
575
- }
576
- const miResult = computeMI(
577
- usedTokens, totalSize, cacheRead, usedTokens,
578
- deltaLines, deltaOutput, miCurveBeta
579
- );
559
+ const miResult = computeMI(usedTokens, totalSize, modelId, miCurveBeta);
580
560
  const miColor = getMIColor(miResult.mi, cGreen, cYellow, cRed);
581
- miInfo = ` ${miColor}MI:${miResult.mi.toFixed(2)}${RESET}`;
561
+ miInfo = ` ${miColor}MI:${miResult.mi.toFixed(3)}${RESET}`;
582
562
  }
583
563
 
584
564
  // Only append if context usage changed (avoid duplicates from multiple refreshes)