fix(hooks): correct statusline context scaling to match autocompact buffer

The statusline uses a hardcoded 80% scaling factor for context usage,
but Claude Code's actual autocompact buffer is 16.5% (usable context is
83.5%). This inflates the displayed percentage and causes the context
monitor's WARNING/CRITICAL thresholds to fire prematurely.

Replace the 80% scaling with proper normalization against the 16.5%
autocompact buffer. Adjust color thresholds to intuitive levels
(50/65/80% of usable context).

Closes #769

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Tibsfox
2026-03-03 03:10:45 -08:00
committed by Lex Christopherson
parent 3d926496d9
commit 59dfad9775

View File

@@ -18,14 +18,15 @@ process.stdin.on('end', () => {
const session = data.session_id || '';
const remaining = data.context_window?.remaining_percentage;
// Context window display (shows USED percentage scaled to 80% limit)
// Claude Code enforces an 80% context limit, so we scale to show 100% at that point
// Context window display (shows USED percentage scaled to usable context)
// Claude Code reserves ~16.5% for autocompact buffer, so usable context
// is 83.5% of the total window. We normalize to show 100% at that point.
const AUTO_COMPACT_BUFFER_PCT = 16.5;
let ctx = '';
if (remaining != null) {
const rem = Math.round(remaining);
const rawUsed = Math.max(0, Math.min(100, 100 - rem));
// Scale: 80% real usage = 100% displayed
const used = Math.min(100, Math.round((rawUsed / 80) * 100));
// Normalize: subtract buffer from remaining, scale to usable range
const usableRemaining = Math.max(0, ((remaining - AUTO_COMPACT_BUFFER_PCT) / (100 - AUTO_COMPACT_BUFFER_PCT)) * 100);
const used = Math.max(0, Math.min(100, Math.round(100 - usableRemaining)));
// Write context metrics to bridge file for the context-monitor PostToolUse hook.
// The monitor reads this file to inject agent-facing warnings when context is low.
@@ -48,12 +49,12 @@ process.stdin.on('end', () => {
const filled = Math.floor(used / 10);
const bar = '█'.repeat(filled) + '░'.repeat(10 - filled);
// Color based on scaled usage (thresholds adjusted for new scale)
if (used < 63) { // ~50% real
// Color based on usable context thresholds
if (used < 50) {
ctx = ` \x1b[32m${bar} ${used}%\x1b[0m`;
} else if (used < 81) { // ~65% real
} else if (used < 65) {
ctx = ` \x1b[33m${bar} ${used}%\x1b[0m`;
} else if (used < 95) { // ~76% real
} else if (used < 80) {
ctx = ` \x1b[38;5;208m${bar} ${used}%\x1b[0m`;
} else {
ctx = ` \x1b[5;31m💀 ${bar} ${used}%\x1b[0m`;