Add opt-in Jujutsu status to compact and expanded layouts with prompt-safe repository probing, bounded non-interactive execution, strict output parsing, Git fallback, and maintainer-reviewed documentation and tests.\n\nPreserves the contribution and commit history from #684.
* feat: add projectLineOrder to customize first-line segment order
Adds a dedicated ordering key for the identity/project line, with
coarse segments shared by the expanded and compact renderers:
model (provider + model + effort; compact mode keeps the context bar
attached), project (path + added dirs + git as one segment), advisor,
sessionName, version, extra, duration, cost, speed, auth.
The key is reordering-only: visibility stays with the display.show*
flags. Unknown entries and duplicates are dropped, and segments
missing from a partial list (including segments added in future
versions) are appended in default order, so existing configs and the
default layout are unchanged. Renderer-specific compact parts (config
counts, usage, prompt cache, ...) keep their current slots, and the
custom line stays pinned by customLinePosition.
Closes#654
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: keep first-line ordering opt-in
* test: cover reordered untrusted full paths
---------
Co-authored-by: Ayush Kumar Sharaf <ayush.sharaf@314ecorp.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Jarrod Watts <35651410+jarrodwatts@users.noreply.github.com>
* feat: support pathLevels: "full" to show the entire absolute cwd
pathLevels was hard-capped at 1-3 segments, with no way to see the
full working directory in the project badge. Add a "full" option
that shows the entire absolute path, and fix the leading slash being
dropped when the full path is shown (split-then-join by "/" loses
the empty leading segment on POSIX absolute paths).
* fix: preserve cross-platform path semantics
---------
Co-authored-by: Jarrod Watts <35651410+jarrodwatts@users.noreply.github.com>
* fix: read the agent model from toolUseResult.resolvedModel
The agents line only rendered a model when the caller passed `model`
explicitly. Whenever a subagent inherits the session model — the common
case — the Agent tool call carries no `model` input, so the label was
dropped entirely.
The harness already reports the answer: the record carrying the
tool_result stamps `toolUseResult.resolvedModel` with the model the
subagent actually runs on, at launch time for async agents. Read that
through sanitizeTranscriptModel, preferring it over the caller's alias,
and compact the raw ID into a statusline-sized label
(claude-opus-4-8[1m] -> opus-4.8).
Routes the caller-supplied alias and the cache deserialize path through
the same sanitizer, so agent.model holds one invariant everywhere.
Bumps TRANSCRIPT_CACHE_VERSION so existing caches re-parse.
* fix: preserve unknown agent model identifiers
---------
Co-authored-by: Jarrod Watts <35651410+jarrodwatts@users.noreply.github.com>
Parse and render bounded model-scoped weekly usage data from Claude Code stdin.
Harden untrusted labels and values, cover expanded and compact layouts, and prevent scoped-only data from overwriting shared external snapshots.
Show detached tags and commits, preserve slash-separated branch links, and accept sanitized plain-text output for explicitly enabled extra commands. Sanitize compact Git refs and add focused regression coverage.
* feat: add display.modelSource for proxy users (auto/stdin/transcript)
Users behind API proxies (cc-switch, LiteLLM, OpenRouter passthrough)
see the wrong model name because Claude Code reports the configured
model, not the one the proxy actually served.
This PR adds a `display.modelSource` config option:
- `auto` (default): uses stdin model for Claude models, transcript model
for non-Claude. Zero impact on existing users.
- `stdin`: always use Claude Code's reported model (current behavior).
- `transcript`: always use the model from the API response's
`message.model` field. Best for proxy users who want the real model.
Implementation:
- TranscriptData.lastAssistantModel: captures the actual model from
each assistant message's `message.model` field
- stdin.resolveModelName(): resolves model based on modelSource config
- project.ts + session-line.ts: use resolveModelName instead of
getModelName
- Cache serialization updated to persist lastAssistantModel
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Harden transcript model display
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude HUD Maintainer <claude-hud-maintainer@users.noreply.github.com>
- Add src/i18n/zh-Hant.ts with full Traditional Chinese translations
- Update Language type to include 'zh-Hant' and 'zh-TW'
- Register zh-Hant locale and zh-TW alias in i18n index
- Update validateLanguage() in config.ts to accept zh-Hant and zh-TW
- Fix session-time.ts: replace getLanguage() === 'zh' with isCjkLanguage()
so zh-Hant also gets correct no-space formatting (e.g. '5m前' not '5m 前')
- Add test coverage for zh-Hant and zh-TW in i18n.test.js
Co-authored-by: YutenC <yuten.c.tcg@tc168.cloud>
Claude Code reports ultracode as a normal level (xhigh) in stdin, so the
statusline recovers it from the transcript: the ultra_effort_enter /
ultra_effort_exit attachment markers and the immediate /effort command
output, latest signal wins. When active, the reported level is wrapped as
ultracode(<level>), taken as-is rather than assumed to be xhigh.
Also drop the ps-based parent-process --effort fallback (#471); the effort
label now comes solely from Claude Code stdin, removing a subprocess spawn
on every statusline refresh.
Co-authored-by: Claude <noreply@anthropic.com>
* feat(display): show auth method and logged-in account on the first line
Adds an opt-in segment at the end of the first HUD line showing the
subscription plan of the current login (e.g. "Claude Max 20x") and the
logged-in account, derived from the oauthAccount block that Claude Code
persists in {CLAUDE_CONFIG_DIR}.json.
New display options (all default off):
- display.showAuth: plan label, e.g. "Claude Max 20x" / "API Key"
- display.showAuthUser: email local part (fallback: displayName)
- display.authUserLength: truncation length, 0 = full (default 8)
* fix(auth): strip whole ANSI sequences via shared sanitizeDisplayText helper
Replaces the local codepoint-class regex with the existing
sanitizeDisplayText() util so CSI/OSC sequences are removed entirely
instead of leaving parameter bytes behind (evil\x1b[31m@… -> evil@…),
matching how other externally-sourced display text is sanitized.
* fix(auth): prefer active API key over stale OAuth profile
---------
Co-authored-by: BBleae <yukinoshita.reimu@gmail.com>
Co-authored-by: Jarrod Watts <35651410+jarrodwatts@users.noreply.github.com>
* fix: correct effort suffix placement and guard check inconsistencies
the effort level tag (like "max") was showing up after the provider name
instead of after the model — so you'd see "Opus | Bedrock max" when it
should be "Opus max | Bedrock". small thing but it looked wrong.
also fixed a few spots where opt-in features (showDuration, showConfigCounts)
were using !== false checks instead of === true, which could show them
unexpectedly if the config object was partially constructed.
cleaned up duplicate debug namespaces too — two modules were both using
'context' and two were using 'config', which made debug output confusing.
* test: cover effort and opt-in render guards
---------
Co-authored-by: Jarrod Watts <jarrodwatts@users.noreply.github.com>
Document the absolute read-path requirement for external usage snapshots and add focused tests for relative read rejection plus private speed-cache directory and file modes.
Add opt-in provider label placement for custom proxy workflows while preserving the existing default model badge order. Compact and expanded layouts now share the same badge formatter.
Closes#620
Add a comment explaining that the existing bedrockOpus46 test exercises
regex normalization (display_name only), not real Bedrock pricing behavior.
Add a complementary test confirming that real Bedrock sessions (with
model.id containing 'anthropic.claude-') correctly skip cost estimation
via the isBedrockModelId early return.
Co-authored-by: Jarrod Watts <jarrodwatts16@gmail.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Update Opus 4.5+ local cost estimates to the official standard Anthropic rate of $5/M input and $25/M output while keeping older Opus 4.0/4.1 estimates on $15/M input and $75/M output. Adds focused coverage for Opus 4.5/4.6, Bedrock-style model IDs, and the older fallback. Verified locally with npm ci, npm run build, and node --test tests/core.test.js tests/render.test.js. GitHub CI passed on Node 18 and 20. Fixes#622. Supersedes #624.
Require CLAUDE_HUD_ALLOW_EXTRA_CMD before executing --extra-cmd, tighten cache file permissions, and document the local command trust boundary. Maintainer follow-up removed generated dist artifacts from the PR and added extra opt-in parsing coverage. Verified locally with npm ci, npm test, and node --test tests/extra-cmd.test.js. Fixes#618.
Merge missing seven-day usage from a configured external snapshot when stdin only supplies the five-hour window. Adds regression coverage for the partial-stdin fallback path. Verified locally with npm ci, npm run build, and node --test tests/index.test.js tests/external-usage.test.js.
令牌 is the established Chinese rendering of "token" in the
authentication/security sense only (access token → 访问令牌). For the
NLP/LLM sense — the unit of text shown by the HUD's token counters —
the standard term is 词元, officially designated as the Chinese name
for the AI-domain term "token" by the China National Committee for
Terms in Sciences and Technologies on 2026-03-25.
dist/ is intentionally untouched; it is auto-compiled by the
build-dist workflow.
Previously, external-snapshot balance_label was shown only when stdin
rate_limits were absent (mutually exclusive). Rate-limit windows (5h/7d)
and a prepaid/metered dollar balance are complementary data, so both
should be visible when available.
Changes:
- index.ts: when stdin provides rate_limits, also read the external
snapshot for a balance_label (only when externalUsagePath is set)
and merge it onto usageData
- session-line.ts: render balance_label as an additive segment after
the windows block rather than as an exclusive leading branch;
standalone balance (no window data) preserves existing behaviour
- tests/render.test.js: add test for combined windows + balance display
Backward-compatible: users without externalUsagePath configured see
no behaviour change (no extra file read, no extra segment).
Addresses review feedback from #573:
- Sanitize both `ctx.transcript.advisorModel` and `display.advisorOverride`
through the existing `sanitizeDisplayText` helper before rendering so
ANSI ESC, OSC, C0/C1 control, and bidi sequences cannot reach the
terminal. Defence-in-depth: re-sanitize the prettified result too.
- Cap advisor display at 64 chars at the render layer
(MAX_ADVISOR_DISPLAY_LEN), regardless of source.
- In the transcript parser, capture `advisorModel` only from records
whose `type === 'assistant'` (matching the documented schema) and
hard-cap the captured value at 64 chars (ADVISOR_MODEL_MAX_LEN) so a
malformed log cannot persist an oversized value through the JSON
cache. Bumps TRANSCRIPT_CACHE_VERSION 6 → 7. The cache deserializer
also re-applies the cap as belt-and-braces against legacy entries.
- Add 9 new tests covering: CSI/OSC stripping (ANSI ESC + BEL + DEL
bytes), bidi mark stripping (RLO/PDF), oversize transcript truncation,
oversize override truncation (display-layer), sanitize-collapses-to-
empty fallback, non-assistant record rejection (user / custom-title /
system), inline placement in expanded layout (renderProjectLine), and
inline placement in compact layout (renderSessionLine).
- Document `display.showAdvisor` and `display.advisorOverride` in
README.md and README.zh.md configuration tables.
`npm test`: 637 / 639 pass + 1 skip + 1 pre-existing unrelated failure
(`countConfigs cache: miss on nested rules additions`, fails identically
on `main`).
Surfaces the model configured via Claude Code's `/advisor` command so it
sits inline on the project line:
[Opus 4.7 (1M context)] │ AthenaKB git:(main*) │ Advisor: Opus 4.7
Context ██░░░░░░░░ 19%
## How it works
Claude Code stamps an `advisorModel` field onto every assistant record
in the session transcript when `/advisor` is configured. The transcript
parser already walks every line for tool / agent / todo state, so this
just captures the latest non-empty `advisorModel` into the existing
`TranscriptData` shape with no extra IO.
`renderProjectLine` then appends the prettified label (e.g.
`claude-opus-4-7` → `Opus 4.7`) as another part on the same row,
respecting the existing ` │ ` separator and ANSI handling. A
`display.showAdvisor` boolean (default false) gates the whole block;
`display.advisorOverride` lets users force a custom label if needed.
## What's included
- `TranscriptData.advisorModel` field + parser capture
- TRANSCRIPT_CACHE_VERSION bumped 5 → 6 to invalidate caches that
predate the new field
- `display.showAdvisor` / `display.advisorOverride` config + validation
- `renderProjectLine` integration + `prettifyAdvisorId` helper that
handles both canonical IDs and short aliases
- i18n labels for en (`Advisor`) and zh-Hans (`顾问`)
- `configure.md` Element Mapping + Turn Off/On flow entries
- 15 new tests covering ID prettification, render gating, transcript
capture (latest-wins, empty-string-ignored, missing-field fallback),
and config validation
## Notes
- `dist/` is intentionally untouched per CONTRIBUTING.md
- One pre-existing failure remains in `countConfigs cache: miss on
nested rules additions` — it fails identically on main without any
of my changes