* fix(widget): claude-account-email respects CLAUDE_CONFIG_DIR
Closes#317.
The widget computed the .claude.json path as ${configDir}/../.claude.json,
which only happens to work when configDir is the default ~/.claude (going
up one level lands at $HOME). When CLAUDE_CONFIG_DIR points elsewhere
(e.g. ~/.claude-work), the same .. heuristic still lands at $HOME, so
all profiles read the same .claude.json and display the same account
email — the symptom reported in the issue.
Fix: branch the path resolution on whether CLAUDE_CONFIG_DIR is set.
Claude Code stores .claude.json inside CLAUDE_CONFIG_DIR when that env
var is set, otherwise it lives at ~/.claude.json.
Also tightened the surrounding code while in the area:
- Drop redundant existsSync check (readFileSync ENOENT is already caught)
- Drop redundant path.resolve on already-absolute paths
- Tighten email field check from truthy (!email) to typeof+length, so a
non-string oauthAccount.emailAddress can't render as Account: 12345
New tests cover:
- $CLAUDE_CONFIG_DIR/.claude.json is read when env var is set (regression)
- $HOME/.claude.json is NOT silently read when CLAUDE_CONFIG_DIR points
to a dir without one (no profile leak)
- Non-string emailAddress returns null (typeof guard)
* chore: Centralize implementation of getClaudeJsonPath similar to getClaudeSettingsPath and getClaudeConfigDir
---------
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
Pressing k on a selected widget inserts a copy just after it and moves
selection to the clone. In Powerline mode, the clone gets a fresh
background color to avoid adjacent duplicate bands.
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
* feat(git-branch): support GitLab and self-hosted hosts for branch link URLs
Replace the GitHub-only parseGitHubBaseUrl helper with a forge-agnostic
buildBranchWebUrl built on the existing parseRemoteUrl + RemoteInfo
plumbing in git-remote.ts. GitBranch links now work for GitHub, GitLab,
and compatible self-hosted remotes that expose the standard
host/owner/repo path.
Uses a single /tree/<branch> suffix because GitLab redirects it to its
canonical /-/tree/<branch> form, so one format covers both forges.
parseGitHubBaseUrl and its helper were the only GitHub-specific URL
builders in hyperlink.ts; remove them and their tests now that GitBranch
is the only caller and has been migrated.
Also rename the metadata key linkToGitHub to linkToRepo to match the
naming used by GitOriginOwnerRepo / GitOriginRepo / GitUpstreamOwner.
Legacy linkToGitHub is preserved as a read-only fallback: toggling the
modifier strips both keys and writes only linkToRepo, so users who
interact with the feature get their settings quietly upgraded. Explicit
linkToRepo:false wins over legacy linkToGitHub:true.
* feat(git-pr): support GitLab merge requests via glab
Replace the GitHub-only gh-pr-cache with a forge-aware git-review-cache.
The widget now renders pull requests for GitHub (via `gh`) and merge
requests for GitLab (via `glab`), picking the CLI per-repo based on the
origin remote host:
- host contains `github` → `gh`
- host contains `gitlab` → `glab`
- unknown/self-hosted host → probe each CLI with
`gh/glab auth status --hostname <h>` and use whichever is authenticated
against that host; if neither is, stay quiet rather than fire wasted
queries
- no origin remote → try both and let the CLI resolve the repo itself
For forks where the CLI would default-resolve to the parent repo, the
fetch falls back to `--repo <origin-url>` after an empty first query so
the user's fork PR/MR is still found.
GitPr.ts now records the provider on the cache entry and renders "MR #N"
for glab and "PR #N" for gh; raw mode stays `#N` for both. Widget display
name becomes "Git PR/MR".
Also rename the widget `type` from `git-pr` to `git-review` to match the
internal `git-review-cache` name. Legacy `git-pr` configs keep rendering
via a resolver in widgets.ts, and loadSettings silently rewrites them to
`git-review` in-memory so the canonical name lands on the next save —
same pattern as the linkToGitHub → linkToRepo rewrite in the previous
commit.
* docs(usage): describe GitHub and GitLab behavior for Git widgets
Update the Git-section intro to mention both forges and the CLI-selection
rules used for self-hosted hosts, and note on the Git PR keybind line
that the widget renders "MR" for GitLab origins.
* chore(gitlab-support): trim verbose comments to match repo style
Most of this repo's TS files carry zero or a handful of one-line comments;
the verbose rationale blocks added during the GitLab work (multi-paragraph
JSDocs on buildBranchWebUrl, getProviderCandidates, fetchFromProvider, etc.
and long inline explainers in the tests) stood out. Trim them to short
one-liners where the why is genuinely non-obvious, and drop the rest.
* fix(git-review): preserve self-hosted remote ports
* fix(tui): clamp status preview to terminal width
---------
Co-authored-by: jmecham <jmecham@foundrydigital.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
Enable circular cursor movement across all TUI menus and lists —
pressing up at the first item wraps to the last, and pressing down
at the last wraps to the first. Also applies to move/reorder modes,
allowing items to be moved cyclically through the list boundaries.
Added 10 unit tests covering wrap-around boundaries for normal
navigation, move mode, picker categories, widgets, and top-level search.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add refreshInterval configuration for Claude Code status line
Add a new "Configure Status Line" menu option (visible when ccstatusline
is installed) that lets users set the Claude Code statusLine
refreshInterval. The setting is written directly to Claude Code's
settings.json.
- Add refreshInterval to ClaudeSettings statusLine interface
- Add getRefreshInterval/setRefreshInterval utility functions
- Add Claude Code version detection (claude --version) with 5s timeout
- Gate refreshInterval behind Claude Code >=2.1.97 version check
- Default to 10s on fresh install, preserve existing value on re-install
- Show disabled state with version requirement message for older versions
- Add RefreshIntervalMenu TUI component with inline numeric input
- Add isKnownCommand path-boundary matching for local dev commands
- Add comprehensive tests for version detection, install flow, and validation
* fix: correct Claude status line state and local install detection
---------
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
- Add xhigh to TranscriptThinkingEffort, ThinkingEffortLevel, and
ClaudeSettings.effortLevel unions
- Accept xhigh and xHigh casings from /model stdout and settings.json
- Pass unknown-but-word-shaped effort values through with a trailing "?"
marker (e.g. "super-max?"), so future Claude Code effort levels render
gracefully without code changes
- Display "Thinking: default" when no effort signal is available from
the transcript or Claude settings, replacing the prior silent medium
fallback
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Fix token overcounting from streaming duplicate JSONL entries
Claude Code writes multiple JSONL entries per API call during streaming:
intermediate entries have stop_reason: null, and only the final entry has
a string value like "end_turn" or "tool_use". The getTokenMetrics function
was summing all entries, inflating the total by ~2.5x.
Now only counts final entries (those with a truthy stop_reason). Falls
back to counting all entries when no stop_reason data is present, for
backward compatibility with older transcript formats.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(jsonl): dedupe live streaming token metrics
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
Removes trailing parenthetical (e.g., "(1M context)", "(200K context)")
from model display names. "Opus 4.6 (1M context)" becomes "Opus 4.6".
Users who want the context window size displayed can add the existing
context-length widget to their layout.
Fixes#238
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
* feat: add git status, git remote, worktree, and custom symbol widgets
Add 19 new widgets split out from PR #255 per reviewer request:
- Git status widgets (addresses #20): git-status, git-staged,
git-unstaged, git-untracked, git-ahead-behind, git-conflicts, git-sha
- Git remote widgets: git-origin-owner, git-origin-repo,
git-origin-owner-repo, git-upstream-owner, git-upstream-repo,
git-upstream-owner-repo, git-is-fork
- Worktree widgets: worktree-mode, worktree-name, worktree-branch,
worktree-original-branch
- Custom symbol widget
Supporting changes:
- Add git command cache and status functions to git.ts
- Add git-remote utilities for fork detection and URL parsing
- Add customSymbol, hide, getNumericValue to Widget types
- Add worktree field to StatusJSON
- Add gitData field to RenderContext
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: correct git status detection for unstaged changes and merge conflicts
Fixed three issues in git status parsing:
1. Unstaged pattern was incomplete - only detected M/D, missed merge conflicts
(UU, AU, DU, AA, UA, UD) and other status codes (R, C, T)
2. Added -z flag for NUL-terminated output to properly handle filenames with
special characters
3. Changed trim() to trimEnd() to preserve significant leading spaces in git
porcelain format (e.g., ' M file.txt' for unstaged modifications)
Added comprehensive test coverage with 20 new test cases covering all merge
conflict scenarios, rename/copy/type-change detection, and edge cases.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* feat: add conflict detection to git status widget
Added '!' indicator to GitStatus widget to show merge conflicts with priority
ordering: !+*? (conflicts, staged, unstaged, untracked).
Conflicts are shown first as they're blocking - work cannot proceed until
resolved. The priority ordering reflects urgency: blocking issues, intentional
work, unsaved changes, then undecided files.
Added conflict detection for all merge conflict states: DD, AU, UD, UA, DU, AA, UU
Added test coverage for DD (both deleted) case and mixed status with conflicts
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix(git-widgets): refine worktree naming and git parsing
Tighten the git widget plumbing and align the new worktree widgets with the Git naming scheme.
- parse nested namespace remotes correctly for both SCP-style and URL-based git remotes
- ignore rename and copy source-path entries when reading porcelain -z status output
- keep the git utility regression coverage aligned with the parser changes
- rename Worktree* widget files and classes to GitWorktree* and update display names
- preserve the existing worktree widget type ids while rewiring exports and manifest entries
* fix(git-widgets): correct conflict rendering and upstream resolution
Improve git widget behavior in preview and runtime rendering.
- treat text-presentation pictographs like the warning symbol as narrow unless explicitly emoji-style
- keep git conflicts visible at zero and return numeric raw values for the conflict count
- add regression coverage for glyph width handling and conflict widget rendering
- resolve git-upstream widgets through the branch tracking remote when no literal upstream remote exists
- cover tracked-upstream fallback behavior in git remote utility tests
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
* Add Claude account email widget for ccswitch integration
* feat(widgets): add Claude account email widget
Adds a new ClaudeAccountEmail widget in the Session category that displays
the user's email by reading from git config user.email or falling back to
common environment variables (GIT_AUTHOR_EMAIL, GIT_COMMITTER_EMAIL, EMAIL, USER_EMAIL).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(widgets): read Claude account email from ~/.claude.json
Replaces the git config approach with reading oauthAccount.emailAddress
directly from ~/.claude.json — the actual Claude account credentials file.
Respects CLAUDE_CONFIG_DIR environment variable. No external tools required.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: Remove unnecessary package-lock.json as this repo uses bun.lock
* fix(widgets): label claude account email output
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
* feat: add a global minimalist mode that defaults widgets to raw mode
Adds the foundational plumbing for a global minimalist mode toggle:
- RenderContext.minimalist flag (optional boolean)
- Settings.minimalistMode field (default false, Zod-managed)
- Threads the flag from settings into RenderContext at render time in
both piped mode (ccstatusline.ts) and TUI preview (StatusLinePreview.tsx)
Adds an (m) keybind to the Global Overrides TUI menu to toggle minimalist
mode on/off. The setting is persisted immediately via onUpdate, and the
preview reflects the change in real time via the RenderContext flag.
When minimalist mode is active, the renderer forces rawValue: true on all
widgets before calling render(), stripping decorative labels and prefixes
globally. No per-widget changes needed.
* fix: keep strict settings literals for minimalist mode
---------
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
* feat: fzf when adding widgets
Adds fuzzy matching to the widget picker as a fallback when the query
has no exact substring match anywhere.
Characters must appear in order but need not be adjacent; matches are
scored by span, consecutive runs, and word boundaries, and ranked below
all exact matches.
Selection now resets to the top-ranked result on every keystroke instead
of staying pinned to the previously-selected widget type.
Matched characters are highlighted in the picker list: yellow-bold for
unselected rows, green-bold for the selected row.
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: prefer widget picker initialisms over incidental matches
Give display-name initialism matches their own ranking path so abbreviation searches like tw, tc, ti, and to select the intended widget instead of description or type fallbacks. Keep picker highlighting aligned with the ranking logic and add regressions for the reported fuzzy-match cases.
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
* feat: use native rate_limits field from Claude Code 2.1.80
Claude Code 2.1.80 now sends rate_limits in the statusline JSON input
with five_hour and seven_day windows (used_percentage + resets_at).
When present, usage widgets consume this data directly instead of
fetching from the Anthropic API. Falls back to the API fetch for
older Claude Code versions that don't send rate_limits.
Also updates the example payload to match the real 2.1.80 format.
* fix: revert example payload to use placeholder values
* chore: align test values with example payload
* fix: Fallback to API usage query if any of the rate limit fields are missing
---------
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
* feat: add hyperlink support to GitBranch and GitRootDir widgets
- GitBranch: toggle (l)ink wraps branch name in OSC 8 link to
github.com/owner/repo/tree/<branch>; only activates for GitHub
remotes, falls back to plain text otherwise
- GitRootDir: toggle (l)ink wraps project root name in OSC 8
cursor://file/<path> link to open the directory in Cursor
- Add shared src/utils/hyperlink.ts with renderOsc8Link and
parseGitHubBaseUrl helpers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Improve git and IDE hyperlink handling
- encode GitHub branch refs so reserved characters do not break branch links
- accept ssh:// and credentialed GitHub remotes when building GitHub URLs
- replace the Cursor-only repo root toggle with IDE link modes for VS Code and Cursor
- build IDE file links from encoded paths so Windows, spaces, #, and UNC paths work
- reuse the shared OSC-8 hyperlink renderer from the Link widget
- add focused coverage for hyperlink parsing and the updated git widgets
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
* feat: add vim-mode widget
Adds a new status line widget that displays the current vim mode when
Claude Code's vim mode is enabled.
Claude Code includes a `vim` field in the status hook JSON when vim mode
is active. The widget reads this field and renders a compact indicator.
When vim mode is not enabled, the widget returns null and hides itself.
Five display formats are supported via metadata.format:
- icon-dash-letter (default): -N / -I
- icon-letter: N / I
- icon: icon only
- letter: N / I
- word: NORMAL / INSERT
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix vim mode defaults and editor toggles
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
* fix(timer): show days in formatUsageDuration for durations >= 24h
Durations of 24+ hours now display a days component instead of
raw hours (e.g. "1d 12hr 30m" instead of "36hr 30m"). Applies to
both normal and compact formats. Fixes#210.
* refactor(timer): simplify formatUsageDuration and show 0m for zero duration
Unify compact/normal branches into a single code path differing only
in hour label and separator. Zero duration now displays "0m" instead
of "0hr"/"0h".
* Add weekly reset hours toggle
Default weekly reset timers to day-based formatting while adding an hours-only toggle and keeping preview text aligned with live rendering.
Also move custom keybind visibility back into widgets and clear disabled toggle metadata when those options become unavailable in the editor.
Supersedes #235Closes#235
---------
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
Adds a new "thinking-effort" widget that shows the current
thinking effort level (low, medium, high) in the status bar.
Data source priority:
1. StatusJSON "thinking.effort" field (from Claude Code status hook)
2. "thinkingMode" key in ~/.claude/settings.json (user fallback)
Usage in config:
{ "type": "thinking-effort", "color": "magenta" }
Closes#209
Changes:
- src/types/StatusJSON.ts: add optional "thinking" field
- src/widgets/ThinkingEffort.ts: new widget implementation
- src/widgets/index.ts: export ThinkingEffortWidget
- src/utils/widget-manifest.ts: register "thinking-effort" type
- src/widgets/__tests__/ThinkingEffort.test.ts: 13 unit tests
Co-authored-by: GitHub User <user@example.com>