How Claude HUD Resolves and Parses the Transcript Path on Every Render Cycle
Claude HUD resolves the transcript_path from the stdin JSON payload on every ~300 ms render cycle, streams the JSONL transcript file line-by-line to rebuild internal state maps, and passes fresh TranscriptData to the renderer to ensure the display always reflects the latest session activity.
In the jarrodwatts/claude-hud open-source project, transcript path resolution and parsing is the core mechanism that keeps the heads-up display synchronized with Claude Code. Because the renderer is invoked continuously while Claude Code runs, the system re-evaluates the transcript file from scratch each time, guaranteeing that new tools, agent updates, and todo changes appear instantly.
Resolving the Path from Stdin
The resolution process begins with the raw JSON payload supplied by Claude Code. In src/index.ts, the application awaits the stdin reader and extracts the optional path field.
readStdin()(src/stdin.ts) consumesprocess.stdinand returns a strongly-typedStdinDataobject.- At lines 51‑53 of
src/index.ts, the code assigns the path using a nullish coalescing fallback:const transcriptPath = stdin.transcript_path ?? ''.
If the field is missing or the HUD starts before Claude Code creates the session file, the variable becomes an empty string, which downstream logic treats as a signal to skip parsing.
// src/index.ts – path resolution (excerpt)
const stdin = await deps.readStdin(); // 1️⃣ read payload
const transcriptPath = stdin.transcript_path ?? ''; // 2️⃣ extract path
const transcript = await deps.parseTranscript(transcriptPath); // 3️⃣ parse
Guard Clauses and File Validation
Before opening any file handles, parseTranscript in src/transcript.ts performs a synchronous existence check to prevent unnecessary I/O on the first invocation.
At lines 24‑33, the function immediately returns an empty TranscriptData result if the path is falsy or if fs.existsSync returns false. This guard ensures the renderer receives a valid but empty context rather than throwing an error when the transcript has not yet been created.
// src/transcript.ts – guard clause (excerpt)
export async function parseTranscript(transcriptPath: string): Promise<TranscriptData> {
const result: TranscriptData = { tools: [], agents: [], todos: [] };
if (!transcriptPath || !fs.existsSync(transcriptPath)) {
return result; // Early exit for missing files
}
// ... streaming logic
}
Streaming Line-by-Line Parsing
For existing transcripts, the parser uses a memory-efficient streaming approach rather than loading the entire JSONL file into memory. At lines 42‑48 of src/transcript.ts, the code creates a read stream and pipes it into a readline interface configured with crlfDelay: Infinity to handle cross-platform line endings.
// src/transcript.ts – stream setup (excerpt)
const rl = readline.createInterface({
input: fs.createReadStream(transcriptPath),
crlfDelay: Infinity,
});
for await (const line of rl) {
if (!line.trim()) continue;
const entry = JSON.parse(line) as TranscriptLine;
processEntry(entry, toolMap, agentMap, taskIdToIndex, latestTodos, result);
}
This incremental approach allows the HUD to process arbitrarily large session histories without blocking the event loop.
Incremental State Accumulation
As each line is parsed, the processEntry function (lines 76‑156) updates three internal Map instances—toolMap, agentMap, and taskIdToIndex—along with a mutable latestTodos array. These data structures accumulate the live state of the session:
- Tools: Mapped by ID to capture the latest status of each tool invocation.
- Agents: Mapped by identifier to track concurrent agent activities.
- Todos: Accumulated in an array indexed by task ID for quick lookup and update.
- Session Metadata: Custom titles or slugs are extracted to label the HUD context.
Once the stream closes, the function slices the collections to limit memory usage and UI clutter. At lines 68‑73, it retains only the latest 20 tools and latest 10 agents, assigns the session name from either a custom title or the most recent slug, and returns the fully populated TranscriptData object.
// src/transcript.ts – final assembly (excerpt)
result.tools = Array.from(toolMap.values()).slice(-20);
result.agents = Array.from(agentMap.values()).slice(-10);
result.todos = latestTodos;
result.sessionName = customTitle ?? latestSlug;
return result;
Rendering with Fresh Context
The assembled TranscriptData is attached to the RenderContext as ctx.transcript. Sub-modules in src/render/—such as tools-line.ts and todos-line.ts—consume this context to build the status lines. Because the entire pipeline runs on every render tick, any tool completion, agent message, or todo check-off that was written to the JSONL file since the last cycle appears immediately in the terminal HUD.
Summary
- Stdin Resolution: The transcript path is extracted from
stdin.transcript_pathinsrc/index.ts(lines 51‑53) with a fallback to an empty string. - Validation:
parseTranscriptguards against missing files at lines 24‑33 ofsrc/transcript.tsto prevent crashes on startup. - Streaming: Large JSONL files are processed via
fs.createReadStreamandreadline(lines 42‑48) for constant memory usage. - State Maps:
processEntry(lines 76‑156) incrementally updatestoolMap,agentMap, andlatestTodosto reflect the live session state. - Slicing: The parser returns only the 20 most recent tools and 10 most recent agents to optimize render performance.
- Freshness: Because the file is re-streamed every ~300 ms, the HUD always displays the current Claude Code session status.
Frequently Asked Questions
What happens if the transcript file does not exist when Claude HUD starts?
If the transcript_path is falsy or the file does not exist on disk, parseTranscript returns an empty TranscriptData object immediately (lines 24‑33 of src/transcript.ts). The renderer then displays empty states for tools, agents, and todos until Claude Code creates the file and the next render cycle detects it.
Why does Claude HUD re-parse the entire transcript file instead of caching it?
The system prioritizes eventual consistency and simplicity. By re-streaming the JSONL on every ~300 ms cycle, the HUD guarantees that any external modifications or appends made by Claude Code are reflected instantly without implementing complex file-watching or incremental diff logic.
How many historical tools and agents does the HUD retain?
To balance information density with performance, the parser slices the maps before returning. It keeps the latest 20 tool entries and latest 10 agent entries (lines 68‑73 of src/transcript.ts). Older entries are discarded from the render context but remain in the physical JSONL file on disk.
What data structure carries the parsed transcript to the renderer?
The parseTranscript function returns a TranscriptData object (defined in src/types.ts) containing arrays of tools, agents, and todos, plus optional sessionName and sessionStart metadata. This object is attached to the RenderContext and consumed by the modular render functions in src/render/.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →