Repository navigation
feat(statusline): effort level, credits segment, per-segment options and context window fix - #135
gonzariosm wants to merge 7 commits into
Conversation
Reviewer's GuideThis 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 resolutionsequenceDiagram
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
Sequence diagram for context window limit resolutionsequenceDiagram
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
Flow diagram for per-segment option editingflowchart 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
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
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
…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>
…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>
|
Addressed all four review findings in 4ae7103 (see the inline replies). @sourcery-ai review |
…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>
4ae7103 to
8fb80a9
Compare
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
effort.level,rate_limitsandcontext_windowfrom the JSON Claude Code passes (2.1.28x+).resets_ataccepts integer, fractional and RFC 3339 values; an unexpected value no longer blanks the whole statusline.Fable 5.1 · high(show_effortoption).show_weekly) andreset_format = "countdown". Falls back to Claude Code'srate_limitswhen the API or token is missing. Reset timestamps and credits are cached in a versioned cache file.creditssegment ($23.75/$50 · 48%) from the API'sextra_usage, sharing one usage fetch per render with Usage. Added to every theme preset and migrated into existing configs afterusage.--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.config.tomlso edits made via--setare not discarded on the next save.context_window.context_window_sizefrom 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.scripts/install.shbuilds and installs the binary from source with a backup of the previous one.Screenshots
Usage options in the TUI Settings panel,
reset_format = "countdown":Same panel with
reset_format = "time":Unsaved-changes prompt when quitting the TUI:
Testing
cargo testpasses, including new tests for the context limit priority and the built-in families.cargo clippy --releasereports only the three warnings already present onmaster.context_window.context_window_size.Related
rate_limitsfrom stdin), fix(usage): show session reset time and add configurable reset display #87 (session reset time): this PR covers the same ground and can supersede or be rebased on them, whichever the maintainer prefers.🤖 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:
Bug Fixes:
Enhancements:
Build:
Documentation:
Tests: