cc-context-stats 1.12.1 → 1.13.1

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
@@ -91,7 +91,7 @@ graph LR
91
91
 
92
92
  ## Customization
93
93
 
94
- Every element in the status line can be individually colored and toggled. Configuration lives in `~/.claude/statusline.conf` (created automatically on first run).
94
+ Every element in the status line can be individually colored and toggled. Configuration lives in `~/.claude/statusline.conf` (created automatically on first run). See [`examples/statusline.conf`](examples/statusline.conf) for the canonical reference with all parameters documented.
95
95
 
96
96
  ### Status Line Anatomy
97
97
 
@@ -334,6 +334,16 @@ Add to `~/.claude/settings.json`:
334
334
  }
335
335
  ```
336
336
 
337
+ **(Optional) Copy the example config for full customization:**
338
+
339
+ A default config is created automatically on first run. To start from the fully-documented example instead, copy it before launching:
340
+
341
+ ```bash
342
+ cp examples/statusline.conf ~/.claude/statusline.conf
343
+ ```
344
+
345
+ See [`examples/statusline.conf`](examples/statusline.conf) for all available settings with detailed explanations.
346
+
337
347
  Restart Claude Code. MI score and context stats appear in your status bar immediately.
338
348
 
339
349
  ### Real-Time Dashboard
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-context-stats",
3
- "version": "1.12.1",
3
+ "version": "1.13.1",
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": {
@@ -6,8 +6,8 @@
6
6
  * Configuration:
7
7
  * Create/edit ~/.claude/statusline.conf and set:
8
8
  *
9
- * autocompact=true (when autocompact is enabled in Claude Code - default)
10
- * autocompact=false (when you disable autocompact via /config in Claude Code)
9
+ * autocompact=false (when autocompact is disabled in Claude Code - default)
10
+ * autocompact=true (when you enable autocompact via /config in Claude Code)
11
11
  *
12
12
  * token_detail=true (show exact token count like 64,000 - default)
13
13
  * token_detail=false (show abbreviated tokens like 64.0k)
@@ -114,35 +114,49 @@ function getMIColor(mi, utilization, greenColor, yellowColor, redColor) {
114
114
  /**
115
115
  * Determine context zone indicator (P/C/D/X/Z) based on token usage.
116
116
  * Returns { zone, colorName }.
117
+ * zoneConfig is an optional object of threshold overrides (0 = use default).
117
118
  */
118
- function getContextZone(usedTokens, contextWindowSize) {
119
+ function getContextZone(usedTokens, contextWindowSize, zoneConfig) {
119
120
  if (contextWindowSize === 0) {
120
121
  return { zone: 'Plan', colorName: 'green' };
121
122
  }
122
123
 
123
- const isLarge = contextWindowSize >= LARGE_MODEL_THRESHOLD;
124
+ const zc = zoneConfig || {};
125
+
126
+ const lmt = zc.large_model_threshold || LARGE_MODEL_THRESHOLD;
127
+ const isLarge = contextWindowSize >= lmt;
124
128
 
125
129
  if (isLarge) {
126
- if (usedTokens < ZONE_1M_P_MAX) {
130
+ const pMax = zc.zone_1m_plan_max || ZONE_1M_P_MAX;
131
+ const cMax = zc.zone_1m_code_max || ZONE_1M_C_MAX;
132
+ const dMax = zc.zone_1m_dump_max || ZONE_1M_D_MAX;
133
+ const xMax = zc.zone_1m_xdump_max || ZONE_1M_X_MAX;
134
+
135
+ if (usedTokens < pMax) {
127
136
  return { zone: 'Plan', colorName: 'green' };
128
137
  }
129
- if (usedTokens < ZONE_1M_C_MAX) {
138
+ if (usedTokens < cMax) {
130
139
  return { zone: 'Code', colorName: 'yellow' };
131
140
  }
132
- if (usedTokens < ZONE_1M_D_MAX) {
141
+ if (usedTokens < dMax) {
133
142
  return { zone: 'Dump', colorName: 'orange' };
134
143
  }
135
- if (usedTokens < ZONE_1M_X_MAX) {
144
+ if (usedTokens < xMax) {
136
145
  return { zone: 'ExDump', colorName: 'dark_red' };
137
146
  }
138
147
  return { zone: 'Dead', colorName: 'gray' };
139
148
  }
140
149
 
141
- // Standard models (< 500k context)
142
- const dumpZoneTokens = Math.floor(contextWindowSize * ZONE_STD_DUMP_ZONE);
143
- const warnStart = Math.max(0, dumpZoneTokens - ZONE_STD_WARN_BUFFER);
144
- const hardLimitTokens = Math.floor(contextWindowSize * ZONE_STD_HARD_LIMIT);
145
- const deadZoneTokens = Math.floor(contextWindowSize * ZONE_STD_DEAD_ZONE);
150
+ // Standard models
151
+ const dumpRatio = zc.zone_std_dump_ratio || ZONE_STD_DUMP_ZONE;
152
+ const warnBuf = zc.zone_std_warn_buffer || ZONE_STD_WARN_BUFFER;
153
+ const hardLim = zc.zone_std_hard_limit || ZONE_STD_HARD_LIMIT;
154
+ const deadRat = zc.zone_std_dead_ratio || ZONE_STD_DEAD_ZONE;
155
+
156
+ const dumpZoneTokens = Math.floor(contextWindowSize * dumpRatio);
157
+ const warnStart = Math.max(0, dumpZoneTokens - warnBuf);
158
+ const hardLimitTokens = Math.floor(contextWindowSize * hardLim);
159
+ const deadZoneTokens = Math.floor(contextWindowSize * deadRat);
146
160
 
147
161
  if (usedTokens < warnStart) {
148
162
  return { zone: 'Plan', colorName: 'green' };
@@ -284,6 +298,23 @@ const COLOR_CONFIG_KEYS = {
284
298
  color_separator: 'separator',
285
299
  };
286
300
 
301
+ // Zone threshold config keys (integer token counts)
302
+ const ZONE_INT_KEYS = new Set([
303
+ 'zone_1m_plan_max',
304
+ 'zone_1m_code_max',
305
+ 'zone_1m_dump_max',
306
+ 'zone_1m_xdump_max',
307
+ 'zone_std_warn_buffer',
308
+ 'large_model_threshold',
309
+ ]);
310
+
311
+ // Zone threshold config keys (float ratios 0-1)
312
+ const ZONE_FLOAT_KEYS = new Set([
313
+ 'zone_std_dump_ratio',
314
+ 'zone_std_hard_limit',
315
+ 'zone_std_dead_ratio',
316
+ ]);
317
+
287
318
  /**
288
319
  * Return the visible width of a string after stripping ANSI escape sequences.
289
320
  */
@@ -384,7 +415,7 @@ function getGitInfo(projectDir, magentaColor, cyanColor) {
384
415
 
385
416
  function readConfig() {
386
417
  const config = {
387
- autocompact: true,
418
+ autocompact: false,
388
419
  tokenDetail: true,
389
420
  showDelta: true,
390
421
  showSession: true,
@@ -393,6 +424,7 @@ function readConfig() {
393
424
  showMI: false,
394
425
  miCurveBeta: 0,
395
426
  colors: {},
427
+ zoneConfig: {},
396
428
  };
397
429
  const configPath = path.join(os.homedir(), '.claude', 'statusline.conf');
398
430
 
@@ -403,29 +435,219 @@ function readConfig() {
403
435
  if (!fs.existsSync(configDir)) {
404
436
  fs.mkdirSync(configDir, { recursive: true });
405
437
  }
406
- const defaultConfig = `# Autocompact setting - sync with Claude Code's /config
407
- autocompact=true
408
-
409
- # Token display format
438
+ const defaultConfig = `\
439
+ # ============================================================================
440
+ # cc-context-stats — statusline configuration
441
+ # ============================================================================
442
+ #
443
+ # Copy this file to: ~/.claude/statusline.conf
444
+ # Windows: %USERPROFILE%\\.claude\\statusline.conf
445
+ #
446
+ # Full reference:
447
+ # https://github.com/luongnv89/cc-context-stats/blob/main/docs/configuration.md
448
+ #
449
+ # Format:
450
+ # - key=value (no spaces around '=')
451
+ # - Lines starting with '#' are comments
452
+ # - Unrecognized keys are silently ignored
453
+ # - Missing or invalid values fall back to built-in defaults
454
+ #
455
+ # ============================================================================
456
+
457
+
458
+ # ─── Display Settings ───────────────────────────────────────────────────────
459
+ #
460
+ # These boolean flags control which elements appear in the statusline.
461
+ # Any value other than "false" (case-insensitive) is treated as true.
462
+
463
+ # Autocompact buffer display.
464
+ # When true, 22.5% of the context window is reserved for Claude Code's
465
+ # autocompact feature. This affects the "free tokens" calculation.
466
+ # Must match your Claude Code setting — check with: /config
467
+ # true -> shows [AC:45k] buffer in statusline
468
+ # false -> shows [AC:off]
469
+ autocompact=false
470
+
471
+ # Token display format.
472
+ # true = exact count with commas (e.g., 64,000 free)
473
+ # false = abbreviated with suffix (e.g., 64.0k free)
474
+ # Also affects the delta display (+2,500 vs +2.5k).
410
475
  token_detail=true
411
476
 
412
- # Show token delta since last refresh (adds file I/O on every refresh)
413
- # Disable if you don't need it to reduce overhead
477
+ # Show token delta since last refresh (e.g., +2,500).
478
+ # Displays how many tokens were consumed since the previous statusline update.
479
+ # Requires file I/O on every refresh to read the previous state.
480
+ # Disable if you want to reduce disk overhead.
414
481
  show_delta=true
415
482
 
416
- # Show session_id in status line
483
+ # Show the session ID at the end of the statusline.
484
+ # Useful when running multiple Claude Code instances to identify sessions.
485
+ # Double-click in terminal to select and copy.
417
486
  show_session=true
418
487
 
419
- # Custom colors - use named colors or hex (#rrggbb)
420
- # Available: color_green, color_yellow, color_red, color_blue, color_magenta, color_cyan
421
- # Examples:
422
- # color_green=#7dcfff
423
- # color_red=#f7768e
488
+ # Show input/output token breakdown.
489
+ # Reserved for future use currently read but not displayed.
490
+ show_io_tokens=true
491
+
492
+ # Disable rotating text and icon animations for accessibility.
493
+ # false = animations enabled (default)
494
+ # true = static display, no motion
495
+ reduced_motion=false
496
+
497
+
498
+ # ─── Model Intelligence (MI) ────────────────────────────────────────────────
499
+ #
500
+ # MI measures how effectively the model uses its context window. The score
501
+ # ranges from 0.000 (fully degraded) to 1.000 (optimal). As context fills,
502
+ # MI degrades following a model-specific curve.
503
+
504
+ # Show the MI score in the statusline (e.g., MI:0.918).
505
+ # When enabled, also requires state file I/O for tracking.
506
+ # false = MI score hidden (default)
507
+ # true = MI score visible
508
+ show_mi=false
509
+
510
+ # Override the MI degradation curve beta for all models.
511
+ # Each model has a built-in profile that controls how quickly MI degrades:
512
+ # opus = 1.8 (retains quality longest, steep drop near end)
513
+ # sonnet = 1.5 (moderate degradation)
514
+ # haiku = 1.2 (degrades earliest)
515
+ # Set to 0 to use the model-specific profile (recommended).
516
+ # Set a positive value (e.g., 1.5) to override for all models.
517
+ mi_curve_beta=0
518
+
519
+
520
+ # ─── Zone Threshold Overrides ───────────────────────────────────────────────
521
+ #
522
+ # Zones indicate how much context pressure your session is under:
523
+ # Plan (P) = plenty of room, ideal for planning and exploration
524
+ # Code (C) = normal coding zone, context is filling but healthy
525
+ # Dump (D) = getting full, consider wrapping up or starting fresh
526
+ # ExDump (X) = critical, autocompact may trigger, quality degrading
527
+ # Dead (Z) = context exhausted, start a new session
528
+ #
529
+ # There are two threshold sets: one for large models (1M+ context) using
530
+ # absolute token counts, and one for standard models using ratios (0-1).
531
+ #
532
+ # Uncomment and set a positive value to override the built-in defaults.
533
+ # Invalid values (negative, non-numeric, ratios outside 0-1) are ignored
534
+ # with a warning to stderr.
535
+
536
+ # Context windows >= this value use 1M-class thresholds (token count).
537
+ # Models below this threshold use the standard ratio-based zones.
538
+ # large_model_threshold=500000
539
+
540
+ # --- 1M-Class Models (context >= large_model_threshold) ---
541
+ # Values are absolute token counts for zone boundaries (tokens used).
542
+ # zone_1m_plan_max=70000 # Plan -> Code boundary
543
+ # zone_1m_code_max=100000 # Code -> Dump boundary
544
+ # zone_1m_dump_max=250000 # Dump -> ExDump boundary
545
+ # zone_1m_xdump_max=275000 # ExDump -> Dead boundary
546
+
547
+ # --- Standard Models (context < large_model_threshold) ---
548
+ # Ratios are 0-1 fractions of the total context window.
549
+ # zone_std_dump_ratio=0.40 # Dump zone starts at 40% utilization
550
+ # zone_std_warn_buffer=30000 # Show warning this many tokens before dump zone
551
+ # zone_std_hard_limit=0.70 # Hard limit at 70% utilization
552
+ # zone_std_dead_ratio=0.75 # Dead zone starts at 75% utilization
553
+
554
+
555
+ # ─── Base Color Slots ───────────────────────────────────────────────────────
556
+ #
557
+ # Override the 6 base palette colors used for MI-based traffic-light coloring
558
+ # and as fallbacks for per-property colors (see next section).
559
+ #
560
+ # Accepts named colors or hex codes (#rrggbb).
561
+ #
562
+ # Named colors (18 available):
563
+ # Standard: black, red, green, yellow, blue, magenta, cyan, white
564
+ # Bright: bright_black, bright_red, bright_green, bright_yellow,
565
+ # bright_blue, bright_magenta, bright_cyan, bright_white
566
+ # Special: bold_white, dim
567
+ #
568
+ # Hex colors: any #rrggbb value (requires 24-bit color terminal support)
569
+ #
570
+ # Unrecognized values are ignored with a warning to stderr.
571
+
572
+ # Traffic-light colors — used for MI score and context zone indicators.
573
+ # Colors are determined by BOTH MI score and context utilization:
574
+ # color_green -> MI >= 0.90 AND context < 40% (model operating well)
575
+ # color_yellow -> MI in (0.80, 0.90) OR context in [40%, 80%) (pressure building)
576
+ # color_red -> MI <= 0.80 OR context >= 80% (significant degradation)
577
+ color_green=#7dcfff
578
+ color_yellow=#e0af68
579
+ color_red=#f7768e
580
+
581
+ # Legacy element fallback colors:
582
+ # color_blue -> fallback for project name (if color_project_name not set)
583
+ # color_magenta -> fallback for branch name (if color_branch_name not set)
584
+ # color_cyan -> git change-count brackets (e.g., [3])
585
+ color_blue=#7aa2f7
586
+ color_magenta=#bb9af7
587
+ color_cyan=#2ac3de
588
+
589
+
590
+ # ─── Per-Property Colors ────────────────────────────────────────────────────
591
+ #
592
+ # Override individual statusline elements. These take precedence over
593
+ # base color slots above.
594
+ #
595
+ # Fallback chain: per-property key -> base color slot -> built-in default
596
+ #
597
+ # For example, if color_project_name is not set, it falls back to color_blue
598
+ # (if set), then to the built-in cyan.
599
+
600
+ # Context tokens remaining — the most critical info.
601
+ # When not set, uses zone traffic-light color (green/yellow/red) automatically.
602
+ # Set explicitly to use a fixed color regardless of zone.
603
+ # color_context_length=bold_white
604
+
605
+ # Project directory name (e.g., "my-project").
606
+ color_project_name=bright_cyan
607
+
608
+ # Git branch name (e.g., "main").
609
+ color_branch_name=bright_magenta
610
+
611
+ # MI score display (e.g., "MI:0.918").
612
+ # When not set, uses MI-based traffic-light color automatically.
613
+ color_mi_score=#ff9e64
614
+
615
+ # Zone indicator label (e.g., "Plan", "Code", "Dump").
616
+ # When not set, uses zone traffic-light color automatically.
617
+ # color_zone=bright_green
618
+
619
+ # Structural elements: model name, token delta, session ID.
620
+ # "dim" makes these visually recede so primary info stands out.
621
+ color_separator=dim
622
+
623
+
624
+ # ─── Statusline Layout Reference ────────────────────────────────────────────
625
+ #
626
+ # The statusline elements are displayed in this order (highest priority first):
627
+ #
628
+ # project_name | branch [changes] | tokens_free (%) | Zone | MI:score | +delta | Model | session_id
629
+ #
630
+ # Example output:
631
+ # my-project | main [3] | 64,000 free (32.0%) | Code | MI:0.918 | +2,500 | Opus 4.6 | abc-123
632
+ #
633
+ # If the terminal is too narrow, lower-priority elements are dropped:
634
+ # 1. session_id (dropped first)
635
+ # 2. model name
636
+ # 3. token delta
637
+ # 4. MI score
638
+ # 5. zone indicator
639
+ # 6. context info
640
+ # 7. git info
641
+ # 8. project name (always shown, never dropped)
424
642
  `;
425
643
  fs.writeFileSync(configPath, defaultConfig);
426
644
  } catch (e) {
427
645
  process.stderr.write(`[statusline] warning: failed to create config: ${e.message}\n`);
646
+ return config;
428
647
  }
648
+ }
649
+
650
+ if (!fs.existsSync(configPath)) {
429
651
  return config;
430
652
  }
431
653
 
@@ -464,6 +686,24 @@ show_session=true
464
686
  if (ansi) {
465
687
  config.colors[COLOR_CONFIG_KEYS[keyTrimmed]] = ansi;
466
688
  }
689
+ } else if (ZONE_INT_KEYS.has(keyTrimmed)) {
690
+ const v = parseInt(rawValue, 10);
691
+ if (!isNaN(v) && v > 0) {
692
+ config.zoneConfig[keyTrimmed] = v;
693
+ } else {
694
+ process.stderr.write(
695
+ `[statusline] warning: ${keyTrimmed} must be a positive integer, ignoring '${rawValue}'\n`
696
+ );
697
+ }
698
+ } else if (ZONE_FLOAT_KEYS.has(keyTrimmed)) {
699
+ const v = parseFloat(rawValue);
700
+ if (!isNaN(v) && v > 0 && v < 1) {
701
+ config.zoneConfig[keyTrimmed] = v;
702
+ } else {
703
+ process.stderr.write(
704
+ `[statusline] warning: ${keyTrimmed} must be between 0 and 1, ignoring '${rawValue}'\n`
705
+ );
706
+ }
467
707
  }
468
708
  }
469
709
  } catch (e) {
@@ -572,7 +812,7 @@ process.stdin.on('end', () => {
572
812
  : `${(freeTokens / 1000).toFixed(1)}k`;
573
813
 
574
814
  // Zone indicator — determines color for both context info and zone label
575
- const zoneResult = getContextZone(usedTokens, totalSize);
815
+ const zoneResult = getContextZone(usedTokens, totalSize, config.zoneConfig);
576
816
  const zoneAnsi = zoneAnsiColor(zoneResult.colorName);
577
817
 
578
818
  // Context info uses zone color (traffic-light), with per-property override