Skip to content

feat(hud): session reset timers and weekly/monthly cost tracking#190

Merged
dean0x merged 14 commits intomainfrom
feat/hud-session-reset-cost-tracking
Apr 20, 2026
Merged

feat(hud): session reset timers and weekly/monthly cost tracking#190
dean0x merged 14 commits intomainfrom
feat/hud-session-reset-cost-tracking

Conversation

@dean0x
Copy link
Copy Markdown
Owner

@dean0x dean0x commented Apr 20, 2026

Summary

  • Replace OAuth API with stdin extraction — delete usage-api.ts and credentials.ts, extract rate_limits.resets_at and cost from Claude Code's stdin payload instead of making external API calls
  • Add cost-history module — persistent per-session cost tracking with atomic writes, 24h archival, 90-day trimmed archive, and rolling 7-day/30-day aggregation with 30s cache
  • Show reset countdown timers5h ████░░░░ 45% (2h 15m) format with parenthesized countdown after quota bars
  • Show weekly/monthly cost$1.42 · $18.50/wk · $62.30/mo on Line 3
  • Rename context label — "Current Session" → "Context", remove sessionDuration from defaults (component retained for reinstatement)

Changes

  • Added: cost-history.ts (310 lines) — atomic persistence, cleanup, archival, aggregation with type guard, caching, and extracted helpers
  • Added: cost-history.test.ts (519 lines, 28 tests) — persistence, path traversal, cleanup, archival, trimming, aggregation
  • Modified: usage-quota.tsformatCountdown, renderQuotaWindow with JSDoc
  • Modified: session-cost.ts — weekly/monthly cost display
  • Modified: index.ts — stdin extraction for resets_at and cost, cost persistence gated behind needsSessionCost
  • Modified: types.tsCostAggregation, epoch-seconds annotations, count-agnostic JSDoc
  • Modified: hud-components.test.ts — 67 tests (added formatCountdown unit tests, updated component output assertions)
  • Deleted: usage-api.ts, credentials.ts, credentials.test.ts — OAuth flow replaced by stdin extraction

Test plan

  • All 1059 tests pass (46 test files)
  • TypeScript compiles clean
  • Code review completed (9 reviewers, 22 issues resolved)
  • Self-review passed (9-pillar quality gate)
  • Path traversal guard tested (3 tests)
  • Cleanup/archival tested (5 tests including orphaned .tmp, archive trimming)
  • formatCountdown unit tested (6 edge cases)
  • Cost aggregation tested (dedup, time windows, malformed files)

Dean Sharon and others added 14 commits April 20, 2026 11:04
…etsAt to UsageData, CostAggregation type, and costHistory to GatherContext

Co-Authored-By: Claude <noreply@anthropic.com>
Implements persistSessionCost (atomic write per session), aggregateCosts
(merges active files + archive, deduplicates by max cost_usd), and
getCostFilePaths (testable path resolution via DEVFLOW_DIR env var).
Cleanup runs periodically: archives 24h-old sessions, trims archive at 90 days.

Co-Authored-By: Claude <noreply@anthropic.com>
usage-quota: drop Session prefix, add formatCountdown helper for reset
timers (↻2h15m format), show countdown after label and before bar.

session-cost: extend to show weekly ($18.50/wk) and monthly ($62.30/mo)
cost totals from costHistory when available.

Co-Authored-By: Claude <noreply@anthropic.com>
render.ts: reorder Line 3 to model · configCounts · sessionDuration · sessionCost.

index.ts: remove OAuth fetchUsageData, add extractUsageFromStdin (reads
rate_limits from stdin directly), wire persistSessionCost and aggregateCosts
for cost tracking. costHistory passed into GatherContext.

Co-Authored-By: Claude <noreply@anthropic.com>
… extraction

OAuth-based usage fetching removed; rate limits now read directly from
stdin.rate_limits on each render. No network calls, no credentials needed.

Co-Authored-By: Claude <noreply@anthropic.com>
hud-components.test.ts: add learningCounts/costHistory to makeCtx;
update usageQuota tests to drop Session prefix, add resets_at fields,
and test countdown rendering; add sessionCost tests for weekly/monthly.

hud-render.test.ts: add costHistory to makeCtx; update usage assertions
to drop Session prefix, add resets_at fields.

cost-history.test.ts: new suite covering persistSessionCost (atomic
writes, skip on zero cost, overwrite, concurrent sessions) and
aggregateCosts (null handling, file reads, archive, dedup, window sums,
malformed entry resilience).

credentials.test.ts: deleted — credentials.ts removed.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Three discrepancies between the README preview and actual render:
- Line 2 lacked 'Context' label prefix and used wrong quota format (↻ symbol
  with pre-bar countdown instead of parenthesized post-percent countdown)
- Line 3 included ⏱ 15m (sessionDuration) which is removed from defaults
- Feature description mentioned 'session duration' — removed

Co-Authored-By: Claude <noreply@anthropic.com>
…-history

- Extract nowEpoch() helper to deduplicate Math.floor(Date.now() / 1000) across 4 call sites
- Add isSessionEntry() type guard replacing all unsafe JSON.parse casts
- Fix !costUsd guard to costUsd <= 0 || !Number.isFinite(costUsd) to correctly reject NaN and negative values
- Guard mkdirSync with sessionsDirCreated module flag to avoid redundant syscalls
- Extract cleanOrphanedTmpFiles() and archiveStaleSessionFiles() from runCleanup to reduce nesting
- Extract upsertMax(), readSessionEntries(), readArchiveEntries() from aggregateCosts to thin the orchestrator
- Add 30s aggregation cache to avoid repeated filesystem reads on every HUD render tick

Co-Authored-By: Claude <noreply@anthropic.com>
…sistence

- formatCountdown JSDoc: remove stale "compact, no spaces" note (impl uses spaces)
- renderQuotaWindow: add JSDoc showing rendered format
- ComponentId JSDoc: replace "16 HUD components" count with accurate description
- StdinData.rate_limits resets_at fields: annotate as Epoch seconds
- sessionCost: add JSDoc documenting weekly/monthly totals
- persistSessionCost call: guard with needsSessionCost so cost is only
  persisted when the sessionCost component is actually enabled

Co-Authored-By: Claude <noreply@anthropic.com>
- Fix module isolation: add vi.resetModules() in beforeEach so module-level
  singletons (sessionsDirCreated, cachedAggregation) reset between tests;
  this fixes 15 pre-existing test failures
- Fix misleading comment: "cache-busting" → accurate description of why
  dynamic import is used (DEVFLOW_DIR read at call time)
- Add path traversal guard tests for persistSessionCost (/, \, empty string)
- Add runCleanup test: verifies stale session files (>24h) are archived to
  archive.jsonl, triggered by mocking Date.now to a timestamp % 50 === 0
- Add formatCountdown describe block: past timestamp, 2h 30m, 2d 5h, 45m
  minutes-only, exact day (no hours sub-unit), exact hour (no minutes sub-unit)

Co-Authored-By: Claude <noreply@anthropic.com>
@dean0x dean0x merged commit 7715993 into main Apr 20, 2026
4 checks passed
@dean0x dean0x deleted the feat/hud-session-reset-cost-tracking branch April 20, 2026 21:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant