提交

提交图

269 次代码提交

作者 SHA1 备注 提交日期
Matthew Breedlove f198e72a62 Version bump and doc update
CI / Lint & Type Check (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Build (push) Has been cancelled
v2.2.13
2026-05-11 03:28:05 -04:00
Sam Hopkins 29c47242a8 feat: add weekly Sonnet and Opus usage widgets (#361)
* feat: add weekly Sonnet and Opus usage widgets

* Fix per-model usage prefetch fallback

* Fix per-model usage cache handling

---------

Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-05-11 02:17:49 -04:00
Bentley(중길) 23b01a3877 docs: add AIWatch integration example (Custom Command + Related Projects) (#365)
Adds a "Integration Example: AIWatch" section to docs/USAGE.md mirroring
the existing ccusage section, plus a Related Projects link in README, per
the placement the maintainer chose in #362.

AIWatch (ai-watch.dev) monitors live status for 30+ AI APIs/apps; the
Custom Command one-liner surfaces degraded providers in the status line
and renders empty when everything's operational.

Co-authored-by: Bentley <bentley@naemomlab.com>
2026-05-11 01:48:40 -04:00
Thomas BENOIT 0ae20bea5e feat: add voice status widget showing Claude Code voice input state (#357)
* feat: add voice status widget showing Claude Code voice input state

* Fix voice status workspace config lookup

---------

Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-05-11 01:45:32 -04:00
Thomas BENOIT 456cc46de7 feat: short bar mode for timer widgets (#358) 2026-05-11 01:27:30 -04:00
dependabot[bot] a69e09a9a9 chore(deps-dev): bump the dev-dependencies group with 2 updates (#354)
* chore(deps-dev): bump the dev-dependencies group with 2 updates

Bumps the dev-dependencies group with 2 updates: [typescript](https://github.com/microsoft/TypeScript) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


Updates `typescript` from 6.0.2 to 6.0.3
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Commits](https://github.com/microsoft/TypeScript/compare/v6.0.2...v6.0.3)

Updates `typescript-eslint` from 8.58.2 to 8.59.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.59.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: typescript
  dependency-version: 6.0.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: typescript-eslint
  dependency-version: 8.59.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix: satisfy updated lint rules

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-05-11 01:17:29 -04:00
Matthew Breedlove 0a749bb1ef fix(hooks): suppress no-op JSON output
Exit silently after hook side effects so Claude Code 2.1.117+ does not validate an empty JSON object.

Resolves #364
2026-05-11 00:57:25 -04:00
Sean Brandt bbd19313cc feat(jj): add Jujutsu VCS widgets (#205)
* feat(jj): add jj utility functions and shared hide-no-jj feature

Add jj VCS utility module mirroring the existing git utilities with
workspace detection, command execution, and diff stat parsing. Add
shared jj-no-jj toggle for hiding 'no jj' messages in jj widgets.

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

* feat(jj): add JjChange and JjBookmark widgets

Add two new Jujutsu VCS widgets mirroring the GitBranch pattern:
- JjChange: displays current jj change ID with `jj:` prefix
- JjBookmark: displays current jj bookmark name(s) with `@` prefix,
  showing `(none)` when no bookmarks are set
- Add `runJjRaw` utility to distinguish empty output from errors

Both widgets support raw value mode, hide-no-jj configuration,
and include full test coverage (17 tests).

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

* feat(jj): add JjChanges, JjInsertions, JjDeletions, JjRootDir, and JjDescription widgets

Add five new Jujutsu VCS widgets with full test coverage:
- JjChanges: combined insertions/deletions count (+ins,-del)
- JjInsertions: insertion count from jj diff --stat
- JjDeletions: deletion count from jj diff --stat
- JjRootDir: workspace root directory name extraction
- JjDescription: current change description via jj log

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

* feat(jj): add JjWorkspace widget for displaying current workspace name

Add getJjCurrentWorkspace() utility that parses `jj workspace list` output
to extract the current workspace name from the first line. Implement
JjWorkspaceWidget with blue color, W: prefix, raw value support, and
hide-no-jj toggle. Include tests for both the utility function and widget.

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

* feat(jj): register all jj widgets in manifest and add shared behavior tests

Add all 8 jj widgets (JjChange, JjBookmark, JjChanges, JjInsertions,
JjDeletions, JjRootDir, JjDescription, JjWorkspace) to the widget
exports and manifest registry. Add shared behavior test suite validating
hide-no-jj keybind, metadata toggling, editor display, and Jujutsu
category across all jj widgets.

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

* fix(jj): consolidate runJj/runJjRaw, fix double subprocess and hideNoJj invariant

- Merge runJjRaw into runJj with allowEmpty parameter to eliminate
  duplication while preserving semantic distinction between empty
  output and command failure
- Fix JjRootDir calling jj workspace root twice per render by
  removing redundant isInsideJjWorkspace guard
- Extract getRootDirName to module-level function (matches project
  convention of no private helper methods)
- Fix JjDescription ignoring hideNoJj flag when command fails after
  workspace check passes
- Fix JjDescription preview string inconsistency ('(no description
  set)' vs '(no description)')
- Add tests for allowEmpty behavior and JjDescription hideNoJj
  command failure case

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

* feat(git): add hideWhenJj toggle to suppress git widgets in jj workspaces

Add a new per-widget toggle that hides git widgets when a jj workspace
is detected, enabling clean colocated repo support. Users can press 'j'
in the TUI items editor to enable this on any git widget.

- Add git-hide-when-jj.ts shared module with metadata flag, keybind,
  and editor display helpers
- Update all 6 git widgets (Branch, Changes, Insertions, Deletions,
  RootDir, Worktree) with the new toggle
- Compose modifier text from both hideNoGit and hideWhenJj flags
- Chain action handlers via nullish coalescing
- Add shared behavior tests for toggle, keybind, and modifier display

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

* fix(jj): remove git widget jj hide keybind

* chore(git): restore git modifier helper usage

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-04-28 04:26:03 -04:00
Daniel Correia ad9519e1b9 feat: Jujutsu VCS (jj) widgets (#189)
* feat: add jujutsu widgets

* fix: better revset functions to improve usability

* Fix JJ widget command execution

---------

Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-04-28 03:45:39 -04:00
Matthew Breedlove af87d5594b Version bump and doc update
CI / Lint & Type Check (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Build (push) Has been cancelled
v2.2.12
2026-04-28 02:10:43 -04:00
黄黄汪 a1a47ab9c5 fix: context percentage labels reflect used/remaining state (#311)
Previously the Context % and Context % (usable) widgets always
rendered the static label "Ctx: " / "Ctx(u): ", so a user looking
at "Ctx: 9.3%" could not tell whether 9.3% was used or remaining
without opening the editor to check the inverse toggle.

Now the label reflects the current state:
- default (used):  "Ctx Used: X%"   /  "Ctx(u) Used: X%"
- inverse (left):  "Ctx Left: X%"   /  "Ctx(u) Left: X%"

The existing (u) keybind "(u)sed/remaining" and modifier text
"(remaining)" already named these states; this just surfaces the
same distinction in the rendered status line output. Tests updated
to match.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-04-28 02:06:03 -04:00
Matthew Breedlove fb0be05f53 fix(tui): allow more than three powerline caps 2026-04-28 01:53:30 -04:00
François-Pierre Bouchard de374741d0 feat: add time cursor to usage progress bars (#254)
* feat: add time cursor to usage progress bars

The old progress bar showed only how much of the usage limit had been
consumed. The new makeTimerProgressBar adds an optional cursor marker
that shows the elapsed time position within the current usage window.
Users can toggle the cursor on and off with the t keybind when in
progress display mode.

* fix: show time cursor in short usage bars

---------

Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-04-28 01:16:51 -04:00
Matthew Breedlove 1f26e52e80 Version bump and doc update
CI / Lint & Type Check (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Build (push) Has been cancelled
v2.2.11
2026-04-28 00:06:52 -04:00
CJ 3b0a26e3a9 feat: add compaction-counter widget (#282)
* feat: add compaction-counter widget

Tracks context compaction events per session. When Claude Code compacts
the conversation, used_percentage drops; this widget detects the drop
and displays ↻N.

Detection: any drop in used_percentage > 2 points between consecutive
renders counts as one compaction. The threshold filters rounding noise
on both 200K and 1M context windows. State stored per-session in
~/.cache/ccstatusline/compaction/ as JSON.

Related: #92

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

* fix: sanitize session ID in cache path and handle write failures

- Sanitize session ID to prevent path traversal via crafted StatusJSON
- Wrap cache writes in try/catch — failures no longer crash status line
- Add persistence tests: round-trip, path traversal regression, write failure
- Clarify threshold doc (drop must exceed 2 points)

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

* style: align compaction files with project conventions

- Restore EOF newlines (eol-last flipped 'never' → 'always' in 9f779db)
- Use bare module names ('fs', 'os', 'path') with namespace imports
- Use safeParse for cache file parsing
- Refactor test to use a render(options) helper
- Strip @param/@returns from detectCompaction JSDoc
- Collapse trivial single-property object literals to one-line form
- Split guard clause across two lines in CompactionCounter.render

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

* fix(widget): harden compaction-counter against bad input and symlinks

Correctness:
- Pass raw used_percentage to detectCompaction; Math.round was missing
  real drops between rounded points (40.4 → 37.6 is a 2.8 drop but
  rounds to 40 → 38 = 2, failing the > 2 threshold).
- Sentinel prevCtxPct=-1 for fresh state so sessions starting at 0%
  are detected correctly (prior prevCtxPct>0 guard excluded that case).
- Guard detectCompaction against NaN/Infinity/negative input; NaN
  silently poisoned prevCtxPct and disabled detection forever.
- Always load the persisted count so the widget keeps showing the
  historical value when a status update has no used_percentage; only
  run detection+save when a current percentage is present.
- Skip the save when state is unchanged, avoiding a redundant fsync
  per refreshInterval tick.

Security:
- Atomic save via temp file + rename defeats a planted symlink at the
  cache path that would otherwise be written through. Orphan temp
  files are cleaned up if rename fails after write succeeded.
- Open with O_NOFOLLOW + fstat on the open fd: rejects symlinks and
  non-regular files on read, and closes the TOCTOU window that would
  otherwise let the path be swapped between stat and read.
- 4 KiB cache file size cap on read prevents DoS via oversized file.
- Hash session IDs that are empty or contain disallowed characters so
  distinct sessions can't collide in the same cache file.

Tests added for NaN/Infinity/negative input, non-integer drops, 0%
session start, sequential compactions, threshold 0, corrupted JSON,
oversized cache file, symlink rejection on read, atomic save replacing
a planted symlink, session-ID collision, and preview ignoring live data.
Symlink tests are skipped on Windows where fs.symlinkSync requires
elevated privileges.

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

* fix(compaction): harden counter detection and display

fix(compaction): derive usage from shared context percentage metrics when raw used_percentage is missing.

fix(compaction): reset persisted baselines when context window sizes change to avoid false compaction counts.

refactor(context): reuse shared percentage metrics in the context percentage widget.

feat(compaction-counter): add format cycling for icon-space, text label, and number-only displays.

feat(compaction-counter): add Nerd Font icon support only for the icon-space format.

feat(compaction-counter): show zero by default and add an opt-in hide-when-zero toggle.

test(compaction): cover window-size changes, derived percentages, display modes, Nerd Font behavior, and zero visibility.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-04-28 00:01:59 -04:00
Andrey Golanov bd20bbb265 fix: detect effort changes from /effort command in ThinkingEffort widget (#252)
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-04-27 23:18:35 -04:00
Matthew Breedlove 78836d74f3 fix: read thinking effort from status JSON 2026-04-27 23:09:52 -04:00
dependabot[bot] a95059ba2a chore(deps-dev): bump typescript from 5.9.3 to 6.0.2 (#342)
* chore(deps-dev): bump typescript from 5.9.3 to 6.0.2

Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.9.3 to 6.0.2.
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.9.3...v6.0.2)

---
updated-dependencies:
- dependency-name: typescript
  dependency-version: 6.0.2
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Include Bun types for TypeScript 6

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-04-27 22:55:26 -04:00
dependabot[bot] cccf448518 chore(deps-dev): bump the dev-dependencies group with 3 updates (#341)
Bumps the dev-dependencies group with 3 updates: [@types/bun](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/bun), [eslint-plugin-react-hooks](https://github.com/facebook/react/tree/HEAD/packages/eslint-plugin-react-hooks) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest).


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

Updates `eslint-plugin-react-hooks` from 7.0.1 to 7.1.1
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/eslint-plugin-react-hooks@7.1.1/packages/eslint-plugin-react-hooks)

Updates `vitest` from 4.1.4 to 4.1.5
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.5/packages/vitest)

---
updated-dependencies:
- dependency-name: "@types/bun"
  dependency-version: 1.3.13
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: eslint-plugin-react-hooks
  dependency-version: 7.1.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-dependencies
- dependency-name: vitest
  dependency-version: 4.1.5
  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-04-27 22:44:23 -04:00
CJ 89a0a78467 fix(renderer): collapse separators around empty widgets (#344)
In layouts with explicit `{type: "separator"}` items, when a widget
between two separators renders empty, both surrounding separators
still emit — producing "A |  | B" instead of "A | B". The walkback
in renderStatusLine that gates each separator was looking for ANY
prior widget with content, not the immediately-prior one. That's
correct for hiding a leading separator (nothing before it) but
wrong when the immediate-prior widget is empty in the middle of a
line.

Fix: stop the walkback at the first non-separator widget and emit
only when that widget actually rendered content. The leading-edge
case still hides correctly (no prior widget → no orphan), and a
merge:'no-padding' chain across a collapsed separator now reaches
the next visible widget as expected. Affects any hide-capable
widget (git-changes with hideNoGit, etc.). The auto-separator
(defaultSeparator) path already filters empty widgets out of its
element chain and isn't touched.

New tests cover:
- empty widget between two separators (the canonical case)
- consecutive empty widgets between content widgets
- leading empty widget (regression — already worked, locks behavior)
- merge:'no-padding' interaction across a collapsed separator
- defaultSeparator path independence (the fix doesn't couple to it)
- all-content happy path (regression guard)

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-27 22:43:45 -04:00
Matthew Breedlove 48bc4208fb Version bump and doc update
CI / Lint & Type Check (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Build (push) Has been cancelled
v2.2.10
2026-04-27 02:03:30 -04:00
isseeeeey55 b4c19ec46f feat: support local/IANA timezone and locale selection in reset timers timestamp mode (#340)
* feat: support local/IANA timezone in reset timer date mode

Closes #337.

Adds an optional 'timezone' metadata key to BlockResetTimer and
WeeklyResetTimer. When provided, the reset timestamp (already exposed
in date mode via #220) is rendered through Intl.DateTimeFormat in the
specified timezone instead of UTC.

## Behavior

- 'timezone' unset or 'UTC': existing UTC behavior is preserved
- 'timezone' = 'local': uses the system timezone
- 'timezone' = an IANA name (e.g. 'Asia/Tokyo'): explicit override
- Invalid timezone names fall back to UTC

## Output examples

| timezone     | output (compact=false)        | output (compact=true) |
|--------------|-------------------------------|-----------------------|
| (none) / UTC | '2026-03-12 08:30 UTC'        | '03-12 08:30Z'        |
| Asia/Tokyo   | '2026-03-12 17:30 GMT+9'      | '03-12 17:30'         |
| local        | '2026-03-12 17:30 <tz>'       | '03-12 17:30'         |

## Implementation

- 'formatUsageResetAt' grows an optional 'timezone' parameter
- When set to a non-UTC value, uses 'Intl.DateTimeFormat' with
  'timeZone' option to render parts, plus 'timeZoneName: short' for
  the trailing label
- Widget render passes 'metadata.timezone' through via a new
  'getUsageTimezone(item)' helper
- Falls back to UTC on Intl errors so misconfiguration never blanks
  the widget

## Tests

- new cases in 'src/utils/__tests__/usage.test.ts' covering specific
  IANA TZ, local TZ, UTC backwards compat, and invalid TZ fallback

* feat: support 'locale' metadata for reset timer date mode

Adds an optional 'locale' metadata key alongside 'timezone' so users
can pick a locale that returns a friendlier short timezone label.

Background: 'Intl.DateTimeFormat' with the default 'en-CA' locale
returns 'GMT+9' for Asia/Tokyo. Switching to 'ja-JP' yields 'JST'.
Same for many other zones — 'en-US' tends to give EST/EDT etc.

## Behavior

- 'locale' omitted: uses 'en-CA' (current behavior, no change)
- 'locale' = an IANA / BCP 47 tag: passed to 'Intl.DateTimeFormat'
- If the supplied locale produces no usable output (rare), falls
  back to the default 'en-CA' formatter, then UTC

The numeric date parts come from 'formatToParts', so even with
'ja-JP' the output is still '2026-04-27 18:40 JST' — only the
trailing zone label changes.

## Sample config

{
  "type": "weekly-reset-timer",
  "metadata": {
    "absolute": "true",
    "timezone": "Asia/Tokyo",
    "locale": "ja-JP"
  }
}

## Tests

Added cases in src/utils/__tests__/usage.test.ts for ja-JP yielding
JST, en-CA yielding GMT+9 for the same instant, compact mode behavior,
and lenient locale handling.

* feat(widgets): add reset timer timezone controls

Add reusable timezone picker support for block and weekly reset timers.

Add a timestamp-mode 12/24hr toggle that defaults to 24hr.

* feat(widgets): add reset timer locale controls

---------

Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-04-27 01:57:54 -04:00
tejas 9c8b6c2781 feat: add git file status and enhanced git widgets (#208)
* feat: add 7 git status widgets (staged, unstaged, untracked, ahead/behind, conflicts, clean status, SHA)

Add granular git repository status widgets addressing issue #20:
- GitStagedFiles: staged file count (S:N format)
- GitUnstagedFiles: unstaged file count (M:N format)
- GitUntrackedFiles: untracked file count (A:N format)
- GitAheadBehind: upstream ahead/behind tracking (↑N ↓M format)
- GitMergeConflicts: merge conflict count (⚠N, hidden when 0)
- GitCleanStatus: clean/dirty indicator (✓/✗)
- GitSha: short commit SHA

All widgets follow existing patterns: Widget interface, git-no-git shared
helpers, hideNoGit support, rawValue support, and cross-platform git
commands via runGit(). Includes shared getGitFileStatusCounts() utility
for the file count widgets.

21 new tests across 7 test files + shared behavior test entries.

Closes #20

* fix: handle unstaged rename source paths

---------

Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-04-27 01:06:15 -04:00
myprogs c2a77c38d3 feat(widgets): add Context Window widget (#335)
Adds a new "context-window" widget that displays the total context
window size for the current model (e.g. 1.0M for Opus 1M, 200k for
Sonnet/Haiku). Resolves the size from runtime context_window_size,
falling back to per-model config when absent — same logic as
ContextBarWidget. Useful for users who want "used/total (pct)"
output without the progress bar from context-bar.

Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-04-27 00:39:22 -04:00
Edison 316d727c0f feat: support absolute datetime for reset timers (#220)
* feat: add date mode for block and weekly reset timers

* fix(tui): avoid reset timer timestamp keybind conflict

Use t for timestamp mode so d remains the editor delete shortcut, and hide the weekly hours-only toggle while timestamp mode is active.

---------

Co-authored-by: Fan Bot <clawbot@Fandexuniji.local>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-04-27 00:31:29 -04:00
Vsevolod Kosarev 17938a98fc feat(widgets): add short bar display mode to percentage widgets (#333)
Add a compact progress bar (▓░, 10 chars, no brackets) as a new display
mode for all percentage-based widgets: SessionUsage, WeeklyUsage,
ContextPercentage, ContextPercentageUsable, and ContextBar. The mode
cycles via the existing 'p' keybind alongside the original progress bar
variants.

Renamed bar size labels for consistency: long bar (32 chars),
medium bar (16 chars), short bar (10 chars), short bar only (10 chars
without percentage text).

Added 11 unit tests covering makeSliderBar rendering, display mode
cycling with and without slider, and Context % slider render output.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
2026-04-27 00:04:15 -04:00
Matthew Breedlove eb1f0d17f0 Version bump and doc update
CI / Lint & Type Check (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Build (push) Has been cancelled
v2.2.9
2026-04-26 23:48:45 -04:00
CJ 6f875631da fix(widget): claude-account-email respects CLAUDE_CONFIG_DIR (#339)
* 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>
2026-04-26 22:53:21 -04:00
Thomas BENOIT cef29e128a feat(tui): add k shortcut to clone selected widget in items editor (#328)
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>
2026-04-24 00:10:19 -04:00
Justin Mecham afbbf66b3a feat: support GitLab in Git Branch and Git PR widgets (#330)
* 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>
2026-04-23 23:59:41 -04:00
Vsevolod Kosarev c5a209b687 feat(tui): add wrap-around navigation to all menus and move modes (#332)
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>
2026-04-23 23:32:47 -04:00
Matthew Breedlove 5a08aeed69 Merge branch 'dependabot/bun/dev-dependencies-33a2694a2c' 2026-04-20 22:41:44 -04:00
Matthew Breedlove 9f779db141 chore(lint): Change annoying eol-last: never lint rule to always 2026-04-20 22:40:19 -04:00
Matthew Breedlove 933d1f7852 chore(ci): Fix failing tests 2026-04-20 22:37:39 -04:00
dependabot[bot] eef365945d chore(deps-dev): bump the dev-dependencies group across 1 directory with 2 updates
Bumps the dev-dependencies group with 2 updates in the / directory: [eslint](https://github.com/eslint/eslint) and [globals](https://github.com/sindresorhus/globals).


Updates `eslint` from 10.2.0 to 10.2.1
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v10.2.0...v10.2.1)

Updates `globals` from 17.4.0 to 17.5.0
- [Release notes](https://github.com/sindresorhus/globals/releases)
- [Commits](https://github.com/sindresorhus/globals/compare/v17.4.0...v17.5.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 10.2.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: globals
  dependency-version: 17.5.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-21 02:22:31 +00:00
Matthew Breedlove 8996d8aaf6 chore: Update eslint-plugin-import to eslint-plugin-import-x for ESLint 10 compatibility 2026-04-20 22:21:00 -04:00
Matthew Breedlove 1ecc240ce8 fix(git): harden git helper layer 2026-04-20 22:08:17 -04:00
Jaakko 298c6716e3 feat: add refreshInterval configuration for Claude Code status line (#297)
* 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>
2026-04-17 09:53:59 -04:00
dependabot[bot] efc7e8a06c chore(deps-dev): bump the dev-dependencies group across 1 directory with 3 updates (#316)
Bumps the dev-dependencies group with 3 updates in the / directory: [@types/bun](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/bun), [typedoc](https://github.com/TypeStrong/TypeDoc) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


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

Updates `typedoc` from 0.28.18 to 0.28.19
- [Release notes](https://github.com/TypeStrong/TypeDoc/releases)
- [Changelog](https://github.com/TypeStrong/typedoc/blob/master/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/TypeDoc/compare/v0.28.18...v0.28.19)

Updates `typescript-eslint` from 8.58.0 to 8.58.2
- [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.58.2/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: "@types/bun"
  dependency-version: 1.3.12
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: typedoc
  dependency-version: 0.28.19
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: typescript-eslint
  dependency-version: 8.58.2
  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-04-17 09:21:32 -04:00
dependabot[bot] 49e5511f4a chore(deps-dev): bump the dev-dependencies group with 6 updates (#301)
Bumps the dev-dependencies group with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [eslint](https://github.com/eslint/eslint) | `10.1.0` | `10.2.0` |
| [globals](https://github.com/sindresorhus/globals) | `17.3.0` | `17.4.0` |
| [react](https://github.com/facebook/react/tree/HEAD/packages/react) | `19.2.4` | `19.2.5` |
| [tinyglobby](https://github.com/SuperchupuDev/tinyglobby) | `0.2.15` | `0.2.16` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.57.2` | `8.58.0` |
| [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `4.1.2` | `4.1.4` |


Updates `eslint` from 10.1.0 to 10.2.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v10.1.0...v10.2.0)

Updates `globals` from 17.3.0 to 17.4.0
- [Release notes](https://github.com/sindresorhus/globals/releases)
- [Commits](https://github.com/sindresorhus/globals/compare/v17.3.0...v17.4.0)

Updates `react` from 19.2.4 to 19.2.5
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.2.5/packages/react)

Updates `tinyglobby` from 0.2.15 to 0.2.16
- [Release notes](https://github.com/SuperchupuDev/tinyglobby/releases)
- [Changelog](https://github.com/SuperchupuDev/tinyglobby/blob/main/CHANGELOG.md)
- [Commits](https://github.com/SuperchupuDev/tinyglobby/compare/0.2.15...0.2.16)

Updates `typescript-eslint` from 8.57.2 to 8.58.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.58.0/packages/typescript-eslint)

Updates `vitest` from 4.1.2 to 4.1.4
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.4/packages/vitest)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 10.2.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-dependencies
- dependency-name: globals
  dependency-version: 17.4.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-dependencies
- dependency-name: react
  dependency-version: 19.2.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: tinyglobby
  dependency-version: 0.2.16
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: typescript-eslint
  dependency-version: 8.58.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-dependencies
- dependency-name: vitest
  dependency-version: 4.1.4
  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-04-17 09:18:22 -04:00
dependabot[bot] ad91b190b1 chore(deps-dev): bump https-proxy-agent from 7.0.6 to 8.0.0 (#302)
Bumps [https-proxy-agent](https://github.com/TooTallNate/proxy-agents/tree/HEAD/packages/https-proxy-agent) from 7.0.6 to 8.0.0.
- [Release notes](https://github.com/TooTallNate/proxy-agents/releases)
- [Changelog](https://github.com/TooTallNate/proxy-agents/blob/main/packages/https-proxy-agent/CHANGELOG.md)
- [Commits](https://github.com/TooTallNate/proxy-agents/commits/https-proxy-agent@8.0.0/packages/https-proxy-agent)

---
updated-dependencies:
- dependency-name: https-proxy-agent
  dependency-version: 8.0.0
  dependency-type: direct:development
  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-04-17 09:17:58 -04:00
Spencer Smith 5e80b5834f feat: support xhigh thinking effort level (#314)
- 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>
2026-04-17 09:01:23 -04:00
hgreene624 ccc180301d fix: Fix token overcounting from streaming duplicate JSONL entries (#278)
* 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>
2026-04-08 13:50:43 -04:00
Chris 67151c61d4 fix: strip parenthetical suffix from model display name (#283)
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>
2026-04-08 13:37:46 -04:00
Matthew Breedlove 497b30e0bf Version bump, README cleanup
CI / Lint & Type Check (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Build (push) Has been cancelled
v2.2.8
2026-04-08 13:21:35 -04:00
Matthew Breedlove ad51a8762c fix: walk process tree for terminal width detection
Closes #262
2026-04-08 03:36:46 -04:00
hangie 70d1d5261f feat: add git status, git remote, worktree, and custom symbol widgets (#257)
* 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>
2026-04-08 03:20:42 -04:00
Matthew Breedlove dfe05312bd refactor(cache): move PR cache files to pr subdirectory 2026-04-08 00:28:37 -04:00
Forbes Avila e87ce2e815 feat(widgets): add Claude account email widget (#295)
* 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>
2026-04-08 00:19:44 -04:00
Matthew Breedlove 00ee862200 feat(powerline): continue theme colors across lines
Closes #288
2026-04-07 23:55:14 -04:00