Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Enhancement: Add account usage limits to statusline (session %, 7-day %, Sonnet %)

Open
#32 1 comment 3 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
48/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Quiet
Tech stack
shell
Domain
cli, tooling

Research direction

Start with scripts/statusline.sh and trace how the existing context, cost, and duration lines are assembled. Review the proposed credentials, cache, curl, jq, and reset-formatting paths, then run the statusline with and without credentials and with an unavailable API. Done means account usage appears when available while missing credentials, failures, null Sonnet data, and missing jq preserve the existing lines.

Written by the indexing model from the issue text.

Description

Summary

The current scripts/statusline.sh shows context window usage, cost, and session duration. This proposal adds a third line displaying account-level rate limit usage — session (5-hour), weekly (all models), and weekly (Sonnet-only) — fetched from Anthropic's OAuth usage API with local caching.

Problem

Heavy Claude Code users frequently hit the 5-hour session limit or weekly limits without any visible warning until they get a hard block. The existing statusline has no visibility into these limits.

Proposed Solution

Add a Line 3 to the statusline that shows account usage limits in real time:

[Sonnet 4.6] 📁 my-project │ 🌿 main

████████░░░░ 42% (84k/200k) │ $0.00 │ ⏱ 12m 30s │ ↻78%

Session: 42% ⟳2h18m │ 7d all: 15% ⟳Sat 16h │ Sonnet: 18% ⟳Sat 16h

Color-coded with the same thresholds as the existing context bar:

  • 🟢 Green: < 50%
  • 🟡 Yellow: 50–79%
  • 🔴 Red: ≥ 80%

API

The data comes from an undocumented but stable Anthropic OAuth endpoint:

GET https://api.anthropic.com/api/oauth/usage
Authorization: Bearer <oauth-access-token>
anthropic-version: 2023-06-01
anthropic-beta: oauth-2025-04-20

Response:

{
  "five_hour": {
    "utilization": 42.0,
    "resets_at": "2026-02-22T00:00:00.318160+00:00"
  },
  "seven_day": {
    "utilization": 15.0,
    "resets_at": "2026-02-28T19:00:00.318184+00:00"
  },
  "seven_day_sonnet": {
    "utilization": 18.0,
    "resets_at": "2026-02-28T19:00:00.318198+00:00"
  },
  "seven_day_opus": null,
  "extra_usage": {
    "is_enabled": false,
    "monthly_limit": null
  }
}

The token is read directly from ~/.claude/.credentials.json (the same file Claude Code already uses), so no additional configuration is needed.

Implementation Details

Caching strategy

The statusline runs on every Claude Code render cycle. To avoid hammering the API, usage data is cached in ~/.claude/usage-cache.json with a 2-minute TTL:

USAGE_CACHE="$HOME/.claude/usage-cache.json"
CACHE_TTL=120

fetch_usage() {
    local now
    now=$(date +%s)

    if [[ -f "$USAGE_CACHE" ]]; then
        local cache_time
        cache_time=$(jq -r '.cached_at // 0' "$USAGE_CACHE" 2>/dev/null)
        if [[ $((now - cache_time)) -lt $CACHE_TTL ]]; then
            cat "$USAGE_CACHE"
            return
        fi
    fi

    local token
    token=$(jq -r '.claudeAiOauth.accessToken // empty' \
        "$HOME/.claude/.credentials.json" 2>/dev/null)
    [[ -z "$token" ]] && { [[ -f "$USAGE_CACHE" ]] && cat "$USAGE_CACHE" || echo "{}"; return; }

    local response
    response=$(curl -sf --max-time 4 \
        -H "Authorization: Bearer $token" \
        -H "Content-Type: application/json" \
        -H "anthropic-version: 2023-06-01" \
        -H "anthropic-beta: oauth-2025-04-20" \
        "https://api.anthropic.com/api/oauth/usage" 2>/dev/null)

    if [[ -n "$response" ]] && echo "$response" | jq -e '.five_hour' &>/dev/null; then
        echo "$response" | jq ". + {cached_at: $now}" > "$USAGE_CACHE" 2>/dev/null
        echo "$response"
    else
        # Serve stale cache on failure rather than breaking the statusline
        [[ -f "$USAGE_CACHE" ]] && cat "$USAGE_CACHE" || echo "{}"
    fi
}
Reset time formatting
format_reset() {
    local iso="$1"
    [[ -z "$iso" || "$iso" == "null" ]] && echo "?" && return

    local reset_epoch now_epoch diff
    reset_epoch=$(date -d "$iso" +%s 2>/dev/null) || { echo "?"; return; }
    now_epoch=$(date +%s)
    diff=$(( reset_epoch - now_epoch ))

    if [[ $diff -le 0 ]]; then
        echo "now"
    elif [[ $diff -lt 3600 ]]; then
        printf "%dm" $(( diff / 60 ))
    elif [[ $diff -lt 86400 ]]; then
        printf "%dh%dm" $(( diff / 3600 )) $(( (diff % 3600) / 60 ))
    else
        LC_TIME=C date -d "$iso" +"%a %Hh" 2>/dev/null || echo "?"
    fi
}
Line 3 assembly
if [ -n "$sess_pct" ] && [ "$sess_pct" != "null" ]; then
    if   [ "$sess_pct" -lt 50 ]; then sess_color='\033[32m'
    elif [ "$sess_pct" -lt 80 ]; then sess_color='\033[33m'
    else                               sess_color='\033[31m'; fi

    sess_reset=$(format_reset "$sess_reset_at")
    line3="$(printf '\033[2mSession:\033[0m %b%s%%\033[0m \033[2m⟳%s\033[0m' \
        "$sess_color" "$sess_pct" "$sess_reset")"

    # 7-day all models
    if [ -n "$week_pct" ] && [ "$week_pct" != "null" ]; then
        if   [ "$week_pct" -lt 50 ]; then week_color='\033[32m'
        elif [ "$week_pct" -lt 80 ]; then week_color='\033[33m'
        else                               week_color='\033[31m'; fi

        week_reset=$(format_reset "$week_reset_at")
        line3="$line3 $(printf '%b \033[2m7d all:\033[0m %b%s%%\033[0m \033[2m⟳%s\033[0m' \
            "$SEP" "$week_color" "$week_pct" "$week_reset")"
    fi

    # 7-day Sonnet-only (only shown when non-null)
    if [ -n "$sonnet_pct" ] && [ "$sonnet_pct" != "null" ]; then
        if   [ "$sonnet_pct" -lt 50 ]; then son_color='\033[32m'
        elif [ "$sonnet_pct" -lt 80 ]; then son_color='\033[33m'
        else                                 son_color='\033[31m'; fi

        line3="$line3 $(printf '%b \033[2mSonnet:\033[0m %b%s%%\033[0m \033[2m⟳%s\033[0m' \
            "$SEP" "$son_color" "$sonnet_pct" "$(format_reset "$sonnet_reset_at")")"
    fi
fi

Failure modes

Scenario Behavior
No credentials file Line 3 omitted entirely, Lines 1–2 unaffected
API unreachable / timeout (4s max) Serves stale cache; if no cache, Line 3 omitted
API returns error JSON Stale cache used; graceful fallback to no Line 3
seven_day_sonnet is null (Opus plan) Sonnet segment omitted from Line 3
jq not installed No Line 3; existing behavior preserved

Token count display (bonus)

While at it, Line 2 also gains an absolute token count display — useful alongside the percentage for planning context compaction:

████████░░░░ 42% (84k/200k) │ $0.00 │ ⏱ 12m 30s

Related

Happy to submit a PR if this direction makes sense to the maintainers.

Dominant language
Shell
Stars
2.1k
Forks
164
PR merge metrics
No merged PRs in 30d

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from trailofbits/claude-code-config

All issues in trailofbits/claude-code-config

Similar issues

More Shell/Bash issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.