提交

提交图

351 次代码提交

作者 SHA1 备注 提交日期
dependabot[bot] 269ae6d71f chore(deps): bump actions/setup-node from 6 to 7
CI / Lint & Type Check (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Build (push) Has been cancelled
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6 to 7.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-20 08:44:40 +00:00
Matthew Breedlove 860df1763c Version bump and docs update
CI / Lint & Type Check (push) Has been cancelled
CI / Test (push) Has been cancelled
Publish / Publish to npm (push) Has been cancelled
CI / Build (push) Has been cancelled
v2.2.24
2026-07-20 02:39:55 -04:00
Zach Landquist 236cd79cb5 feat: add CacheTimer widget (continues #307) (#466)
* feat: add CacheTimer widget for prompt cache TTL countdown

Reads ~/.claude/state/cache-timer-{session_id}.json written by the
claude-cache-countdown hooks and displays live cache state:
  🔥 HOT  — agent active, cache being refreshed
  🟢 4:52 — countdown with green/yellow/red urgency colors
  ❄️ COLD — cache expired

Widget type: 'cache-timer', category: Session

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

* refactor(cache-timer): read transcript directly, no external deps

Instead of reading ~/.claude/state/cache-timer-{session_id}.json
(which required the claude-cache-countdown hooks to be installed),
the widget now reads the last assistant message timestamp directly
from the transcript_path provided by Claude Code.

No hooks, no external scripts — works out of the box with ccstatusline alone.

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

* fix(cache-timer): show HOT when Claude is actively working

When Claude is processing a request, the transcript's last entry is a
user message (the assistant response hasn't been written yet). The
previous code only searched for assistant timestamps, so it would use
the prior turn's timestamp — never showing HOT during active work.

Now detects the pending user message and displays 🔥 HOT immediately.

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

* refactor(cache-timer): adopt n/a empty-state and harden parsing

Builds on the widget from #307 and aligns it with the empty-state convention the Cache widgets follow: instead of returning null when there is no transcript or no assistant turn yet, render n/a gated behind a new (h)ide when empty toggle, so the widget stays visible unless the user opts out.

Also guard a malformed assistant timestamp so it can never reach the countdown as NaN, and document that a trailing user-role entry (a prompt or a tool result) is what drives the HOT state.

* test(cache-timer): cover preview, empty-state, countdown, and HOT

Sandboxed widget tests using temp transcript files: preview, n/a versus hidden empty-state, HOT during an in-flight turn, the green/yellow/red countdown buckets and COLD, the malformed-timestamp guard, and the hide-when-empty keybind and editor wiring.

* feat(cache-timer): customizable state glyphs and configurable TTL

The five state icons (HOT, fresh, draining, urgent, cold) become editable glyphs via the shared (g)lyph editor, declared as named SymbolSlots the same way the other symbol-aware widgets do, so nerd-font and ASCII users can replace the emoji with symbols that respect the widget color. A blanked glyph collapses its trailing space.

The cache TTL is now configurable: a (t)tl keybind cycles 5m/1h and any positive ttlSeconds can be set in settings.json, matching Claude Code writing both 5-minute and 1-hour cache breakpoints. The 5-minute default and existing output are unchanged.

* test(cache-timer): cover custom glyphs and configurable TTL

Adds cases for per-state glyph overrides (custom and blanked), the preview reflecting a custom glyph, a 1-hour TTL extending the countdown window, malformed-TTL fallback, the (t)tl cycle, and the editor TTL annotation; updates the keybind assertion for the new (t) and (g) binds.

* fix(cache-timer): ignore sidechain and API-error transcript rows

The transcript scan treated any trailing user or assistant row as the
session's cache state. Two kinds of rows broke that assumption:

- Sidechain (subagent) rows: a trailing sidechain user row reported HOT
  and a trailing sidechain assistant row restarted the countdown, even
  though subagent traffic runs against its own prompt prefix and never
  refreshes the main conversation's cache. Background agents can write
  these rows after the main turn has ended.
- Synthetic API-error rows (e.g. "You've hit your session limit"): these
  are written as type "assistant" with a timestamp and zero-token usage,
  so a failed request that refreshed nothing still produced a fresh
  countdown.

Skip rows with isSidechain: true or isApiErrorMessage: true so the scan
falls through to the newest main-chain row, matching the filtering
already done in jsonl-metrics.ts, jsonl-blocks.ts, and compaction.ts.

* fix(cache-timer): grow the tail read when the trailing record exceeds 32 KiB

The transcript scan read a fixed 32 KiB tail. When the newest relevant
record was itself larger than that (real transcripts contain tool-result
and pasted-prompt rows of several hundred KiB), the read started in the
middle of the record, the reverse scan could not parse the fragment, and
the widget rendered "n/a" instead of HOT or a countdown until a smaller
record was appended.

Retry the read with a doubled tail size whenever the scan finds no
relevant record and the read has not yet reached the start of the file,
capped at 1 MiB so a degenerate unparseable file bounds the work done
per render. readFileTail now also closes its file descriptor in a
finally block so a failed fstat/read no longer leaks it.

* fix(cache-timer): only reset the countdown on rows with cache activity

Every successful assistant row restarted the countdown, even when the
request reported zero cache-read and cache-creation tokens — as happens
when prompt caching is disabled (DISABLE_PROMPT_CACHING) or unsupported
by the provider/proxy. The widget then advertised a hot cache that did
not exist.

Inspect message.usage on assistant rows and skip those whose request
neither read nor wrote the cache, so the scan falls back to the newest
row with real cache activity, or reports n/a when caching never
happened. Rows without usage data cannot be classified and continue to
drive the countdown so older transcript formats keep working.

* fix(cache-timer): never report HOT for a finished turn without a cache anchor

Skipping a non-anchoring trailing assistant row (zero cache activity or
a synthetic API error) let the reverse scan fall through to the user row
that started that same turn, so a completed exchange was classified as
in-flight and the widget showed HOT indefinitely. The previous tests
missed this because they omitted the user row that precedes every
assistant response in a real transcript.

Track the two concerns separately: any main-chain assistant row now
finalizes the in-flight state (older user rows can no longer flip the
scan to HOT), while the countdown anchors on the newest assistant row
whose request actually read or wrote the cache. Non-anchoring rows fall
back to the prior real cache event, or to n/a when none exists.

A malformed anchor timestamp now also falls back to the prior cache
event instead of immediately reporting no data.

* fix(cache-timer): expand the tail read to the file start instead of capping

The tail expansion stopped at 1 MiB, so a valid trailing record larger
than that (a multi-megabyte pasted prompt or tool result) could never be
parsed and the widget rendered n/a permanently instead of HOT or a
countdown.

Keep doubling the read until the state resolves or the read reaches the
start of the file. The worst case — scanning a whole transcript that
contains no resolvable record — is bounded by file size, matching what
the token widgets already do by reading the full transcript on every
render; the common case still resolves within the initial 32 KiB tail.

---------

Co-authored-by: Tuan Son <tuan.dinh@interzero.de>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-07-20 02:26:03 -04:00
CC eecdf46b04 feat(widget): add Sandbox Status widget (#481)
* feat(widget): add Sandbox Status widget

Shows whether Claude Code's bash sandbox mode is enabled, read from the effective sandbox.enabled setting across the layered Claude config (project-local -> project -> user-local -> user). Because /sandbox persists its toggle to .claude/settings.local.json, the widget reflects runtime toggles on each status refresh, not just the configured default.

Display modes (cycle 'f'): glyph 'SB: dot' (default), text 'SB: ON/OFF', word 'Sandbox: ON/OFF', bare glyph-only; optional Nerd Font lock glyphs ('n'). Standard per-widget color picker; raw value on/off for composition.

Reuses the layered-settings candidate-path reader shared with getVoiceConfig.

* docs(widget): clarify sandbox status limitations

Add a second-line picker warning that the sandbox indicator is best effort when managed or CLI settings override local files or sandbox initialization fails.

Expand Sandbox Status coverage for bare Nerd Font glyphs and the current raw-output precedence so its display states are explicit ahead of follow-up behavior changes.

* fix(widgets): align raw and Nerd Font modes

Make Sandbox Status, Voice Status, and Remote Control Status follow the documented raw-value contract by removing display labels without replacing the configured value representation.

Expose and apply Nerd Font settings only while a configurable icon is visible across Sandbox Status, Voice Status, Remote Control Status, and Vim Mode. Hide the binding in text-only modes, guard direct actions, clear metadata when cycling away from icons, and ignore stale flags in rendering and editor labels.

Remove the redundant Sandbox bare format now that raw glyph mode provides glyph-only output, and cover all affected formats and state transitions.

---------

Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-07-20 01:24:37 -04:00
Casey Peters bd74e68c71 feat(widget): add github ci status (#492)
* feat(widget): add github ci status

* fix(git): preserve PR metadata without CI access

---------

Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-07-20 00:04:54 -04:00
Vishnu J a523195573 feat(renderer): add left/right option for Default Padding (#497)
* feat(renderer): add left/right option for default padding

The Default Padding setting always applied to both sides of a widget.
Add a defaultPaddingSide setting (both/left/right, default both) so
padding can be narrowed to one side without affecting the other,
matching the request in #482.

Both the standard and Powerline renderers honor the new option, and
alignment width accounting (calculateMaxWidthsFromPreRendered) counts
only the sides that are actually applied. Existing configs are
unaffected: defaultPaddingSide defaults to 'both', preserving current
rendering exactly.

* test(renderer): strip ANSI in padding-side assertions, cover merge + TUI cycle

Independent review found the padding-side tests asserted exact strings
against chalk-wrapped padding output, which breaks under FORCE_COLOR=1
since chalk.reset() reacts to ambient color support, not the Settings
colorLevel used elsewhere in the file. Strip SGR codes before every
assertion, matching the repo's existing convention (stripSgrCodes in
renderer-flex-width.test.ts and the separator-collapse tests). Verified
passing under both FORCE_COLOR=1 and NO_COLOR=1.

Also add the two coverage gaps called out in review:
- merge:'no-padding' regression tests (standard + Powerline) proving no
  double-pad and correct glue across a no-padding merge boundary for
  both the 'left' and 'right' padding sides.
- a GlobalOverridesMenu TUI test for the (d) cycle, mirroring the
  existing (m) minimalist-mode pattern.

---------

Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-07-19 23:34:56 -04:00
Treebird 98e3c8bbbc docs: add ccsessions / cc-session-num integration example (#483)
Adds ccsessions to Related Projects in README and a new Integration
Example section in USAGE.md showing how to wire the cc-session-num
companion script as a Custom Command widget to display the current
session rank (#1, #2, …) in the status line.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-07-19 23:24:49 -04:00
dependabot[bot] 1edc66516d chore(deps-dev): bump the dev-dependencies group with 5 updates (#500)
---
updated-dependencies:
- dependency-name: "@remotion/cli"
  dependency-version: 4.0.487
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: remotion
  dependency-version: 4.0.487
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: typedoc
  dependency-version: 0.28.20
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: typescript-eslint
  dependency-version: 8.62.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: vitest
  dependency-version: 4.1.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-19 22:44:32 -04:00
Karan Gourisaria b8ffa30b66 docs: Add ccsidekick to related projects (#512) 2026-07-19 22:42:20 -04:00
gwittebolle f22564bf51 docs: add claude-carbon to Related Projects (#514) 2026-07-19 22:41:21 -04:00
Nate 1b6e738cea chore: Update README with statuslin.es related project 2026-07-16 16:26:37 -04:00
Matthew Breedlove 1af051e207 Version bump and docs update
CI / Lint & Type Check (push) Has been cancelled
CI / Test (push) Has been cancelled
Publish / Publish to npm (push) Has been cancelled
CI / Build (push) Has been cancelled
v2.2.23
2026-07-10 17:49:05 -04:00
Zach Landquist 745299d819 feat(compaction): add a metric selector for composable sub-values (#457)
* feat(compaction): add a metric selector for composable sub-values

compaction-counter only emitted the full composite line. A new `metric` metadata selector (count|auto|manual|unknown|reclaimed) makes one instance emit a single raw value, so several can be composed with custom separators/symbols, following the existing skills `mode` / context-bar `display` metadata-mode precedent. Default `count` keeps the composite display unchanged; sub-metrics render a bare number (reclaimed via formatTokens) and respect hideZero on the selected value.

Closes #450

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

* fix(compaction): use reachable metric keybind

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-07-10 17:42:16 -04:00
dependabot[bot] 8c40ef0a83 chore(deps-dev): bump the dev-dependencies group with 6 updates (#496)
Bumps the dev-dependencies group with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [@remotion/cli](https://github.com/remotion-dev/remotion) | `4.0.481` | `4.0.484` |
| [eslint](https://github.com/eslint/eslint) | `10.5.0` | `10.6.0` |
| [eslint-plugin-import-x](https://github.com/un-ts/eslint-plugin-import-x) | `4.16.2` | `4.17.1` |
| [globals](https://github.com/sindresorhus/globals) | `17.6.0` | `17.7.0` |
| [remotion](https://github.com/remotion-dev/remotion) | `4.0.481` | `4.0.484` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.61.0` | `8.62.0` |


Updates `@remotion/cli` from 4.0.481 to 4.0.484
- [Release notes](https://github.com/remotion-dev/remotion/releases)
- [Commits](https://github.com/remotion-dev/remotion/compare/v4.0.481...v4.0.484)

Updates `eslint` from 10.5.0 to 10.6.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v10.5.0...v10.6.0)

Updates `eslint-plugin-import-x` from 4.16.2 to 4.17.1
- [Release notes](https://github.com/un-ts/eslint-plugin-import-x/releases)
- [Changelog](https://github.com/un-ts/eslint-plugin-import-x/blob/master/CHANGELOG.md)
- [Commits](https://github.com/un-ts/eslint-plugin-import-x/compare/v4.16.2...v4.17.1)

Updates `globals` from 17.6.0 to 17.7.0
- [Release notes](https://github.com/sindresorhus/globals/releases)
- [Commits](https://github.com/sindresorhus/globals/compare/v17.6.0...v17.7.0)

Updates `remotion` from 4.0.481 to 4.0.484
- [Release notes](https://github.com/remotion-dev/remotion/releases)
- [Commits](https://github.com/remotion-dev/remotion/compare/v4.0.481...v4.0.484)

Updates `typescript-eslint` from 8.61.0 to 8.62.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.62.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: "@remotion/cli"
  dependency-version: 4.0.484
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: eslint
  dependency-version: 10.6.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-dependencies
- dependency-name: eslint-plugin-import-x
  dependency-version: 4.17.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-dependencies
- dependency-name: globals
  dependency-version: 17.7.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-dependencies
- dependency-name: remotion
  dependency-version: 4.0.484
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: typescript-eslint
  dependency-version: 8.62.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-10 16:24:54 -04:00
ekkoitac 460c2519d9 fix: invert plain usage percentages (#473)
* fix: invert plain usage percentages

* fix: clarify usage direction controls

---------

Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-07-10 16:23:28 -04:00
黄黄汪 4fefc51139 fix: silence stderr on best-effort package-manager probes (#491)
When bun is on PATH but its global directory was never initialized
(no `bun add -g` ever run), launching the config TUI prints
`error: No package.json was found for directory "~/.bun/install/global"`
into the terminal. The TUI probes `bun pm bin -g` (and `where`/`which`,
`npm prefix -g`, `npm root -g`) via execFileSync without an stdio
option, so the child's stderr is inherited by the parent terminal even
though the thrown error itself is caught and treated as "not found".

Add stdio: ['ignore', 'pipe', 'ignore'] to these best-effort probes
(stdout stays piped for the return value), matching the pattern already
used elsewhere in the codebase. Adds regression tests asserting every
probe call suppresses stderr and that a throwing bun probe degrades
gracefully.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 15:58:40 -04:00
Matthew Breedlove 44f49d3efb Adding related project 2026-07-08 01:29:39 -04:00
Matthew Breedlove c56849db42 Adding related project 2026-07-07 02:37:29 -04:00
CC d47b7bd902 feat(context): make the context-window fallback configurable (#480)
Motivated by #429. The context widgets already read the real window from Claude Code's context_window.context_window_size when present, and from a [1m]-style model-name hint otherwise; only the final fallback was a hard-coded 200k. On an older Claude Code that does not report the window size for a 1M-context model, that fallback made the bar read against 200k (e.g. 750k/200k, pinned full).

Demote the hard-coded 200k to the default of a configurable last-resort fallback, CCSTATUSLINE_CONTEXT_SIZE_FALLBACK (positive integer, ignored when unset or invalid), mirroring CCSTATUSLINE_WIDTH. The live status field and model-name hint still take precedence, so the override only applies when the window is otherwise unknown.
2026-06-30 13:22:34 -04:00
Tyler Hebenstreit 477164e605 fix: self-heal legacy untagged ccstatusline hooks on sync (#490)
* fix: self-heal legacy untagged ccstatusline hooks on sync

stripManagedHooks only removed entries tagged 'ccstatusline-managed', so
untagged hooks written by pre-tag versions survived every sync and piled up
beside the freshly-written managed hooks. Also strip untagged entries whose
command matches the ccstatusline --hook invocation, making hook sync idempotent
for legacy installs. Non-ccstatusline user hooks are untouched.

* fix(hooks): preserve user commands during legacy cleanup

Treat the ccstatusline-managed tag as whole-entry ownership, but handle legacy untagged ccstatusline hooks at the individual command level. This prevents sync and uninstall cleanup from dropping unrelated user commands that share the same Claude hook entry with an old ccstatusline --hook command.

Add regression coverage for mixed legacy hook entries in both syncWidgetHooks and removeManagedHooks so future cleanup changes keep user-managed hook commands intact.

---------

Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-06-30 13:14:12 -04:00
Bernát Gábor f7d7af7c32 feat(widgets): add maxWidth support to Git Branch and Git Root Dir (#488)
The maxWidth field already exists on every widget in the schema, but only
the Custom Command widget honored it. On a single-line status bar a long
branch or repo name pushes the model, context and other widgets off the
end, where they are truncated as `|...`.

Let Git Branch and Git Root Dir truncate their own visible text to a
configured maxWidth (ellipsis included), so the rest of the line survives.
For hyperlinked widgets only the visible label is truncated; the link
target stays intact. A shared max-width helper carries the truncation,
the `(w)idth` keybind, the editor and the `max:N` modifier display.

Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-06-30 12:21:18 -04:00
Bernát Gábor c20e11145b feat(renderer): let widgets opt out of powerline auto-align (#489)
* feat(renderer): let widgets opt out of powerline auto-align

In powerline auto-align mode every widget is padded so its column lines
up with the widget above and below it. A single wide widget, such as a
long git branch, therefore stretches that column on every other line and
wastes horizontal space.

Add an `excludeFromAutoAlign` flag. A flagged widget, and everything
after it on the same line, stops contributing to the shared column widths
and stops receiving alignment padding, so it keeps its natural width
while the columns before it stay aligned. The items editor exposes an
`e(x)clude align` toggle and a `(no-align)` marker, shown only when
powerline auto-align is on.

* fix(renderer): constrain no-align to alignable widgets

Only allow excludeFromAutoAlign to be toggled from the items editor when powerline auto-align is active and the selected widget is not merged into a previous widget. This keeps the shortcut behavior aligned with the visible help text and prevents hidden exclusions from being persisted while the feature is unavailable.

Treat no-align as a merge-chain-head option in the UI, so widgets merged into a previous item no longer show an effective no-align marker. The renderer keeps merged-in widgets participating in their parent group width while still honoring exclusions on the first widget in the chain.

Add regression coverage for merged no-align width calculation and for the editor shortcut gate.

---------

Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-06-30 12:09:29 -04:00
CC cea4d3c9fd feat(widget): add optional glyph to Current Working Dir (#479)
Wire the shared symbol-override module into the Current Working Dir widget so it exposes the same (g)lyph option the Git and JJ widgets use. The default symbol is empty, so existing output is unchanged and no glyph is emitted unless the user sets one. The glyph also applies in raw value mode, so pairing it with raw value replaces the cwd: label with the chosen icon (e.g. a Nerd Font folder glyph).

Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-06-30 11:48:26 -04:00
Matthew Breedlove aae5addb13 fix(usage): soften reset timer startup state
Suppress fallback usage API errors when reset-style timer widgets are only missing reset timestamps. This prevents transient rate-limit responses during Claude startup from surfacing as [Rate limited] before embedded rate_limits data is available.

Show labeled loading placeholders for reset timers while reset data is unavailable, preserving raw mode as just [Loading]. Add regression coverage for suppressed startup fetch errors and raw/non-raw loading output.
2026-06-30 11:37:12 -04:00
Bernát Gábor 08d3c7cc00 🐛 fix(usage): clear in-flight lock on a successful fetch (#487)
* fix(usage): drop in-flight lock on fetch success

The pre-fetch usage.lock is written as a 'timeout' guard before the API
call but is never removed on success, so it lingers for LOCK_MAX_AGE. A
cache miss in that window (e.g. an account switch invalidating the token
fingerprint) then returns getStaleUsageOrError('timeout', ...), showing a
spurious [Timeout] while the API is healthy.

Clear the lock after a successful cache write. Genuine error and
rate-limit backoff locks are untouched.

Fixes #486

* fix(usage): preserve throttle for incomplete usage fetches

Only clear the in-flight usage lock after a successful API response satisfies the fields requested by the caller. Aggregate-only responses that are missing per-model fields remain cached, but keep the short timeout lock so later renders do not refetch the API on every status line render.

Add regression coverage for a weekly Sonnet usage request receiving an aggregate-only 200 response, verifying that the second fetch is throttled instead of issuing another API request.

---------

Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-06-30 11:28:21 -04:00
dependabot[bot] 6793c75515 chore(deps): bump actions/checkout from 6 to 7 (#476)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-30 11:14:31 -04:00
dependabot[bot] 70ca56353e chore(deps-dev): bump the dev-dependencies group with 5 updates (#477)
Bumps the dev-dependencies group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [@remotion/cli](https://github.com/remotion-dev/remotion) | `4.0.475` | `4.0.481` |
| [eslint](https://github.com/eslint/eslint) | `10.4.1` | `10.5.0` |
| [remotion](https://github.com/remotion-dev/remotion) | `4.0.475` | `4.0.481` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.60.1` | `8.61.0` |
| [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `4.1.8` | `4.1.9` |


Updates `@remotion/cli` from 4.0.475 to 4.0.481
- [Release notes](https://github.com/remotion-dev/remotion/releases)
- [Commits](https://github.com/remotion-dev/remotion/compare/v4.0.475...v4.0.481)

Updates `eslint` from 10.4.1 to 10.5.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v10.4.1...v10.5.0)

Updates `remotion` from 4.0.475 to 4.0.481
- [Release notes](https://github.com/remotion-dev/remotion/releases)
- [Commits](https://github.com/remotion-dev/remotion/compare/v4.0.475...v4.0.481)

Updates `typescript-eslint` from 8.60.1 to 8.61.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.61.0/packages/typescript-eslint)

Updates `vitest` from 4.1.8 to 4.1.9
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.9/packages/vitest)

---
updated-dependencies:
- dependency-name: "@remotion/cli"
  dependency-version: 4.0.481
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: eslint
  dependency-version: 10.5.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-dependencies
- dependency-name: remotion
  dependency-version: 4.0.481
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: typescript-eslint
  dependency-version: 8.61.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-dependencies
- dependency-name: vitest
  dependency-version: 4.1.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-30 11:13:40 -04:00
CC 8398d91706 feat: print version and exit on --version (#463)
Running ccstatusline --version launched the TUI instead of reporting the version. Handle the flag at the top of main() before mode detection: print getPackageVersion() and exit. Closes #461.
2026-06-19 13:57:09 -04:00
Zach Landquist 151521ca6e feat(usage): invalidate the usage cache when the account token changes (#460)
* feat(usage): invalidate the usage cache when the account token changes

The usage cache was keyed only by CACHE_MAX_AGE, so a logout/login to a different account served the prior account stale usage until the 180s TTL. Fingerprint the token (truncated SHA-256, an identifier not the token) and persist it with the cache; fetchUsageData resolves the token first and gates the file-cache read on a fingerprint match, so a mismatch refetches immediately. No-token falls through to the existing path; pre-fingerprint caches refetch once on upgrade.

Closes #459

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

* fix(usage): respect token hash for stale cache fallbacks

Thread the current usage token fingerprint into stale-cache fallback handling so cached usage from a previous account is not returned when a lock is active or the API is unavailable.

This keeps account-switch invalidation consistent across fresh file-cache reads, active locks, rate-limit backoffs, API errors, and parse errors while preserving the existing no-token fallback behavior.

Add regression coverage for active-lock and rate-limited fallback paths with mismatched cached token hashes.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-06-16 21:33:28 -04:00
Zach Landquist 2bb0cdfc65 fix: stop refetching usage every render on accounts without rate-limit windows (#434)
Co-authored-by: Claude <noreply@anthropic.com>
2026-06-16 21:19:53 -04:00
CC c04247f2f4 feat(tui): warn on an invalid settings.json and confirm before overwriting it (#458)
* feat(tui): add config-load warning + save-guard helpers

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

* feat(tui): surface invalid settings.json with a banner and save-guard

* fix(tui): address adversarial review (accurate confirm reason, post-install re-sync, save-failure flash + screen reset, Ctrl+S re-entrancy guard)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-06-16 18:46:48 -04:00
Matthew Breedlove 30de97505f Version bump and docs update
CI / Lint & Type Check (push) Has been cancelled
CI / Test (push) Has been cancelled
Publish / Publish to npm (push) Has been cancelled
CI / Build (push) Has been cancelled
v2.2.22
2026-06-16 02:00:58 -04:00
Shawn Sorichetti cb2555d180 fix: honour flex-separator in powerline render path (#411)
* fix: honour flex-separator in powerline render path

renderPowerlineStatusLine filters out flex-separator widgets at the
start of the function and never restores their layout effect, so a
configured flex-separator silently has no effect when powerline mode
is enabled. The non-powerline render path correctly distributes
terminal-width minus content-width across each flex-separator
position; this commit teaches the powerline path to do the same.

How:

- Before filtering, compute a Set of filteredWidgets indices that are
  immediately followed by a flex-separator in the original widget
  array.
- During the render loop, when one of those indices is reached, emit
  a closing powerline cap (a triangle in the previous widget's bg
  colour with no bg of its own) followed by a sentinel string, and
  skip the regular between-widgets separator.
- After the render loop, if any flex positions were marked, split the
  result string on the sentinel, measure the visible width of each
  part, distribute the remaining terminal width evenly across the
  flex positions as spaces, and reassemble. When the terminal width
  is unknown the sentinels are stripped so they cannot leak.

Tests:

Four new tests in renderer-flex-width.test.ts cover
- single flex-separator in powerline mode
- multiple flex-separators in powerline mode
- sentinel cleanup when terminal width is unknown
- non-powerline path regression check

All 585 tests pass (581 existing + 4 new). Lint and tsc are clean.

* fix(powerline): honor flex separator segments

Preserve flex separators when enabling powerline mode and keep them available in the widget catalog while manual separators remain disabled.

Track powerline flex positions against rendered widgets so hidden or empty widgets do not move right-aligned segments. Reserve end caps before distributing flex space to avoid truncating visible content.

Advance start cap selection across flex-created segments and subsequent lines so each visible powerline segment uses the next configured cap.

Add regression coverage for hidden widgets before flex separators, end caps with flex spacing, powerline catalog/settings behavior, and multi-line start cap sequencing.

* fix(renderer): preserve powerline separator cycling across flex segments

Treat flex separators as boundaries between independent powerline segments instead of consuming configured separator glyphs as caps.

Each flex-delimited segment now receives the matching start and end cap, while intra-segment separators advance using the actual number of rendered separator slots. Separator glyph selection now cycles through the configured list instead of clamping to the last configured glyph, so multi-line layouts continue the expected sequence after flex splits.

Update separator index accounting to skip flex boundaries and count slots independently within each segment. Add regression coverage for segment caps, flex separator accounting, separator cycling across lines, and explicit separator widgets.

Tests: bun test

Tests: bun run lint

* fix(powerline): align separator and cap sequencing

* fix(renderer): respect separator merge boundaries

Treat explicit separators and flex separators as hard merge boundaries when calculating separator slots, powerline theme slots, and rendered powerline elements.

This prevents no-padding merges from crossing separators, keeps theme color advancement isolated on each side of a boundary, and keeps auto-alignment width groups from spanning flex separators.

When terminal width is unknown, render powerline flex separators as a single visible space instead of dropping the boundary entirely so adjacent segments remain separated.

Add regression coverage for separator indexing, powerline theme slot counting, powerline flex separator spacing, padding behavior, theme color advancement, and auto-align width grouping.

---------

Co-authored-by: Shawn Sorichetti <ssoriche@users.noreply.github.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-06-16 01:22:48 -04:00
Zach Landquist 50195e15ae test: make the suite pass on Windows hosts (#454)
Sixteen specs failed on a Windows host while passing on POSIX CI. The production code is already platform-aware; only the specs assumed a POSIX host. These changes are test-only.

terminal: pin process.platform for the probe specs via a defineProperty setter restored in afterEach (vi.spyOn on the getter did not re-apply reliably across specs); also consumes a leaked mockImplementationOnce. claude-settings: pin platform and stub getConfigPath to a literal POSIX path so the --config quoting specs avoid path.resolve drive-prefixing. usage-token: build the credentials path via path.join. config-dir: assert path.resolve of the input.

Closes #453

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-15 17:40:52 -04:00
Zach Landquist 7be789fa56 feat(compaction): make the reclaimed-tokens arrow an overridable glyph (#452)
The reclaimed-tokens suffix hardcoded its leading down-arrow. Route it through the existing symbol-override slot system (metadata slot symbolReclaimed) so it can be customized or cleared from the glyph editor, the same way the git widgets expose their symbols.

The default is unchanged, so existing configs render identically. An empty override drops the glyph but keeps the separating space.

Closes #448

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-15 17:22:30 -04:00
Zach Landquist e51b0027c6 fix(context-bar): render counts >= 1M as "1.0M" instead of "1000k" (#451)
ContextBar formatted used/total with Math.round(n / 1000) + 'k', so a 1M context window showed '1000k'. Add an optional decimals arg to formatTokens (default 1, behavior-preserving for all existing callers) and route the bar through it with decimals=0, so it keeps compact whole-number k and gains the existing M rollup.

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-15 17:08:31 -04:00
Matthew Breedlove b20296c008 fix(config): preserve symlinked settings on save
Resolve symlinked settings paths before performing the atomic temp-file rename. This keeps dotfile-managed settings.json files and custom --config links intact while still updating the linked target.

Write the temporary file beside the resolved target so the final rename remains atomic and avoids cross-device replacement issues. Fall back to the configured settings path when the file does not exist yet.

Add a regression test that saves through a symlinked settings file, verifies the link survives, verifies the target JSON updates, and checks temp cleanup in both directories.
2026-06-15 16:16:25 -04:00
CC f3ecfe8254 fix(config): non-destructive recovery + loud warning for invalid settings.json (#447)
* fix(config): stop overwriting invalid settings.json on load

On a parse or validation failure, loadSettings now returns defaults in
memory and leaves the user's file untouched instead of backing it up and
overwriting it with defaults. The render path runs loadSettings on every
prompt, so the old behavior silently reset a malformed config. Removes the
now-redundant .bak backup (the preserved original is the backup).

Closes #393

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

* fix(config): write settings atomically via temp file + rename

Route every config write through a write-to-temp-then-rename, so a reader
(the render path runs on every prompt) never observes a partially written
settings.json. Mirrors the existing atomic-write idiom in git.ts. The temp
is cleaned up if the write fails.

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

* fix(config): show a loud warning when settings.json can't be loaded

When loadSettings falls back to defaults (invalid or unreadable settings.json),
the statusline now prepends a red "invalid config" badge so the failure is
visible instead of silent — previously the only signal was a stderr message the
piped statusline never surfaces. The detailed reason still goes to stderr.

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

* fix(config): validate a migration before persisting it

Previously a versioned config was migrated and written back to disk *before*
the final schema validation, so a faulty migration could overwrite the user's
original file with invalid output. Now the migrated result is validated first
and persisted only if it passes; on failure the original file is left untouched
and defaults are used in memory. This makes the non-destructive guarantee total
and lets the schema-fail path honestly report "file left unchanged".

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

* fix(config): don't overwrite an unreadable settings.json when saving install metadata

saveInstallationMetadata loads settings then writes them back to record the
installation method. If the existing file was unreadable, loadSettings returns
defaults in memory, so the write would discard the user's (recoverable) file —
the last path still violating the non-destructive contract. It now skips the
write when the existing config could not be read, leaving the file untouched
(metadata is non-critical and is persisted on the next clean save).

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-15 15:35:00 -04:00
Zach Landquist 4f01094f21 feat: add per-widget dim styling, whole widget or parens-only (#433)
* feat: add per-widget dim styling, whole widget or parens-only

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

* fix(tui): scope dim styling in previews

Preserve parens dim styling when foreground gradients render in both regular and powerline paths.

Emit a combined intensity reset when restoring bold after dim so Ink preserves the dim reset in preview output.

Reset whole-widget dim before powerline separators and end caps, and render ColorMenu style indicators as one suffix to avoid badge wrapping.

Add regression coverage for gradient dim composition, powerline dim boundaries, Ink preview output, and ColorMenu indicator layout.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-06-15 15:05:40 -04:00
dependabot[bot] 2159379a3a chore(deps-dev): bump the dev-dependencies group with 4 updates (#446)
Bumps the dev-dependencies group with 4 updates: [@remotion/cli](https://github.com/remotion-dev/remotion), [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react), [remotion](https://github.com/remotion-dev/remotion) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


Updates `@remotion/cli` from 4.0.472 to 4.0.475
- [Release notes](https://github.com/remotion-dev/remotion/releases)
- [Commits](https://github.com/remotion-dev/remotion/compare/v4.0.472...v4.0.475)

Updates `@types/react` from 19.2.16 to 19.2.17
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

Updates `remotion` from 4.0.472 to 4.0.475
- [Release notes](https://github.com/remotion-dev/remotion/releases)
- [Commits](https://github.com/remotion-dev/remotion/compare/v4.0.472...v4.0.475)

Updates `typescript-eslint` from 8.60.0 to 8.60.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.60.1/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: "@remotion/cli"
  dependency-version: 4.0.475
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: "@types/react"
  dependency-version: 19.2.17
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: remotion
  dependency-version: 4.0.475
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: typescript-eslint
  dependency-version: 8.60.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 04:46:23 -04:00
Matthew Breedlove 6d4f10a273 docs(thinking-effort): clarify ultracode xhigh reporting 2026-06-15 04:20:27 -04:00
Matthew Breedlove 29895206d7 Version bump and docs update
CI / Lint & Type Check (push) Has been cancelled
CI / Test (push) Has been cancelled
Publish / Publish to npm (push) Has been cancelled
CI / Build (push) Has been cancelled
v2.2.21
2026-06-15 02:45:16 -04:00
CC bfe86ab904 fix(renderer): render 999950-999999 tokens as '1.0M' not '1000.0k' (#444)
Values in [999950, 999999] hit the thousands branch where (count/1000).toFixed(1) rounds up to '1000.0', producing '1000.0k'. Lower the millions threshold to that exact rounding boundary so they render as '1.0M'. Adds a direct unit test for formatTokens (previously exercised only indirectly via widget spies).

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-06-15 02:35:26 -04:00
CC 3c1f472598 feat(compaction): opt-in trigger split and tokens reclaimed (#445)
* refactor(compaction): compute stats struct (count, byTrigger, tokensReclaimed) from markers

Parse compactMetadata.{trigger,preTokens,postTokens} during the existing
compact_boundary scan. No widget behavior change yet.

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

* refactor(compaction): freeze zero stats, floor reclaimed at 0, clarify naming

Address review feedback on the data layer: freeze the shared
ZERO_COMPACTION_STATS (incl. nested byTrigger); floor each marker's reclaimed
contribution with Math.max(0, ...) so postTokens>preTokens can't go negative;
rename metaObj -> metaRecord; add unknown-trigger and negative-reclaim tests.

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

* feat(compaction): opt-in trigger split via (s) keybind

Adds a per-item showTriggers toggle that appends '(2 auto, 1 manual)';
unknown bucket shown only when > 0; omitted at count 0. Rendering now flows
through formatStats, shared by live render and preview.

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

* test(compaction): clarify zero-suffix test name; cover manual-only bucket

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

* feat(compaction): opt-in tokens reclaimed via (t) keybind

Adds a per-item showReclaimed toggle that appends the reclaimed token total
(e.g. 887.0k) via the shared formatTokens humanizer; omitted when 0. Stacks
independently with the trigger split.

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

* refactor: extract formatTokens to a leaf module to avoid a circular import

CompactionCounter importing formatTokens from renderer.ts created a cycle
(renderer -> widget registry -> CompactionCounter) that broke isolated runs of
its test file with a TDZ error. Move formatTokens into src/utils/format-tokens.ts;
renderer re-exports it so existing importers are unaffected.

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

* test(compaction): cover tokens reclaimed with a non-default format

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

* docs(compaction): document trigger-split and tokens-reclaimed toggles

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

* refactor(compaction): apply code-review feedback

- use shared isMetadataFlagEnabled/toggleMetadataFlag for the trigger/reclaimed
  flags instead of local copies
- freeze SAMPLE_STATS to match ZERO_COMPACTION_STATS
- guard tokensReclaimed against non-finite (overflow) marker differences
- docs: note the unknown trigger bucket and that reclaimed is floored/omitted at 0

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

* fix(compaction): carry the formatTokens rounding fix into the extracted leaf

The reclaimed display calls formatTokens; the extracted format-tokens.ts held the
pre-fix version, so a reclaimed total in [999950, 999999] rendered '1000.0k'
instead of '1.0M'. Apply the same threshold fix as PR #444 here, so the reclaimed
figure is correct and merging both PRs in either order cannot regress it.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-15 02:11:32 -04:00
dmsp 2fc4921b77 feat(widget): add Cache Hit / Read / Write widgets (#423)
* feat(widgets): add Cache Hit / Read / Write widgets

Adds a new "Cache" widget category that surfaces prompt-cache efficiency,
which previously was hidden: TokensCached summed cache_read and
cache_creation into one opaque number.

New widgets, each toggling between per-turn ("last action") and session
scope via the 't' keybind:
- Cache Hit   - read / (read + creation), %
- Cache Read  - cache_read tokens with context share, e.g. "88.0k (84.5%)"
- Cache Write - cache_creation tokens with context share, e.g. "12.0k (11.5%)"

No new data source: turn scope reads context_window.current_usage from the
live status JSON; session scope sums the transcript. TokenMetrics gains
optional cacheReadTokens / cacheCreationTokens (split of cachedTokens, which
is unchanged for backward compatibility).

Defaults: Cache Hit/Read green, Cache Write yellow (recolorable in the TUI).

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

* feat(cache): add hide option for empty values

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-06-14 04:12:44 -04:00
Sebastian Szewczyk 6126ff75f2 feat(widget): add cache-hit-rate widget (#409)
Adds a Cache Hit Rate widget that mirrors the Anthropic Console's
prompt-cache hit-rate formula: cache_read / (cache_read + cache_creation
+ fresh_input). Returns null on empty transcripts so it does not flash
0% before any tokens are recorded.

Splits cacheReadTokens and cacheCreationTokens out of the existing
cachedTokens roll-up on TokenMetrics (both new fields are optional and
cachedTokens is preserved unchanged), so downstream widgets and configs
are unaffected.

Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-06-14 03:53:12 -04:00
CC dd820ea42f feat(custom-command): include terminal_width in stdin JSON (#396)
* feat(custom-command): include terminal_width in stdin JSON

Custom Command widgets receive context.data as JSON over stdin, but the
terminal width was not part of it, so scripts had no way to adapt their
output to the terminal (tput/stty/$COLUMNS all return 80 in the piped
context).

Populate context.terminalWidth once in the render pipeline via
getTerminalWidth() (which also lets the renderer reuse it instead of
re-probing per line) and add terminal_width to the JSON piped to custom
commands when a numeric width is known. The field name uses snake_case
to match the existing Claude Code payload (session_id, current_dir, ...).

Resolves the request in upstream issue #308.

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

* docs(custom-command): document terminal_width in stdin JSON

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-06-14 03:44:17 -04:00
Martijn Riemers 7db0914856 feat: add Extra Usage Used widget showing spent overage budget (#417)
* fix: format extra usage in the currency reported by the usage API

The usage API reports the account's billing currency
(extra_usage.currency, e.g. "EUR"), but ExtraUsageRemaining always
formatted with a hardcoded dollar sign, showing non-USD budgets with
the wrong symbol.

Parse the currency field through the API response schema, file cache,
and prefetch merge, and format the remaining budget with
Intl-based currency formatting (falling back to USD when the field is
absent or invalid, preserving current output for USD accounts).

Fixes #415

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

* feat: add Extra Usage Used widget showing spent overage budget

The usage API's extra_usage.used_credits is already fetched and cached
(extraUsageUsed), but no widget could display it. Accounts with extra
usage enabled and no monthly limit configured get neither of the
existing widgets (both require limit-derived fields), so the spent
amount is the only displayable extra usage number for them.

Adds an extra-usage-used widget modeled on extra-usage-remaining,
including the hide-if-disabled toggle and raw value support.

Closes #414

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

* feat: format Extra Usage Used with the API-reported currency

Builds on the formatUsageCurrency helper from the currency fix so the
new widget shows the account's billing currency too.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-06-14 03:39:02 -04:00
Martijn Riemers 2d85849476 fix: format extra usage in the currency reported by the usage API (#418)
The usage API reports the account's billing currency
(extra_usage.currency, e.g. "EUR"), but ExtraUsageRemaining always
formatted with a hardcoded dollar sign, showing non-USD budgets with
the wrong symbol.

Parse the currency field through the API response schema, file cache,
and prefetch merge, and format the remaining budget with
Intl-based currency formatting (falling back to USD when the field is
absent or invalid, preserving current output for USD accounts).

Fixes #415

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-06-14 03:33:25 -04:00
CC df25207d18 fix(compaction): count compact_boundary markers instead of inferring from context-% drops (#425)
* feat(compaction): add marker-based compaction counting

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

* feat(compaction): count markers instead of inferring from context-% drops

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

* refactor(compaction): remove obsolete context-%-drop heuristic and cache

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

* test(compaction): pin explicit isSidechain:false; docs: marker-count wording

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-06-14 03:12:56 -04:00