Commit Graph

68 Commits

Author SHA1 Message Date
Xy
cb407d0534 feat: add customLine display support (#223)
Add a static `display.customLine` config field that renders a user-defined
phrase (max 80 chars) in Claude orange on the project line, joined with
the standard │ separator.

- config.ts: add customLine to HudConfig.display with validation
- colors.ts: add claudeOrange() using 256-color (38;5;208)
- project.ts: append customLine to expanded mode project line
- session-line.ts: append customLine to compact mode parts
- setup.md: add "Custom line" option to Step 4
- configure.md: add Q5 Custom Line to both Flow A and Flow B
2026-03-20 11:52:29 +11:00
hoklims
8e51163c96 fix: render showSpeed and showDuration in expanded layout (#232)
* fix: render showSpeed and showDuration in expanded layout

These config options were only implemented in the compact layout
(renderSessionLine in session-line.ts). When using lineLayout: "expanded",
enabling display.showSpeed or display.showDuration had no effect.

This adds both options to renderProjectLine (the expanded layout's
project line), matching the compact layout behavior:
- showSpeed: output token speed (tok/s)
- showDuration: session duration timer

Fixes #221

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add renderProjectLine tests for showSpeed and showDuration

Address Copilot review feedback by adding test coverage for the
new expanded layout rendering of speed and duration:
- duration shown when showDuration is true
- duration omitted when showDuration is false
- speed code path doesn't crash when showSpeed is true
- speed omitted when showSpeed is false

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: cover expanded layout speed rendering

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Jarrod Watts <jarrod@cubelabs.xyz>
2026-03-20 11:45:39 +11:00
Abhishek Tiwari
faacda01ee fix: hud not appearing after setup without restart (#213) (#250)
* fix: detect macOS and show restart hint when HUD initializes without stdin (closes #213)

* fix: prompt user to restart Claude Code after config write for HUD setup

* fix: update assertion to check if output starts with initializing message

* Align restart messaging in docs

---------

Co-authored-by: Jarrod Watts <jarrod@cubelabs.xyz>
2026-03-20 11:45:33 +11:00
d 🔹
2330284631 fix: clarify usage time format — show 'resets in' instead of ambiguous elapsed/total (#244)
* fix: clarify usage time format — show 'resets in' instead of ambiguous elapsed/total

Fixes #240

The format `(2h 43m / 5h)` was confusing because it mimics the common
`(elapsed / total)` pattern, but actually showed time remaining until
reset. Changed to `(resets in 2h 43m)` which is unambiguous.

Before: `Usage █░░░░░░░░░ 7% (2h 43m / 5h)`
After:  `Usage █░░░░░░░░░ 7% (resets in 2h 43m)`

* test: cover usage reset wording

---------

Co-authored-by: d 🔹 <258577966+voidborne-d@users.noreply.github.com>
Co-authored-by: Jarrod Watts <jarrod@cubelabs.xyz>
2026-03-20 11:43:34 +11:00
Pan Hu
e952a399f1 fix: stop terminal scrolling to top on tool execution (#248)
* fix: detect terminal width via stderr when stdout is piped

When Claude Code runs the statusLine as a subprocess, stdout is captured
(piped), so process.stdout.columns is undefined. With no terminal width,
long lines aren't wrapped by the HUD — the terminal wraps them silently
instead. Claude Code counts \n characters to determine how many lines to
erase on the next render, so the physical line count diverges from what
Claude Code expects. Over successive renders the cursor drifts upward into
the scrollback buffer, causing the terminal to jump to the top on every
tool execution (https://github.com/jarrodwatts/claude-hud/issues/209).

Fix: fall back to process.stderr.columns before the COLUMNS env var.
When the statusLine subprocess is spawned, only stdout is redirected;
stderr remains connected to the real TTY and returns the correct width.
With accurate width, lines are properly wrapped in the HUD output and
Claude Code's line count stays consistent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: drop dist artifacts and cover stderr width

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Jarrod Watts <jarrod@cubelabs.xyz>
2026-03-20 11:41:51 +11:00
Anna Terek
e5d384af35 fix: scale progress bars based on terminal width (#237)
* fix: scale progress bars based on terminal width

Resolves #231

Progress bars (coloredBar/quotaBar) were hardcoded to width 10,
causing line wrapping in narrow terminals.

Now bars scale dynamically based on process.stdout.columns:
- Wide terminal (>=100 cols): width 10 (default)
- Medium terminal (60-99 cols): width 6
- Narrow terminal (<60 cols): width 4

Affected files:
- src/render/session-line.ts (context bar + usage bars)
- src/render/lines/identity.ts (context bar in expanded layout)
- src/render/lines/usage.ts (quota bars in expanded layout)

* fix: also check COLUMNS env var as fallback for bar width

For cross-platform compatibility (zsh, macOS, Windows),
getBarWidth() now also checks process.env.COLUMNS when
process.stdout.columns is unavailable (non-TTY, piped output).

* refactor: extract getAdaptiveBarWidth to shared utility

- Remove duplicated getBarWidth() from 3 render files
- Add src/utils/terminal.ts with exported getAdaptiveBarWidth()
- Add Math.floor() for consistency with getTerminalWidth()
- All 3 render files now import from shared utility (DRY)

* test: add unit tests for getAdaptiveBarWidth

Tests cover:
- Narrow terminal (<60 cols) → 4
- Medium terminal (60-99 cols) → 6
- Wide terminal (>=100 cols) → 10
- Boundary values (59, 60, 99, 100)
- Non-TTY/piped output (undefined columns) → 10 (default)
- Fallback to COLUMNS env var
- No terminal info at all → 10 (default)

* style: simplify JSDoc to match project comment style

---------

Co-authored-by: Anna Terek <wabalabudabdab@users.noreply.github.com>
2026-03-20 11:41:40 +11:00
Chris Anthony
30f46e7fbd Fix extraLabel not displayed in expanded layout (#243)
The --extra-cmd feature only rendered extraLabel in compact mode via
renderSessionLine. In the default expanded layout, extraLabel was
collected but never passed to any render function.

Append extraLabel to the project line in expanded mode, consistent
with its placement on the session line in compact mode.

Fixes #242
2026-03-20 11:41:01 +11:00
Jarrod Watts
256b811156 chore: bump version to 0.0.10 (#208)
* chore: bump version to 0.0.10

* chore: sync lockfile version metadata

* docs: clarify 1M context window compatibility
2026-03-14 11:39:26 +11:00
Jarrod Watts
a089537a11 fix: surface stale usage syncing during rate limits (#207) 2026-03-14 11:25:42 +11:00
Jarrod Watts
14763584eb feat: add configurable HUD color overrides (#206)
* feat: add configurable HUD color overrides

* docs: preserve manual color overrides in configure flow
2026-03-14 11:20:23 +11:00
Jarrod Watts
d4abb17a20 fix: prefer account-scoped keychain credentials (#205)
* fix: prefer account-scoped keychain credentials

* test: block generic fallback after empty account-scoped keychain lookup

* test: block generic fallback after account lookup errors

* fix: block cross-service keychain credential fallback
2026-03-14 11:19:35 +11:00
Hwang In-wook
603e48f699 feat: show reset time for 7-day usage in text-only mode (#194)
* feat: show reset time for 7-day usage in text-only mode

- When usageBarEnabled is false, 7d usage now displays reset time,
- consistent with the existing 5h usage text-only behavior.

* test: cover text-mode 7-day reset countdown

---------

Co-authored-by: Jarrod Watts <jarrod@cubelabs.xyz>
2026-03-14 11:14:56 +11:00
Paul
e8d64924bc fix: resilient usage display under API rate limiting (#193)
* fix: resilient usage display under API rate limiting

The Anthropic usage API rate-limits to ~1 call per 5 minutes. With the
previous 60s cache TTL, 4 out of 5 API calls returned 429, causing the
HUD to permanently display "(429)" instead of actual usage data.

Three-layer fix:
- Increase cache TTL from 60s to 5 minutes to match rate limit window
- Preserve lastGoodData in cache across rate-limited periods so the HUD
  always shows the best available data instead of errors
- Exponential backoff (60s→120s→240s→5min cap) with Retry-After header
  support for consecutive 429 responses

Also show "syncing..." instead of raw HTTP status on first-run rate limit.

* Update usage-api.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix: harden 429 cache fallback behavior

* test: stabilize usage cache suite after rebase

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Jarrod Watts <jarrod@cubelabs.xyz>
2026-03-14 11:11:36 +11:00
Brandon Wie
086603e7ca fix: treat zero-byte lock file as stale to prevent permanent busy state (#203)
* fix: treat zero-byte lock file as stale to prevent permanent busy state

If the HUD process crashes between fs.openSync(lockPath, 'wx') and
fs.writeFileSync(fd, timestamp), a 0-byte .usage-cache.lock remains.
readLockTimestamp() returns null for unparseable content, and the
existing guard (lockTimestamp != null && expired) never fires, leaving
the cache permanently stuck in 'busy' state.

Add a lockTimestamp === null guard that uses fs.statSync().mtimeMs to
distinguish a crash leftover (old mtime → remove and retry) from an
active writer that is between openSync and writeFileSync (recent mtime
→ return busy). The original != null stale-timestamp path is unchanged.

Closes #202

* test: stabilize stale-lock regression coverage

* test: make usage-api suite self-build for source-only PRs

---------

Co-authored-by: Jarrod Watts <jarrod@cubelabs.xyz>
2026-03-14 11:06:09 +11:00
Xingxing
069f194fb2 fix(context): scale autocompact buffer by raw usage (#190)
* fix(context): scale autocompact buffer by raw usage to avoid inflated percentages at low context

Previously the buffered context percentage applied a flat 22.5% buffer
regardless of actual usage. This caused the HUD to show ~28% context
used immediately after /clear or at session start, when real usage was
only ~5%. The fix scales the buffer linearly: zero buffer at ≤5% raw
usage, ramping to full buffer at ≥50%, matching when autocompact
actually kicks in.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: harden autocompact buffer startup coverage

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Jarrod Watts <jarrod@cubelabs.xyz>
2026-03-14 10:57:49 +11:00
yelo
ce5960dcd8 Add configurable usage cache TTL settings (#175)
* feat(config): add configurable usage cache TTL settings

Add cacheTtlSeconds and failureCacheTtlSeconds to the usage section of
config.json, allowing users to control how often the Anthropic usage API
is fetched (default 60s) and how quickly failures are retried (default 15s).

* docs: document usage cache TTL config options

* refactor(usage): bundle cache TTL params into single CacheTtls object

Replace cacheTtlMs/failureCacheTtlMs pair with a CacheTtls type wherever
they appear together, reducing parameter count in readCacheState, readCache,
waitForFreshCache, and UsageApiDeps.

* chore(build): drop dist changes from pr

---------

Co-authored-by: Jarrod Watts <jarrod@cubelabs.xyz>
2026-03-09 15:13:22 +11:00
yelo
8c247a0ab2 Skip usage api for custom provider (#176)
* fix(usage): skip API call when using custom provider

Check ANTHROPIC_BASE_URL / ANTHROPIC_API_BASE_URL env vars and skip
OAuth usage API call when user is configured to use a custom provider
(e.g., via cc-switch). This prevents unnecessary API failures when
the user is not using Anthropic's default endpoint.

* test(usage): add tests for custom API endpoint detection

Cover isUsingCustomApiEndpoint behavior via getUsage integration:
- ANTHROPIC_BASE_URL with custom endpoint returns null without fetch
- ANTHROPIC_API_BASE_URL with custom endpoint returns null without fetch
- Empty ANTHROPIC_BASE_URL proceeds normally (falsy check)
- Default endpoint with/without trailing slash proceeds normally

* docs: note that custom API endpoints skip usage display

* fix(usage): tighten custom endpoint detection

---------

Co-authored-by: Jarrod Watts <jarrod@cubelabs.xyz>
2026-03-09 14:57:51 +11:00
Jarrod Watts
499ddc881e fix(context): adjust autocompact fallback buffer (#184) 2026-03-09 14:37:25 +11:00
Peter Zerg
46b827a302 fix(usage-api): restore User-Agent to claude-code/2.1 to fix 429 (#177)
PR #174 (67ddceb) accidentally reverted the User-Agent from
'claude-code/2.1' back to 'claude-hud'. Anthropic's /api/oauth/usage
endpoint rejects non-official User-Agent strings with 429.

This was the root cause of issue #173 — not request volume or
failure cache TTL. Same token, same IP:
- User-Agent: claude-hud      → 429 (always)
- User-Agent: claude-code/2.1 → 200 with usage data

Restores the fix from PR #168 (ceb1127).

Fixes #173

Co-authored-by: Peter Yan <yanxiaozerg1997@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 12:14:30 +11:00
Jarrod Watts
67ddceb38a fix(usage): prevent cross-process usage API stampedes (#174)
* fix(usage): dedupe concurrent usage API refreshes

* chore: stop tracking dist artifacts

* chore: restore tracked dist artifacts
2026-03-06 18:05:16 +11:00
seb
6c8c89cb19 feat(config): repair expanded element ordering (#166)
Co-authored-by: Jarrod Watts <jarrod@cubelabs.xyz>
2026-03-06 10:55:44 +11:00
xiangboit
ceb112777c fix(usage-api): change User-Agent to avoid Anthropic 429 rate limiting (#168)
* fix(usage-api): change User-Agent to avoid Anthropic 429 rate limiting

Anthropic applies stricter rate limits to non-official User-Agent
strings. The current 'claude-hud/1.0' consistently receives 429
responses while 'claude-code/2.1' succeeds for the same token.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test(usage-api): cover user-agent constant

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Jarrod Watts <jarrod@cubelabs.xyz>
2026-03-06 10:55:11 +11:00
myaiexp
5467f29e50 feat(config): add showSessionName toggle (default off) (#170)
* feat(config): add showSessionName toggle (default off)

Session name display from #155 is now opt-in via display.showSessionName
config. This addresses user feedback requesting the ability to hide the
session name. Added to setup onboarding and configure command flows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test(docs): cover session-name default behavior

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Jarrod Watts <jarrod@cubelabs.xyz>
2026-03-06 10:44:05 +11:00
xiangboit
9b491d09eb fix(usage-api): proxy CONNECT tunnel broken by premature socket return (#167)
* fix(usage-api): do not return socket from createConnection in proxy tunnel agent

Node.js _http_agent.js calls `oncreate(null, newSocket)` immediately
when createConnection returns a truthy value, which causes the HTTP
request to be written directly to the raw proxy socket before the
CONNECT handshake completes. Proxies like Clash reject this with
400 Bad Request, surfacing as persistent 403/network errors in the
Usage display.

Return undefined instead so the socket is only delivered via the
async callback after the CONNECT tunnel + TLS handshake succeeds.

Fixes proxy CONNECT failures with Clash and similar HTTP proxies.

* test(usage-api): cover proxy CONNECT request ordering

---------

Co-authored-by: Jarrod Watts <jarrod@cubelabs.xyz>
2026-03-05 23:14:15 +11:00
Jarrod Watts
9141620f45 fix(render): avoid non-breaking space output in status line (#165) 2026-03-05 18:36:06 +11:00
Jarrod Watts
a47f5b7905 fix: address Windows setup, Bedrock labels, and usage API reliability (#164)
* docs(setup): handle win32 bash vs powershell command paths

* fix(stdin): normalize bedrock model ids when display_name is missing

* fix(usage): harden proxy oauth fallback and timeout handling
2026-03-05 18:32:44 +11:00
Fredrik Averpil
4d31c33682 feat: add showProject config to hide project name (#136)
* feat: add showProject config to hide project name from statusline

Adds `display.showProject` (default: true) to control whether the
project path is displayed. When set to false, the project name and
its associated git info are hidden from both compact and expanded
layouts.

* fix: keep git status visible when project name is hidden

---------

Co-authored-by: Jarrod Watts <jarrod@cubelabs.xyz>
2026-03-03 17:00:33 +11:00
Jarrod Watts
9ae17fb644 fix: consolidate CLAUDE_CONFIG_DIR handling and keychain fallback (#160)
* fix: consolidate CLAUDE_CONFIG_DIR handling and keychain fallback

* chore: remove dist artifacts from this PR

* chore: bump version to 0.0.8

* docs: expand 0.0.8 changelog from post-0.0.7 commits
2026-03-03 16:47:15 +11:00
Jarrod Watts
6ff4212238 fix: prevent HUD rows disappearing in narrow terminals (#159)
* fix(render): handle narrow terminals without dropping rows

* chore: drop dist artifacts from issue-151 PR

* fix(render): preserve multiline activity under narrow widths
2026-03-03 15:26:30 +11:00
Jarrod Watts
26eddbaec9 fix: prefer subscription plan label over API env var (#158) 2026-03-03 14:19:19 +11:00
Jarrod Watts
1792060612 feat(config): add contextValue remaining mode (#157) 2026-03-03 14:18:46 +11:00
KwCCCC
eb81ffb1bd fix: display reset time in days when >= 24 hours (#132)
* fix: display reset time in days when >= 24 hours

The 7-day usage window reset countdown shows raw hours (e.g., "151h 59m")
which is hard to read. Format as days + hours when >= 24h (e.g., "6d 7h").

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: cover day-based usage reset formatting

---------

Co-authored-by: KwCCCC <KwCCCC@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Jarrod Watts <jarrod@cubelabs.xyz>
2026-03-03 14:08:17 +11:00
myaiexp
bdfa4454b3 feat: show session name in statusline (#155)
* feat: show session name in statusline

Reads the session slug (auto-generated) and custom title (set via
/rename) from the transcript JSONL and displays it in dim text after
the project/git info on both expanded and compact layouts.

Custom title takes priority over auto-generated slug when both exist.

* test: add session name coverage and harden integration spawn

---------

Co-authored-by: Jarrod Watts <jarrod@cubelabs.xyz>
2026-03-03 13:54:01 +11:00
Ricky (aka 利器君)
bd520e42d7 fix: prevent double-counting CLAUDE.md when cwd is home directory (#141)
* fix: prevent double-counting CLAUDE.md when cwd is home directory

When the working directory is the user's home directory, the project
scope check for {cwd}/.claude/CLAUDE.md resolves to the same path as
the user scope check (~/.claude/CLAUDE.md), causing the HUD to display
"2 CLAUDE.md" instead of "1 CLAUDE.md". Skip the project scope
.claude/ checks when cwd equals the home directory.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: handle home cwd config counting edge cases

* fix: avoid home cwd double-counting for rules and settings

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Jarrod Watts <jarrod@cubelabs.xyz>
2026-03-03 13:48:30 +11:00
seb
dc3e34ed4b fix: handle object layout values in migrateConfig() (#144)
Add typeof guard to only run legacy string migration when layout is a
string. When layout is an object (written by third-party config editors),
extract nested lineLayout, showSeparators, and pathLevels to top level.

- Widen LegacyConfig type to accept Record<string, unknown>
- Export mergeConfig for direct testing of migration paths
- Add test cases for all migration scenarios
2026-03-03 13:43:25 +11:00
Jarrod Watts
10193cc94a Bump marketplace version to 0.0.7 (#117)
* feat: show API billing indicator in model badge

When ANTHROPIC_API_KEY is present in the environment, display
[Opus | API] in red instead of [Opus | Max]. This helps users
who manage both Max plan and API key access (e.g., work vs personal)
immediately see when they're running on API billing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Bump marketplace version to 0.0.7

---------

Co-authored-by: Fielding Johnston <fielding@justfielding.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 13:53:01 +11:00
Jarrod Watts
ffef15fc33 feat: redesign default layout to clean 2-line display (#112)
New default: model+project on line 1, context+usage bars combined on line 2.
All optional features (tools, agents, todos) hidden by default with
setup onboarding step to enable them.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 13:17:23 +11:00
Jarrod Watts
9fc2858619 feat: detect bedrock provider (#111) 2026-02-03 12:47:13 +11:00
Jarrod Watts
dec6efa509 feat(render): add output speed display (#110) 2026-02-03 12:36:19 +11:00
Jarrod Watts
0ac11a1f36 fix(usage): show api failure reason (#109) 2026-02-03 12:33:07 +11:00
Jarrod Watts
eafe12d876 feat(render): allow token context display (#108) 2026-02-03 12:20:00 +11:00
Jarrod Watts
f25627d7f8 feat(config): add seven-day usage threshold (#107) 2026-02-03 12:15:13 +11:00
Jarrod Watts
44adb2631d fix(transcript): support task todo updates (#106) 2026-02-03 12:14:27 +11:00
Jarrod Watts
c4582e9831 fix(render): keep hud to one line (#105) 2026-02-03 12:13:32 +11:00
Jarrod Watts
17475d3f61 fix(extra-cmd): improve robustness and add tests (#94)
- Add debug logging for failures (uses DEBUG=claude-hud pattern)
- Support --extra-cmd=value syntax (in addition to --extra-cmd value)
- Reject empty values with debug warning
- Add security comment explaining shell injection is intentional (user CLI input)
- Export sanitize() for testability
- Add 30 unit tests for sanitize, arg parsing, timeout, malformed JSON

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 16:11:23 +11:00
Jarrod Watts
32cd0ca56b feat: add --extra-cmd CLI argument for custom status labels (#92)
- Add `--extra-cmd` CLI flag to execute a user-specified command
- Command must return JSON with a `label` field, e.g., `{"label": "$1.23/day"}`
- Includes security hardening:
  - sanitize() to strip terminal escape sequences (CSI, OSC, control chars, bidi)
  - maxBuffer: 10KB limit to prevent memory issues
  - Label truncation to 50 chars max
- Display extraLabel in session line when present

Co-authored-by: Johnny Wang <johnnywang1991@msn.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 15:24:29 +11:00
Aster
e0df6fdf90 feat: add usageBarEnabled config option for quota display style (#85)
* feat: add usageBarEnabled config option for quota display style

Add configurable display style for usage limits:
- usageBarEnabled: true → visual bar (██░░ 25%)
- usageBarEnabled: false → text format (5h: 25%)

Changes:
- config.ts: Add usageBarEnabled option (default: true)
- colors.ts: Add quotaBar() and getQuotaColor() with blue color scheme
- usage.ts, session-line.ts: Conditional rendering based on config

Closes #84

* fix: add clamp guard to coloredBar for consistency

Apply the same safeWidth/safePercent guards from quotaBar to coloredBar
to prevent RangeError on malformed input values.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: revert dist files to main

Remove build artifacts from PR diff. CI will rebuild dist/ after merge.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: add usageBarEnabled: false to baseContext

Ensures existing tests continue to check text format behavior.
The new bar format is opt-in via config.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Aster Kim <aster@spoonlabs.com>
Co-authored-by: Jarrod Watts <jarrod@cubelabs.xyz>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 15:03:12 +11:00
Nikolay Bratanov
2a13e6f828 fix: resolve symlinks in path comparison for auto-execute (#87)
* fix: resolve symlinks in path comparison for auto-execute

The HUD fails to output when ~/.claude is a symlink (common on macOS
with iCloud). This happens because process.argv[1] contains the symlink
path while fileURLToPath resolves to the real path.

Use realpathSync to normalize both paths before comparison.

Fixes #86

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: add try/catch fallback for realpathSync

Wrap realpathSync calls in try/catch to handle edge cases where the path
doesn't exist, has permission issues, or has a broken symlink chain.
Falls back to raw path comparison on errors.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Jarrod Watts <jarrod@cubelabs.xyz>
2026-01-19 14:54:48 +11:00
Jarrod Watts
1cffbdd57b feat(layout): add expanded multi-line layout mode (#76)
* feat(layout): add expanded multi-line layout mode

Split the overloaded session line into semantic lines for better readability:

- Identity line: model, plan, context bar, duration
- Project line: path, git status
- Environment line: config counts (CLAUDE.md, rules, MCPs, hooks)
- Usage line: rate limits with reset times

New config options:
- `lineLayout`: 'compact' | 'expanded' (default: expanded for new users)
- `showSeparators`: boolean (orthogonal to layout)
- `usageThreshold`: show usage line only when >= N%
- `environmentThreshold`: show env line only when counts >= N

Backward compatible: old `layout` config is automatically migrated.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address code review feedback

- Fix usage threshold to use max(5h, 7d) so high 7d usage isn't hidden
  when 5h is null
- Update stale comment in session-line.ts (now compact layout only)
- Remove non-null assertions in identity.ts by hoisting planName

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: apply thresholds to compact layout for consistency

- Add environmentThreshold gating to config counts in compact mode
- Add usageThreshold with max(5h, 7d) logic to usage in compact mode
- Remove non-null assertion for planName (same fix as identity.ts)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: update tests for new lineLayout config schema

- Update config.test.js to validate lineLayout instead of layout
- Update render.test.js to use lineLayout and showSeparators
- Update index.test.js mock config with new schema
- Update integration test expected output for expanded default

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 12:17:36 +11:00
Rareș T. Gosman
c36738d63c fix: read OAuth credentials from macOS Keychain (Claude Code 2.x) (#50)
* fix: read OAuth credentials from macOS Keychain (Claude Code 2.x)

Claude Code 2.x stores OAuth credentials in the macOS Keychain under
"Claude Code-credentials" instead of ~/.claude/.credentials.json.

This caused the usage tracker to silently fail on macOS since the
credentials file doesn't exist.

Changes:
- Add readKeychainCredentials() to read from macOS Keychain via security CLI
- Add 1.5s timeout to prevent HUD hangs if Keychain is slow
- Fall back to file-based credentials if Keychain lacks subscriptionType
- Extract parseCredentialsData() to share validation logic
- Add readKeychain to UsageApiDeps for test isolation
- Add test for Keychain-to-file fallback behavior

The credential lookup order is now:
1. macOS Keychain (Claude Code 2.x on darwin)
2. File-based ~/.claude/.credentials.json (older versions, non-macOS)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address review feedback for keychain credentials

- Increase keychain timeout from 1.5s to 5s to allow time for macOS
  permission prompts (user needs to click "Allow")
- Fix fallback logic: always use keychain token (authoritative) when
  present, supplement subscriptionType from file if needed
- Add happy-path test for complete keychain credentials
- Add test verifying keychain token is used even when subscriptionType
  comes from file

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* security: harden keychain credential reading

- Use execFileSync with absolute path (/usr/bin/security) instead of
  execSync with shell - prevents PATH hijacking and shell injection
- Sanitize debug logging to only log error.message, not full error
  object which may contain stdout/stderr with credential data
- Add 60s backoff on keychain failures to prevent re-prompting user
  on every render cycle after a timeout/denial

Addresses security review feedback from Codex.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Jarrod Watts <jarrod@cubelabs.xyz>
2026-01-14 11:26:16 +11:00