How Claude HUD Parses Context Usage from stdin JSON and Native Percentage Fields

Claude HUD reads JSON data from process.stdin, aggregates token counts from context_window.current_usage, and prioritizes the native used_percentage field when available, falling back to manual calculations for legacy Claude Code versions.

Claude HUD is a real-time status line interface for Claude Code that displays context window usage. To render accurate progress bars, it must parse context usage from stdin JSON payloads and handle both native percentage fields and legacy token calculations. This article breaks down the exact parsing logic implemented in the jarrodwatts/claude-hud source code.

Reading the JSON Payload from stdin

The parsing pipeline begins in src/stdin.ts with the readStdin() function. This utility collects all data chunks from process.stdin, joins them into a single string, and parses the result into a StdinData object.

If the process is attached to a TTY or the payload is empty or malformed, readStdin() returns null, causing the HUD to skip rendering for that cycle.

// src/stdin.ts
export async function readStdin(): Promise<StdinData | null> {
  const chunks: Buffer[] = [];
  
  for await (const chunk of process.stdin) {
    chunks.push(chunk);
  }
  
  const data = Buffer.concat(chunks).toString();
  
  if (!data || process.stdin.isTTY) {
    return null;
  }
  
  try {
    return JSON.parse(data) as StdinData;
  } catch {
    return null;
  }
}

Extracting Token Counts from context_window

Once the JSON is parsed, getTotalTokens(stdin) computes the raw token totals by summing three possible counters from stdin.context_window.current_usage:

  • input_tokens
  • cache_creation_input_tokens
  • cache_read_input_tokens

This aggregation ensures the HUD accounts for all token types that Claude Code reports, including cached context that doesn't count against the main window limit.

// src/stdin.ts
function getTotalTokens(stdin: StdinData): number {
  const usage = stdin.context_window?.current_usage;
  if (!usage) return 0;
  
  return (usage.input_tokens || 0) + 
         (usage.cache_creation_input_tokens || 0) + 
         (usage.cache_read_input_tokens || 0);
}

Parsing Native Percentage Fields

Claude Code version 2.1.6+ introduced a native used_percentage field that eliminates manual calculation drift. The getNativePercent() function in src/stdin.ts validates this value, ensuring it is numeric and within the 0-100 range before returning the rounded integer.

When available, this native percentage takes precedence over all fallback calculations, ensuring the HUD displays exactly what Claude Code reports in its /context command.

// src/stdin.ts
function getNativePercent(stdin: StdinData): number | null {
  const percent = stdin.context_window?.used_percentage;
  
  if (typeof percent !== 'number' || percent < 0 || percent > 100) {
    return null;
  }
  
  return Math.round(percent);
}

Fallback Calculations for Legacy Versions

For Claude Code versions prior to 2.1.6, the HUD implements two fallback strategies to estimate context usage when parsing context usage from stdin JSON without native percentage fields.

Plain Context Percentage

The getContextPercent() function attempts to use the native percentage first. If unavailable, it falls back to manual calculation using totalTokens / context_window_size * 100, capping the result at 100%.

Buffered Context Percentage

To match Claude Code's visual "autocompact" buffer behavior, getBufferedPercent() adds a dynamic buffer to the token count. This buffer scales between 5% and 50% based on current usage (defined by AUTOCOMPACT_BUFFER_PERCENT in src/constants.ts).

The function calculates (totalTokens + buffer) / context_window_size * 100, providing the "early warning" visual that grows before hitting the actual limit.

// src/stdin.ts
function getBufferedPercent(stdin: StdinData, totalTokens: number): number {
  // Try native percentage first
  const native = getNativePercent(stdin);
  if (native !== null) return native;
  
  const size = stdin.context_window?.context_window_size;
  if (!size) return 0;
  
  // Calculate dynamic buffer based on usage scale
  const usageRatio = totalTokens / size;
  const scale = usageRatio < 0.5 ? 0.05 : 0.50;
  const buffer = size * AUTOCOMPACT_BUFFER_PERCENT * scale;
  
  return Math.round(((totalTokens + buffer) / size) * 100);
}

Summary

  • Claude HUD reads JSON from stdin via readStdin() in src/stdin.ts, parsing Claude Code's real-time context data into a StdinData object.
  • Token totals aggregate input_tokens, cache_creation_input_tokens, and cache_read_input_tokens via getTotalTokens() to account for all usage types.
  • Native percentage fields (used_percentage) from Claude Code 2.1.6+ are validated and prioritized by getNativePercent(), eliminating calculation drift.
  • Legacy versions fall back to manual calculations in getContextPercent() (plain percentage) and getBufferedPercent() (with autocompact buffer).
  • The AUTOCOMPACT_BUFFER_PERCENT constant from src/constants.ts drives dynamic buffer scaling between 5% and 50% in legacy buffered calculations.

Frequently Asked Questions

What JSON structure does Claude HUD expect from stdin?

Claude HUD expects a JSON object containing a context_window property with current_usage (token counts), context_window_size (total capacity), and optionally used_percentage (native percentage from Claude Code 2.1.6+). The current_usage object includes input_tokens, cache_creation_input_tokens, and cache_read_input_tokens to account for regular and cached input.

How does Claude HUD handle missing or invalid percentage data?

When the native used_percentage field is missing, null, or outside the 0-100 range, getNativePercent() returns null. This triggers fallback logic in getContextPercent() and getBufferedPercent(), which manually calculate usage based on total tokens divided by window size. This ensures the HUD always displays meaningful data even when parsing context usage from stdin JSON on older Claude Code versions.

Why does Claude HUD use a buffered percentage calculation?

The buffered percentage in getBufferedPercent() replicates Claude Code's "autocompact" visual behavior, which adds a dynamic buffer (scaled between 5% and 50% based on current usage) to the token count. This creates an "early warning" system where the visual bar grows before hitting the actual context limit, giving users proactive feedback about approaching capacity constraints. The buffer calculation uses the AUTOCOMPACT_BUFFER_PERCENT constant from src/constants.ts.

Which Claude Code version introduced the native used_percentage field?

Claude Code version 2.1.6 introduced the native used_percentage field in the JSON payload sent to stdin. This version allows Claude HUD to bypass manual calculations and display the exact percentage reported by Claude Code's internal /context command, eliminating calculation drift between the HUD and the IDE. The getNativePercent() function specifically checks for this field and validates it before use.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →