When TodoWrite is called, the taskIdToIndex map was cleared and
rebuilt from scratch. However, TodoItem objects only contain content
and status fields — no taskId. This meant all taskId mappings from
prior TaskCreate calls were permanently lost, causing subsequent
TaskUpdate operations to silently fail.
Fix: Before clearing the map, build a reverse lookup of content →
taskIds from the existing state. After replacing latestTodos with the
new TodoWrite items, re-register taskId mappings for items whose
content matches a previously-known task. Stale mappings (for content
no longer present) are naturally dropped.
Adds two tests:
- TaskCreate → TodoWrite → TaskUpdate flow preserves taskId
- TodoWrite-only regression test (no TaskCreate involved)
Cover both rendering paths (expanded + compact) to verify that:
- modelFormat compact/short correctly strips context suffix and prefix
- modelOverride completely replaces the model name in the badge
- modelOverride takes precedence over modelFormat
- loadConfig structural test validates new display fields
Users can now set a fully custom model name in their config:
{ "display": { "modelOverride": "zane's intelligent opus 4.6" } }
When set, the override completely replaces the auto-detected model
name (while preserving the provider qualifier like "| Bedrock").
Follows the same pattern as customLine: string type, max 80 chars,
empty string means disabled (falls through to modelFormat).
Claude Code may include the context window size in display_name
(e.g. "Opus 4.6 (1M context)"), but the HUD already shows context
usage via the context bar — making the parenthetical redundant.
This adds stripContextSuffix() which removes any parenthetical
containing the word "context" from the display name. It handles
common variants like "(1M context)", "(200k context)",
"(with 1M context)", and "(extended context window)" while
preserving non-context parentheticals like "(beta)" or "(preview)".
Before: [Opus 4.6 (1M context) | Bedrock]
After: [Opus 4.6 | Bedrock]
- Fix detectLanguage() to follow POSIX priority: LC_ALL > LC_MESSAGES > LANG
- Fix Chinese format.resetsIn from broken wrap-around grammar to prefix form
- Fix loadConfig() to call mergeConfig({}) when no config file exists,
ensuring detectLanguage() runs for auto-detection
- Add setLanguage('en') guard to render.test.js for locale-independent tests
- Add dedicated i18n test suite (tests/i18n.test.js) covering t(),
detectLanguage(), and mergeConfig language handling
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add i18n infrastructure with automatic language detection from system
locale (LANG/LC_ALL/LC_MESSAGES) and manual override via config.json.
Translates all user-facing HUD labels, status messages, and format
strings. English remains the default.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
MAX_RECENT_COMPLETED and MAX_AGENTS_SHOWN constants at the top
Dedup seen set in renderAgentsLine
getStatusIcon() extracted with failed case handled
Math.max(0, ...) guard in formatElapsed
Hour-aware branch at the bottom of formatElapsed
Claude Code now writes subagent tool_use entries with "name": "Agent"
in the transcript JSONL, but processEntry() only checked for "Task".
This meant the agents line never displayed any agents.
Keep backwards compatibility with older transcripts that used "Task".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When both 5h and 7d usage windows are displayed, the 7d gauge had no
label prefix. This adds a dim-styled "Weekly" label (matching the
Context/Usage label style) and removes colons from usage window labels
for consistency with other HUD labels.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The ANSI escape patterns only matched SGR color sequences (\x1b[...m),
leaving OSC 8 hyperlink sequences (\x1b]8;;URL\x1b\ ... \x1b]8;;\x1b\)
counted as visible characters in visualLength() and sliceVisible().
This caused HUD elements to be incorrectly measured as wider than they
are, so line-wrapping kicked in too early and pushed elements like the
context bar and session duration off-screen on terminals with OSC 8
hyperlinks enabled (Ghostty, iTerm2, WezTerm, Kitty).
Fix: extend both patterns to also match \x1b][^\x1b]*\x1b\ (OSC sequences
terminated by ST), which covers OSC 8 and any future OSC sequences.
countConfigs() performs ~15 file I/O operations on every invocation.
Since the statusLine plugin spawns a new process ~every 300ms, this means
~50 filesystem ops/sec per session. With multiple concurrent sessions,
this scales to ~1500 ops/sec for config data that rarely changes.
Add a sentinel-based mtime cache (following the pattern established in
transcript.ts) that stats ~4-10 sentinel paths and serves cached results
when no config files have changed. Steady-state cost drops from ~15
syscalls to ~4-10 stat calls + 1 cache read.
Cache files are stored under ~/.claude/plugins/claude-hud/config-cache/
with SHA-256 hashed keys for cwd+configDir isolation. Cache misses
trigger a fresh recompute and cache write.
Known limitation: nested rules/ subdirectory changes are not monitored
(only the top-level rules/ directory mtime is checked).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Parse assistant message usage fields from the transcript JSONL to
accumulate input, output, cache creation and cache read tokens across
the entire session. Display the total as a new HUD line controlled by
the `display.showSessionTokens` config option (default: false).
Changes:
- types.ts: add SessionTokenUsage interface
- transcript.ts: accumulate usage from assistant messages
- config.ts: add showSessionTokens option
- render/lines/session-tokens.ts: new render component
- render/index.ts & session-line.ts: integrate into both layouts
- commands/configure.md: add Session tokens to configure flow
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>