Claude HUD is a statusline plugin that Claude Code invokes as a subprocess roughly every 300ms. Each invocation receives data, renders output, and exits — there is no persistent daemon.
Data flow
Claude Code → stdin JSON → parse → render lines → stdout → Claude Code displays
↘ transcript_path → parse JSONL → tools/agents/todos
Each render cycle:
- Claude Code passes session data as JSON on stdin.
- Claude HUD reads
transcript_path from that JSON and parses the JSONL transcript for tool and agent activity.
- Config files and OAuth credentials are read for MCP/hooks counts and usage limits.
- All data is combined into a
RenderContext and rendered to stdout.
- Claude Code displays every line Claude HUD prints.
Data sources
1. Stdin JSON
Claude Code passes a JSON object on stdin on every invocation. This data is authoritative — no estimation.
| Field | Description |
|---|
model.display_name | Current model name (e.g. Opus) |
context_window.current_usage | Token counts: input_tokens, output_tokens, cache_creation_input_tokens, cache_read_input_tokens |
context_window.context_window_size | Maximum context window size |
context_window.used_percentage | Native usage percentage (Claude Code v2.1.6+) |
context_window.remaining_percentage | Native remaining percentage (Claude Code v2.1.6+) |
transcript_path | Absolute path to the session transcript file |
cwd | Current working directory |
When used_percentage is present (Claude Code v2.1.6+), Claude HUD uses it directly. Otherwise it calculates (total_tokens / context_window_size) × 100.
Token counts are the sum of input_tokens, cache_creation_input_tokens, and cache_read_input_tokens. Output tokens are not counted against context usage.
2. Transcript JSONL
Claude Code writes each turn to a JSONL file at transcript_path. Claude HUD reads this file on every render cycle to extract tool and agent state.
The parser (src/transcript.ts) processes each line as a JSON object and looks for tool_use and tool_result content blocks:
tool_use block — records the tool name, input, and start time. Creates a running entry.
tool_result block — matches against the tool_use by ID. Sets status to completed or error and records duration.
- Running tools — any
tool_use without a matching tool_result.
TodoWrite calls — the todos array in the input replaces the entire todo list.
Task calls — creates an agent entry with subagent_type, model, and description.
TaskCreate calls — appends a new todo item with subject/description and optional initial status.
TaskUpdate calls — updates an existing todo item’s status or content by task ID or numeric index.
The parser retains the most recent 20 tool entries and 10 agent entries per render cycle.
3. Config files
Read once per render cycle from ~/.claude/settings.json:
| Source | What is read |
|---|
settings.json → mcpServers | Count of configured MCP servers |
settings.json → hooks | Count of configured hooks |
CLAUDE.md files | Count of CLAUDE.md rule files found in the project hierarchy |
These counts are shown in the environment line when display.showConfigCounts is true.
4. OAuth credentials and usage API
When display.showUsage is enabled (the default), Claude HUD reads your subscription usage from the Anthropic API.
Credential sources (in priority order):
- macOS Keychain — Claude Code 2.x stores the current OAuth token here.
~/.claude/.credentials.json — fallback for older Claude Code versions and non-macOS platforms.
The credentials file contains claudeAiOauth.accessToken and claudeAiOauth.subscriptionType. Only Pro, Max, and Team subscriptions have usage limits to display; API users return null and no usage line is shown.
API call:
GET https://api.anthropic.com/api/oauth/usage
Authorization: Bearer <accessToken>
anthropic-beta: oauth-2025-04-20
The response includes five_hour.utilization and seven_day.utilization as 0–100 percentages, plus resets_at timestamps.
Caching:
Because Claude HUD runs as a new process on every render, it uses a file-based cache at ~/.claude/plugins/claude-hud/.usage-cache.json.
| Result | Cache TTL |
|---|
| Successful response | 60 seconds (configurable via usage.cacheTtlSeconds) |
| Failed response | 15 seconds (configurable via usage.failureCacheTtlSeconds) |
| Rate-limited (429) | Exponential backoff: 60s → 120s → 240s → 300s max |
Skipped when:
ANTHROPIC_BASE_URL or ANTHROPIC_API_BASE_URL points to a non-Anthropic host (custom providers).
- Model ID indicates AWS Bedrock (contains
anthropic.claude-).
Context thresholds
The context bar color and behavior change as the context window fills:
| Usage | Color | Behavior |
|---|
| < 70% | Green | Normal display |
| 70–85% | Yellow | Warning color |
| > 85% | Red | Token breakdown shown (when display.showTokenBreakdown is true) |
File structure
src/
├── index.ts # Entry point
├── stdin.ts # Parse Claude's JSON input
├── transcript.ts # Parse transcript JSONL
├── config-reader.ts # Read MCP/rules configs
├── config.ts # Load/validate user config
├── git.ts # Git status (branch, dirty, ahead/behind)
├── usage-api.ts # Fetch usage from Anthropic API
├── types.ts # TypeScript interfaces
└── render/
├── index.ts # Main render coordinator
├── session-line.ts # Compact mode: single line with all info
├── tools-line.ts # Tool activity (opt-in)
├── agents-line.ts # Agent status (opt-in)
├── todos-line.ts # Todo progress (opt-in)
├── colors.ts # ANSI color helpers
└── lines/
├── project.ts # Line 1: model bracket + project + git
├── identity.ts # Line 2a: context bar
├── usage.ts # Line 2b: usage bar
└── environment.ts # Config counts (opt-in)
Render coordinator
render/index.ts is the main coordinator. It selects the layout (expanded or compact), builds the list of output lines, optionally inserts a separator, then prints each line to stdout.
In expanded mode, elementOrder controls which elements appear and in what order. When context and usage are adjacent in the order, they are joined on a single line with │ between them.
In compact mode, session-line.ts renders everything onto one header line. Activity lines (tools, agents, todos) are appended after it.
The coordinator also handles terminal-width wrapping: if a line exceeds the terminal width, it wraps at | or │ separator boundaries rather than mid-token.