Skip to content

feat(statusline): effort level, credits segment, per-segment options and context window fix - #135

Open
gonzariosm wants to merge 7 commits into
Haleclipse:masterfrom
gonzariosm:upstream-effort-credits-options
Open

gonzariosm wants to merge 7 commits into
Haleclipse:masterfrom
gonzariosm:upstream-effort-credits-options

Conversation

@gonzariosm

@gonzariosm gonzariosm commented Sep 25, 2026 •

Copy link
Copy Markdown

Summary

Support the newer statusline payload Claude Code sends (effort level, rate_limits, context_window), add a Credits segment, and make per-segment options editable from the CLI and the TUI. Also fixes the context window percentage overflowing on models with a 1M native window (Fable 5.1 showed 468% at 937k tokens).

Changes

  • Statusline input: parse effort.level, rate_limits and context_window from the JSON Claude Code passes (2.1.28x+). resets_at accepts integer, fractional and RFC 3339 values; an unexpected value no longer blanks the whole statusline.
  • Model segment: append the active effort level, e.g. Fable 5.1 · high (show_effort option).
  • Usage segment: show the five-hour reset time next to the five-hour percentage, add the weekly block (show_weekly) and reset_format = "countdown". Falls back to Claude Code's rate_limits when the API or token is missing. Reset timestamps and credits are cached in a versioned cache file.
  • Credits segment: new credits segment ($23.75/$50 · 48%) from the API's extra_usage, sharing one usage fetch per render with Usage. Added to every theme preset and migrated into existing configs after usage.
  • Options: registry of per-segment options with defaults, descriptions and choices. CLI: --options, --set SEGMENT.KEY=VALUE, --unset SEGMENT.KEY. TUI: one row per option in the Settings panel (checkbox, inline select or prompt), Left/Right steps through choices, preview honours the options.
  • TUI: scroll the Settings panel and segment list on short terminals; ask to save, discard or keep editing when quitting with unsaved changes; start from config.toml so edits made via --set are not discarded on the next save.
  • Context window: prefer context_window.context_window_size from Claude Code over the model-derived limit; recognize Fable and Mythos as built-in families with a 1M default. Overlaps with feat: recognize Fable/Mythos model families with 1M context window #133, which only adds the families; this also uses the size Claude Code reports, so any future model is correct without a code change.
  • Install: scripts/install.sh builds and installs the binary from source with a backup of the previous one.
  • README documents the new segments, option editing and the context limit resolution order.

Screenshots

Usage options in the TUI Settings panel, reset_format = "countdown":

TUI options with countdown reset format

Same panel with reset_format = "time":

TUI options with time reset format

Unsaved-changes prompt when quitting the TUI:

TUI unsaved changes prompt

Testing

  • cargo test passes, including new tests for the context limit priority and the built-in families.
  • cargo clippy --release reports only the three warnings already present on master.
  • Manual: statusline run against a real 954k-token Fable 5.1 transcript shows 95.5% instead of 468%; Opus payloads unchanged. Verified in the Claude Code 2.1.282 binary that the statusline JSON includes context_window.context_window_size.
  • Used daily as the live statusline on macOS.

Related

🤖 Generated with Claude Code

Summary by Sourcery

Extend the statusline for Claude Code's newer payloads with credits and configurable usage display while making context-window reporting accurate for large-context models.

New Features:

  • Add a Credits segment that displays extra-usage spending and monthly limits.
  • Support effort levels, rate limits, reset times, weekly usage, and configurable reset formats in statusline output.
  • Add per-segment option discovery and editing through CLI commands and the TUI.

Bug Fixes:

  • Use Claude Code's reported context window size to prevent usage percentages from exceeding 100% on large-window models.
  • Make malformed reset timestamps non-fatal when parsing statusline input.

Enhancements:

  • Improve the TUI with option-aware previews, scrolling, configuration migration, and unsaved-change handling.
  • Recognize Fable and Mythos model families with native 1M context defaults and prioritize active payload context limits.
  • Share usage data and versioned caching between Usage and Credits segments.

Build:

  • Add a source installation script that builds the release binary, backs up the existing installation, and creates a shell command symlink.

Documentation:

  • Document the Credits segment, usage and context-window behavior, per-segment configuration, CLI option editing, and source installation.

Tests:

  • Add coverage for reset timestamp formats, rate-limit payloads, context-window precedence, and built-in model context limits.

@sourcery-ai

sourcery-ai Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

This PR updates the statusline for newer Claude Code payloads, adds usage resets and credits with shared cached data, makes segment options editable through the CLI and TUI, fixes native context-window sizing, migrates themes/configurations, and adds source-install tooling and documentation.

Sequence diagram for statusline usage and credits resolution

sequenceDiagram
    participant ClaudeCode
    participant Statusline
    participant UsageData
    participant UsageAPI
    participant Cache
    participant Usage
    participant Credits

    ClaudeCode->>Statusline: Provide InputData with rate_limits
    Statusline->>UsageData: load_usage_data(input)
    UsageData->>Cache: Read versioned cache
    alt Valid cache
        Cache-->>UsageData: UsageData with credits
    else Cache miss or expired
        UsageData->>UsageAPI: Fetch usage data
        alt API succeeds
            UsageAPI-->>UsageData: five_hour, seven_day, extra_usage
            UsageData->>Cache: Save versioned cache
        else API unavailable
            UsageData->>UsageData: Fall back to rate_limits
        end
    end
    UsageData-->>Usage: Shared usage data
    UsageData-->>Credits: Shared usage data
    Usage-->>Statusline: Usage percentages and reset times
    Credits-->>Statusline: Extra-usage credit amount and percentage
Loading

Sequence diagram for context window limit resolution

sequenceDiagram
    participant ClaudeCode
    participant ContextWindow
    participant ModelConfig
    participant ContextSegment

    ClaudeCode->>ContextWindow: Provide context_window_size
    ContextSegment->>ContextWindow: resolve_context_limit(input)
    alt Reported size is present and greater than zero
        ContextWindow-->>ContextSegment: Use context_window_size
    else Reported size is absent or zero
        ContextWindow->>ModelConfig: get_context_limit(model.id)
        ModelConfig-->>ContextWindow: Modifier, model entry, or built-in family limit
        ContextWindow-->>ContextSegment: Use derived limit
    end
    ContextSegment-->>ClaudeCode: Render bounded context percentage
Loading

Flow diagram for per-segment option editing

flowchart LR
    Config[config.toml] --> CLI[CLI options commands]
    Config --> TUI[TUI Settings panel]
    CLI --> Registry[Option registry]
    TUI --> Registry
    Registry --> Preview[Preview honours options]
    CLI --> Save[Config::save]
    TUI --> Save
    Save --> Config
    CLI --> Render[Statusline render]
    TUI --> Render
Loading

File-Level Changes

Change Details Files
Expanded statusline input parsing and context-window resolution for Claude Code’s newer payloads.
  • Added effort, rate-limit, and reported context-window fields with tolerant reset timestamp parsing.
  • Prioritized the reported context window size and added 1M Fable/Mythos built-in families.
  • Added tests covering context-limit precedence and model-family defaults.
src/config/types.rs
src/config/models.rs
src/core/segments/context_window.rs
Extended usage reporting with reset details, weekly limits, API fallback, caching, and credits.
  • Added five-hour and weekly reset formatting with time/countdown modes and rate_limits fallback.
  • Added versioned usage caching and shared per-render usage data.
  • Added the Credits segment backed by API extra-usage data and configurable percentage display.
src/core/segments/usage.rs
src/core/segments/credits.rs
src/core/statusline.rs
src/config/types.rs
Introduced centralized per-segment option metadata and non-interactive configuration editing.
  • Defined option defaults, descriptions, choices, typed parsing, listing, setting, and unsetting.
  • Added --options, repeatable --set, and repeatable --unset CLI flows.
  • Migrated existing configurations by inserting Credits after Usage and preserved unknown options with warnings.
src/config/options.rs
src/config/loader.rs
src/config/types.rs
src/cli.rs
src/main.rs
Added TUI editing and improved configuration-editing behavior on constrained terminals.
  • Rendered per-segment option rows as toggles, selectors, or value prompts and updated previews accordingly.
  • Added scrolling for settings and segment lists plus unsaved-change save/discard/continue handling.
  • Loaded config.toml as the TUI source to preserve CLI and manual edits.
src/ui/app.rs
src/ui/components/settings.rs
src/ui/components/segment_list.rs
src/ui/components/name_input.rs
src/ui/components/confirm.rs
src/ui/components/help.rs
src/ui/components/preview.rs
src/ui/components/mod.rs
Integrated Credits into built-in theme presets and documented the new functionality.
  • Added Credits after Usage to all theme presets with shared Usage styling and a credit-card icon.
  • Documented statusline payload behavior, options, credits, context-limit resolution, and CLI installation.
src/ui/themes/presets.rs
src/ui/themes/theme_cometix.rs
src/ui/themes/theme_default.rs
src/ui/themes/theme_gruvbox.rs
src/ui/themes/theme_minimal.rs
src/ui/themes/theme_nord.rs
src/ui/themes/theme_powerline_dark.rs
src/ui/themes/theme_powerline_light.rs
src/ui/themes/theme_powerline_rose_pine.rs
src/ui/themes/theme_powerline_tokyo_night.rs
README.md
Added a source installation script with binary backup and shell linking.
  • Builds or reuses the release binary, backs up the existing Claude Code binary, installs the new binary, and links the shell command.
  • Supports configurable install directories and --no-build.
scripts/install.sh

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 4 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/config/types.rs" line_range="165-169" />
<code_context>
+    pub resets_at: Option<ResetsAt>,
+}
+
+/// Rate limits reported by Claude Code (five-hour session, seven-day week)
+#[derive(Debug, Clone, Deserialize)]
+pub struct RateLimits {
+    pub five_hour: Option<RateLimitWindow>,
+    pub seven_day: Option<RateLimitWindow>,
+}
+
</code_context>
<issue_to_address>
**issue (bug_risk):** A `rate_limits` object with only one window is rejected during `InputData` deserialization because `five_hour` and `seven_day` are required fields, and `UsageData::from_input` also returns `None` whenever `five_hour` is absent. The statusline therefore either fails completely or shows no fallback usage instead of handling the independently optional windows.

**Triggers:** When Claude Code sends a partial `rate_limits` payload, such as a weekly limit without a five-hour limit.

**Suggested fix:** Add `#[serde(default)]` to both `RateLimits` fields and build `UsageData` from whichever window is present instead of requiring `five_hour`.
</issue_to_address>

### Comment 2
<location path="src/core/segments/model.rs" line_range="49-63" />
<code_context>
 }

 impl ModelSegment {
+    /// Whether the effort level should be appended to the model name.
+    /// Controlled by the `show_effort` option of the model segment (default: true).
+    fn show_effort_enabled() -> bool {
+        crate::config::Config::load()
+            .ok()
+            .and_then(|config| {
+                config
+                    .segments
+                    .iter()
+                    .find(|s| s.id == SegmentId::Model)
+                    .and_then(|sc| sc.options.get("show_effort"))
+                    .and_then(|v| v.as_bool())
+            })
+            .unwrap_or(true)
+    }
+
</code_context>
<issue_to_address>
**issue (broader_impact):** Segment option readers reload `config.toml` instead of using the active `Config` passed to rendering, so options from a selected `--theme` configuration are ignored. For example, `ccline --theme ...` can select a model with `show_effort = false`, but `show_effort_enabled` reads the old config file and still enables effort output.

**Triggers:** When rendering with `--theme` or any caller that supplies an active configuration different from the on-disk `config.toml`.

**Suggested fix:** Pass the relevant `SegmentConfig` or option values into segment collection rather than reloading the global config inside each segment.
</issue_to_address>

### Comment 3
<location path="src/core/segments/usage.rs" line_range="359-369" />
<code_context>
-        let token = credentials::get_oauth_token()?;
+/// Usage data is fetched once per statusline render and shared by every
+/// segment that needs it (Usage, Credits).
+static USAGE_DATA: std::sync::OnceLock<Option<UsageData>> = std::sync::OnceLock::new();
+
+/// Load usage data: API (cached on disk, options from the `usage` segment),
+/// falling back to the `rate_limits` block Claude Code passes in the input.
+pub(crate) fn load_usage_data(input: &InputData) -> Option<UsageData> {
+    USAGE_DATA
+        .get_or_init(|| UsageSegment::new().load(input))
+        .clone()
+}

-        // Load config from file to get segment options
</code_context>
<issue_to_address>
**issue (bug_risk):** The `OnceLock` permanently caches the first input's usage data for the lifetime of the process, so subsequent renders with different `InputData` reuse stale percentages and reset timestamps instead of loading the new input or current API/cache data.

**Triggers:** When the library is used to render more than one statusline in the same process.

**Suggested fix:** Make the usage cache scoped to a single render/input, or key the cache by the input/account/config rather than using a process-global one-time cell.

```suggestion
/// Load usage data: API (cached on disk, options from the `usage` segment),
/// falling back to the `rate_limits` block Claude Code passes in the input.
pub(crate) fn load_usage_data(input: &InputData) -> Option<UsageData> {
    UsageSegment::new().load(input)
}
```
</issue_to_address>

### Comment 4
<location path="src/config/types.rs" line_range="127-144" />
<code_context>
+#[derive(Debug, Clone)]
+pub struct ResetsAt(String);
+
+impl ResetsAt {
+    fn from_json(value: &serde_json::Value) -> Option<Self> {
+        match value {
+            serde_json::Value::String(s) if !s.trim().is_empty() => Some(Self(s.clone())),
+            serde_json::Value::Number(n) => {
+                let secs = n
+                    .as_i64()
+                    .or_else(|| n.as_f64().map(|f| f.trunc() as i64))?;
+                chrono::DateTime::from_timestamp(secs, 0).map(|dt| Self(dt.to_rfc3339()))
+            }
+            _ => None,
+        }
</code_context>
<issue_to_address>
**nitpick (bug_risk):** `to_rfc3339` returns `Some` for every non-empty string without validating or normalizing it, despite its contract saying it returns a normalized RFC 3339 value. Invalid string values reach the usage formatter and are rendered as `?`, while valid non-RFC3339 strings are also treated as present by downstream code.

**Triggers:** When Claude Code supplies a non-empty but malformed string for `resets_at`.

**Suggested fix:** Parse string values as RFC 3339 in `from_json` and return `None` for strings that do not parse.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 3 findings to address first, and the new usage path reads the OAuth token and sends it to the configurable api_base_url; if that URL is wrong or malicious, the token and usage data could be exposed, and reverting cannot undo that exposure. Other display and configuration changes are reversible.

Blocking findings: src/config/types.rs:169, src/core/segments/model.rs:63, src/core/segments/usage.rs:369


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread src/config/types.rs
Comment thread src/core/segments/model.rs Outdated
Comment thread src/core/segments/usage.rs Outdated
Comment thread src/config/types.rs
gonzariosm added a commit to gonzariosm/CCometixLine that referenced this pull request Sep 25, 2026
…s_at

Address the review on Haleclipse#135:

- Model, Usage and Credits read their options from the config passed to
  collect_all_segments instead of reloading config.toml, so `--theme` and
  any caller-supplied configuration are honoured. Usage options are grouped
  in `UsageOptions`; Credits receives the Usage API/cache settings.
- Drop the process-global OnceLock usage cache; the on-disk cache already
  lets the second segment in a render reuse the first fetch, and a library
  caller rendering several inputs no longer gets stale data.
- `rate_limits` windows are independently optional: `UsageData::from_input`
  returns data when either window is present. Both fields carry
  `#[serde(default)]` explicitly.
- `ResetsAt` parses string values as RFC 3339 and rejects anything else, so
  `to_rfc3339` always returns a normalized value.
- Tests for resets_at parsing and partial rate_limits payloads.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
gonzariosm added a commit to gonzariosm/CCometixLine that referenced this pull request Sep 25, 2026
…s_at

Address the review on Haleclipse#135:

- Model, Usage and Credits read their options from the config passed to
  collect_all_segments instead of reloading config.toml, so `--theme` and
  any caller-supplied configuration are honoured. Usage options are grouped
  in `UsageOptions`; Credits receives the Usage API/cache settings.
- Drop the process-global OnceLock usage cache; the on-disk cache already
  lets the second segment in a render reuse the first fetch, and a library
  caller rendering several inputs no longer gets stale data.
- `rate_limits` windows are independently optional: `UsageData::from_input`
  returns data when either window is present. Both fields carry
  `#[serde(default)]` explicitly.
- `ResetsAt` parses string values as RFC 3339 and rejects anything else, so
  `to_rfc3339` always returns a normalized value.
- Tests for resets_at parsing and partial rate_limits payloads.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@gonzariosm

Copy link
Copy Markdown
Author

Addressed all four review findings in 4ae7103 (see the inline replies). @sourcery-ai review

gonzariosm and others added 7 commits September 25, 2026 12:06
…option editing

Statusline input:
- Parse `effort.level` and `rate_limits` from the JSON Claude Code passes
  (2.1.28x+). `resets_at` in rate_limits is a unix timestamp, so accept both
  integer and RFC 3339 forms.

Model segment:
- Append the active effort level, e.g. "Fable 5.1 · high" (`show_effort`).

Usage segment:
- Show the five-hour reset time next to the five-hour percentage; upstream
  paired it with the weekly reset. Add the weekly block ("7d 16% · Thu 00h")
  and a `reset_format = "countdown"` option ("4h 52m │ 7d 16% · 6d 1h").
- Fall back to Claude Code's `rate_limits` when the API or token is missing.
- Cache both reset timestamps and credits; version the cache file so stale
  entries from older builds are ignored.

Credits segment:
- New `credits` segment ("$23.75/$50 · 48%") from the API's `extra_usage`,
  sharing one usage fetch per render with the Usage segment. Added to every
  theme preset; existing configs and theme files get it migrated in after
  `usage`, inheriting its enabled state and colors.

Options:
- Registry of per-segment options with defaults, descriptions and choices.
- CLI: `--options`, `--set SEGMENT.KEY=VALUE`, `--unset SEGMENT.KEY`.
- TUI: one row per option in the Settings panel (checkbox for booleans,
  inline select for enumerations, prompt for free-form values), Left/Right
  step through choices, preview honours the options.

TUI:
- Scroll the Settings panel and segment list on short terminals.
- Ask to save, discard or keep editing when quitting with unsaved changes.
- Start from config.toml instead of the theme file, which silently discarded
  edits made to config.toml (e.g. via `--set`) on the next save.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Builds the release binary, installs it to ~/.claude/ccline/ccline with a
backup, and links ~/.local/bin/ccline so the command is on PATH. Replaces
the npm package, whose postinstall hard-links the upstream binary over the
same path.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude Code documents rate_limits.*.resets_at as a number, so a fractional
epoch such as 1758750000.5 matched neither variant of the untagged enum and
serde rejected the whole InputData, blanking every segment. Deserialize the
field leniently: integer or fractional epoch seconds and RFC 3339 strings are
accepted, anything else becomes None and only the reset time shows "?".

Also print the option listing after `--set`/`--unset` when `--options` is
passed alongside, instead of silently ignoring the flag, and document that
the TUI now starts from config.toml and applies theme files only when a
theme is selected.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The context segment derived the limit only from the model ID, so any model
outside the Sonnet/Opus/Haiku families fell back to 200k. With Fable 5.1
(native 1M window) this showed 468% usage at 937k tokens.

- Parse `context_window.context_window_size` from the statusline JSON and
  prefer it over the model-derived limit (fall back when missing or 0).
- Add Fable and Mythos as built-in 1M families for the fallback path.
- Document the limit resolution order in the README.
- Unit tests for the new priority and the built-in families.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s_at

Address the review on Haleclipse#135:

- Model, Usage and Credits read their options from the config passed to
  collect_all_segments instead of reloading config.toml, so `--theme` and
  any caller-supplied configuration are honoured. Usage options are grouped
  in `UsageOptions`; Credits receives the Usage API/cache settings.
- Drop the process-global OnceLock usage cache; the on-disk cache already
  lets the second segment in a render reuse the first fetch, and a library
  caller rendering several inputs no longer gets stale data.
- `rate_limits` windows are independently optional: `UsageData::from_input`
  returns data when either window is present. Both fields carry
  `#[serde(default)]` explicitly.
- `ResetsAt` parses string values as RFC 3339 and rejects anything else, so
  `to_rfc3339` always returns a normalized value.
- Tests for resets_at parsing and partial rate_limits payloads.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@gonzariosm
gonzariosm force-pushed the upstream-effort-credits-options branch from 4ae7103 to 8fb80a9 Compare September 25, 2026 10:06
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