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

> Learn how Claude HUD parses context usage from stdin JSON and native percentage fields. Discover its efficient token aggregation and fallback calculation methods for older Claude Code versions.

- Repository: [Jarrod Watts/claude-hud](https://github.com/jarrodwatts/claude-hud)
- Tags: internals
- Published: 2026-03-18

---

**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`](https://github.com/jarrodwatts/claude-hud/blob/main/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.

```typescript
// 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.

```typescript
// 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`](https://github.com/jarrodwatts/claude-hud/blob/main/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.

```typescript
// 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`](https://github.com/jarrodwatts/claude-hud/blob/main/src/constants.ts)).

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

```typescript
// 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`](https://github.com/jarrodwatts/claude-hud/blob/main/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`](https://github.com/jarrodwatts/claude-hud/blob/main/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`](https://github.com/jarrodwatts/claude-hud/blob/main/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.