ccstatusline 2.2.24 โ†’ 2.2.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -47,6 +47,14 @@
47
47
 
48
48
  ## ๐Ÿ†• Recent Updates
49
49
 
50
+ ### v2.2.25 - v2.2.26 - Fable usage, migrated usage API support, compaction accuracy, and rendering reliability
51
+
52
+ - **๐Ÿช„ Weekly Fable usage** - Added a `Weekly Fable Usage` widget with percentage, progress-bar, remaining-mode, and time-cursor controls.
53
+ - **๐Ÿ“Š Reshaped usage API support** - Session and all-model weekly usage can fall back to the newer `limits[]` response, while Sonnet, Opus, and Fable weekly widgets read authoritative `weekly_scoped` entries so migrated accounts do not show stale or frozen values.
54
+ - **๐Ÿง  Correct post-compaction context** - Context length and percentage widgets reset immediately from `compact_boundary.postTokens` after `/compact`, then switch to the first new turn instead of retaining the pre-compaction size.
55
+ - **๐Ÿงน Reliable hidden-widget separators** - Manual separators now look past widgets that render empty, preserve the intended visible boundary, and inherit colors from the actual preceding visible widget.
56
+ - **โšก Non-blocking Git PR/CI refreshes** - Git PR and CI widgets render from a versioned disk cache while stale data refreshes in the background, preventing slow `gh` calls from blocking the status line.
57
+
50
58
  ### v2.2.22 - v2.2.24 - Powerline flex mode, cache/CI/sandbox visibility, layout controls, composable metrics, and safer config
51
59
 
52
60
  ![Powerline Flex Mode](https://raw.githubusercontent.com/sirmalloc/ccstatusline/main/screenshots/powerline-flex.png)
@@ -258,7 +266,7 @@
258
266
 
259
267
  ## โœจ Features
260
268
 
261
- - **๐Ÿ“Š Real-time Metrics** - Display model name, git branch, token usage, per-model weekly usage, extra usage limits, voice input state, session duration, compaction count, block timer, and more
269
+ - **๐Ÿ“Š Real-time Metrics** - Display model name, git branch, token usage, Sonnet/Opus/Fable weekly usage, extra usage limits, voice input state, session duration, compaction count, block timer, and more
262
270
  - **๐ŸŽจ Fully Customizable** - Choose what to display and customize colors for each element
263
271
  - **โšก Powerline Support** - Beautiful Powerline-style rendering with arrow separators, caps, and custom fonts
264
272
  - **๐Ÿ“ Multi-line Support** - Configure multiple independent status lines
@@ -55594,12 +55594,18 @@ var init_GitUntrackedFiles = __esm(() => {
55594
55594
  });
55595
55595
 
55596
55596
  // src/utils/git-review-cache.ts
55597
- import { execFileSync as execFileSync2 } from "child_process";
55598
55597
  import {
55598
+ execFileSync as execFileSync2,
55599
+ spawn
55600
+ } from "child_process";
55601
+ import {
55602
+ closeSync,
55599
55603
  existsSync as existsSync2,
55600
55604
  mkdirSync as mkdirSync2,
55605
+ openSync,
55601
55606
  readFileSync as readFileSync3,
55602
55607
  statSync as statSync2,
55608
+ unlinkSync,
55603
55609
  writeFileSync as writeFileSync2
55604
55610
  } from "fs";
55605
55611
  import { createHash as createHash2 } from "node:crypto";
@@ -55689,35 +55695,66 @@ function getCachePath2(cwd2, ref, deps) {
55689
55695
  const hash2 = createHash2("sha256").update(cwd2).update("\x00").update(ref).digest("hex").slice(0, 16);
55690
55696
  return path2.join(getGitReviewCacheDir(deps), `git-review-${hash2}.json`);
55691
55697
  }
55698
+ function isGitReviewData(value) {
55699
+ if (typeof value !== "object" || value === null) {
55700
+ return false;
55701
+ }
55702
+ const candidate = value;
55703
+ return typeof candidate.number === "number" && typeof candidate.url === "string";
55704
+ }
55705
+ function decodeCache(content) {
55706
+ if (content.length === 0) {
55707
+ return { data: null, checksQueried: true };
55708
+ }
55709
+ const parsed = JSON.parse(content);
55710
+ if (typeof parsed === "object" && parsed !== null) {
55711
+ const stored = parsed;
55712
+ if (stored.version === 1 && typeof stored.checksQueried === "boolean" && (stored.data === null || isGitReviewData(stored.data))) {
55713
+ return {
55714
+ data: stored.data,
55715
+ checksQueried: stored.data === null || stored.checksQueried
55716
+ };
55717
+ }
55718
+ }
55719
+ if (isGitReviewData(parsed)) {
55720
+ return {
55721
+ data: parsed,
55722
+ checksQueried: parsed.checks !== undefined
55723
+ };
55724
+ }
55725
+ return "miss";
55726
+ }
55692
55727
  function readCache(cachePath, deps) {
55693
55728
  try {
55694
55729
  if (!deps.existsSync(cachePath)) {
55695
55730
  return "miss";
55696
55731
  }
55697
55732
  const age = deps.now() - deps.statSync(cachePath).mtimeMs;
55698
- if (age > GIT_REVIEW_CACHE_TTL) {
55699
- return "miss";
55700
- }
55701
55733
  const content = deps.readFileSync(cachePath, "utf-8").trim();
55702
- if (content.length === 0) {
55703
- return null;
55704
- }
55705
- const data = JSON.parse(content);
55706
- if (typeof data.number !== "number" || typeof data.url !== "string") {
55734
+ const decoded = decodeCache(content);
55735
+ if (decoded === "miss") {
55707
55736
  return "miss";
55708
55737
  }
55709
- return data;
55738
+ return {
55739
+ ...decoded,
55740
+ stale: age > GIT_REVIEW_CACHE_TTL
55741
+ };
55710
55742
  } catch {
55711
55743
  return "miss";
55712
55744
  }
55713
55745
  }
55714
- function writeCache(cachePath, data, deps) {
55746
+ function writeCache(cachePath, data, checksQueried, deps) {
55715
55747
  try {
55716
55748
  const cacheDir = getGitReviewCacheDir(deps);
55717
55749
  if (!deps.existsSync(cacheDir)) {
55718
55750
  deps.mkdirSync(cacheDir, { recursive: true });
55719
55751
  }
55720
- deps.writeFileSync(cachePath, data ? JSON.stringify(data) : "", "utf-8");
55752
+ const stored = {
55753
+ version: 1,
55754
+ data,
55755
+ checksQueried: data === null || checksQueried
55756
+ };
55757
+ deps.writeFileSync(cachePath, JSON.stringify(stored), "utf-8");
55721
55758
  } catch {}
55722
55759
  }
55723
55760
  function getOriginUrl(cwd2, deps) {
@@ -55798,11 +55835,18 @@ function getProviderCandidates(cwd2, deps) {
55798
55835
  }
55799
55836
  return authed;
55800
55837
  }
55801
- function isCliAvailable(cli, deps) {
55838
+ function getRemainingTimeout(deadline, deps) {
55839
+ const remaining = deadline - deps.now();
55840
+ if (remaining <= 0) {
55841
+ throw new GitReviewDeadlineError("Git review lookup deadline exceeded");
55842
+ }
55843
+ return Math.max(1, Math.min(CLI_TIMEOUT, remaining));
55844
+ }
55845
+ function isCliAvailable(cli, deadline, deps) {
55802
55846
  try {
55803
55847
  deps.execFileSync(cli, ["--version"], {
55804
55848
  stdio: ["pipe", "pipe", "ignore"],
55805
- timeout: CLI_TIMEOUT,
55849
+ timeout: getRemainingTimeout(deadline, deps),
55806
55850
  windowsHide: true
55807
55851
  });
55808
55852
  return true;
@@ -55833,12 +55877,25 @@ function mapGlabState(state) {
55833
55877
  return "LOCKED";
55834
55878
  return state.toUpperCase();
55835
55879
  }
55836
- function queryGhPr(cwd2, args, fields, deps) {
55880
+ function errorText(error51) {
55881
+ if (!(error51 instanceof Error)) {
55882
+ return "";
55883
+ }
55884
+ const stderr = "stderr" in error51 ? error51.stderr : undefined;
55885
+ const stderrText = Buffer.isBuffer(stderr) ? stderr.toString("utf8") : stderr ?? "";
55886
+ return `${error51.message}
55887
+ ${stderrText}`.toLowerCase();
55888
+ }
55889
+ function isCiFieldUnavailableError(error51) {
55890
+ const text = errorText(error51);
55891
+ return text.includes("statuscheckrollup") || text.includes("resource not accessible by integration");
55892
+ }
55893
+ function queryGhPr(cwd2, args, fields, deadline, deps) {
55837
55894
  const output = deps.execFileSync("gh", [...args, "--json", fields], {
55838
55895
  encoding: "utf8",
55839
- stdio: ["pipe", "pipe", "ignore"],
55896
+ stdio: ["pipe", "pipe", "pipe"],
55840
55897
  cwd: cwd2,
55841
- timeout: CLI_TIMEOUT,
55898
+ timeout: getRemainingTimeout(deadline, deps),
55842
55899
  windowsHide: true
55843
55900
  }).trim();
55844
55901
  if (output.length === 0) {
@@ -55846,7 +55903,7 @@ function queryGhPr(cwd2, args, fields, deps) {
55846
55903
  }
55847
55904
  return JSON.parse(output);
55848
55905
  }
55849
- function fetchFromGh(cwd2, repoRef, deps) {
55906
+ function fetchFromGh(cwd2, repoRef, includeChecks, deadline, deps) {
55850
55907
  const args = ["pr", "view"];
55851
55908
  if (repoRef) {
55852
55909
  const branch = getCurrentBranch(cwd2, deps);
@@ -55856,10 +55913,17 @@ function fetchFromGh(cwd2, repoRef, deps) {
55856
55913
  args.push(branch, "--repo", repoRef);
55857
55914
  }
55858
55915
  let parsed;
55859
- try {
55860
- parsed = queryGhPr(cwd2, args, GH_PR_WITH_CHECKS_FIELDS, deps);
55861
- } catch {
55862
- parsed = queryGhPr(cwd2, args, GH_PR_METADATA_FIELDS, deps);
55916
+ if (includeChecks) {
55917
+ try {
55918
+ parsed = queryGhPr(cwd2, args, GH_PR_WITH_CHECKS_FIELDS, deadline, deps);
55919
+ } catch (error51) {
55920
+ if (!isCiFieldUnavailableError(error51)) {
55921
+ throw error51;
55922
+ }
55923
+ parsed = queryGhPr(cwd2, args, GH_PR_METADATA_FIELDS, deadline, deps);
55924
+ }
55925
+ } else {
55926
+ parsed = queryGhPr(cwd2, args, GH_PR_METADATA_FIELDS, deadline, deps);
55863
55927
  }
55864
55928
  if (!parsed) {
55865
55929
  return null;
@@ -55877,7 +55941,7 @@ function fetchFromGh(cwd2, repoRef, deps) {
55877
55941
  checks: computeCiRollup(parsed.statusCheckRollup) ?? undefined
55878
55942
  };
55879
55943
  }
55880
- function fetchFromGlab(cwd2, repoRef, deps) {
55944
+ function fetchFromGlab(cwd2, repoRef, deadline, deps) {
55881
55945
  const args = ["mr", "view"];
55882
55946
  if (repoRef) {
55883
55947
  const branch = getCurrentBranch(cwd2, deps);
@@ -55891,7 +55955,7 @@ function fetchFromGlab(cwd2, repoRef, deps) {
55891
55955
  encoding: "utf8",
55892
55956
  stdio: ["pipe", "pipe", "ignore"],
55893
55957
  cwd: cwd2,
55894
- timeout: CLI_TIMEOUT,
55958
+ timeout: getRemainingTimeout(deadline, deps),
55895
55959
  windowsHide: true
55896
55960
  }).trim();
55897
55961
  if (output.length === 0) {
@@ -55910,41 +55974,129 @@ function fetchFromGlab(cwd2, repoRef, deps) {
55910
55974
  provider: "glab"
55911
55975
  };
55912
55976
  }
55913
- function fetchFromProvider(provider, cwd2, repoRef, deps) {
55914
- const fetchFn = provider === "gh" ? fetchFromGh : fetchFromGlab;
55977
+ function fetchFromProvider(provider, cwd2, repoRef, includeChecks, deadline, deps) {
55978
+ const fetch2 = (targetRepoRef) => provider === "gh" ? fetchFromGh(cwd2, targetRepoRef, includeChecks, deadline, deps) : fetchFromGlab(cwd2, targetRepoRef, deadline, deps);
55915
55979
  try {
55916
- const unpinned = fetchFn(cwd2, null, deps);
55980
+ const unpinned = fetch2(null);
55917
55981
  if (unpinned) {
55918
55982
  return unpinned;
55919
55983
  }
55920
55984
  } catch {}
55921
55985
  if (repoRef) {
55922
- return fetchFn(cwd2, repoRef, deps);
55986
+ return fetch2(repoRef);
55923
55987
  }
55924
55988
  return null;
55925
55989
  }
55926
- function fetchGitReviewData(cwd2, deps = DEFAULT_GIT_REVIEW_CACHE_DEPS) {
55990
+ function fetchGitReviewData(cwd2, deps = DEFAULT_GIT_REVIEW_CACHE_DEPS, options = {}) {
55991
+ const includeChecks = options.includeChecks ?? false;
55927
55992
  const cachePath = getCachePath2(cwd2, getCacheRef(cwd2, deps), deps);
55928
55993
  const cached2 = readCache(cachePath, deps);
55929
- if (cached2 !== "miss") {
55930
- return cached2;
55994
+ if (cached2 !== "miss" && !cached2.stale && (!includeChecks || cached2.checksQueried)) {
55995
+ return cached2.data;
55931
55996
  }
55932
55997
  const repoRef = getOriginRepoRef(cwd2, deps);
55998
+ const deadline = deps.now() + CLI_TIMEOUT;
55933
55999
  for (const provider of getProviderCandidates(cwd2, deps)) {
55934
- if (!isCliAvailable(provider, deps)) {
56000
+ if (!isCliAvailable(provider, deadline, deps)) {
55935
56001
  continue;
55936
56002
  }
55937
56003
  try {
55938
- const data = fetchFromProvider(provider, cwd2, repoRef, deps);
56004
+ const data = fetchFromProvider(provider, cwd2, repoRef, includeChecks, deadline, deps);
55939
56005
  if (data) {
55940
- writeCache(cachePath, data, deps);
56006
+ writeCache(cachePath, data, includeChecks, deps);
55941
56007
  return data;
55942
56008
  }
55943
56009
  } catch {}
55944
56010
  }
55945
- writeCache(cachePath, null, deps);
56011
+ if (cached2 !== "miss" && cached2.data !== null) {
56012
+ return cached2.data;
56013
+ }
56014
+ writeCache(cachePath, null, true, deps);
55946
56015
  return null;
55947
56016
  }
56017
+ function getRefreshLockPath(cachePath) {
56018
+ return `${cachePath}.lock`;
56019
+ }
56020
+ function releaseRefreshLock(lockPath, deps) {
56021
+ try {
56022
+ deps.unlinkSync(lockPath);
56023
+ } catch {}
56024
+ }
56025
+ function createRefreshLock(cachePath, deps) {
56026
+ const cacheDir = getGitReviewCacheDir(deps);
56027
+ try {
56028
+ if (!deps.existsSync(cacheDir)) {
56029
+ deps.mkdirSync(cacheDir, { recursive: true });
56030
+ }
56031
+ } catch {
56032
+ return null;
56033
+ }
56034
+ const lockPath = getRefreshLockPath(cachePath);
56035
+ for (let attempt2 = 0;attempt2 < 2; attempt2++) {
56036
+ try {
56037
+ const descriptor = deps.openSync(lockPath, "wx");
56038
+ deps.closeSync(descriptor);
56039
+ return lockPath;
56040
+ } catch {
56041
+ try {
56042
+ const age = deps.now() - deps.statSync(lockPath).mtimeMs;
56043
+ if (age <= REFRESH_LOCK_STALE_MS) {
56044
+ return null;
56045
+ }
56046
+ deps.unlinkSync(lockPath);
56047
+ } catch {
56048
+ return null;
56049
+ }
56050
+ }
56051
+ }
56052
+ return null;
56053
+ }
56054
+ function scheduleRefresh(cwd2, cachePath, includeChecks, deps) {
56055
+ const scriptPath = deps.getScriptPath();
56056
+ if (!scriptPath) {
56057
+ return;
56058
+ }
56059
+ const lockPath = createRefreshLock(cachePath, deps);
56060
+ if (!lockPath) {
56061
+ return;
56062
+ }
56063
+ try {
56064
+ const child = deps.spawn(deps.getExecPath(), [
56065
+ scriptPath,
56066
+ GIT_REVIEW_REFRESH_FLAG,
56067
+ cwd2,
56068
+ includeChecks ? "checks" : "metadata",
56069
+ lockPath
56070
+ ], {
56071
+ detached: true,
56072
+ stdio: "ignore",
56073
+ windowsHide: true
56074
+ });
56075
+ child.unref();
56076
+ } catch {
56077
+ releaseRefreshLock(lockPath, deps);
56078
+ }
56079
+ }
56080
+ function getCachedGitReviewData(cwd2, options = {}, deps = DEFAULT_GIT_REVIEW_CACHE_DEPS) {
56081
+ const includeChecks = options.includeChecks ?? false;
56082
+ const cachePath = getCachePath2(cwd2, getCacheRef(cwd2, deps), deps);
56083
+ const cached2 = readCache(cachePath, deps);
56084
+ const needsRefresh = cached2 === "miss" || cached2.stale || includeChecks && !cached2.checksQueried;
56085
+ if (needsRefresh) {
56086
+ scheduleRefresh(cwd2, cachePath, includeChecks, deps);
56087
+ }
56088
+ return cached2 === "miss" ? null : cached2.data;
56089
+ }
56090
+ function refreshGitReviewCacheFromCli(cwd2, options, lockPath, deps = DEFAULT_GIT_REVIEW_CACHE_DEPS) {
56091
+ const expectedLockPath = getRefreshLockPath(getCachePath2(cwd2, getCacheRef(cwd2, deps), deps));
56092
+ try {
56093
+ fetchGitReviewData(cwd2, deps, options);
56094
+ } finally {
56095
+ if (lockPath === expectedLockPath) {
56096
+ releaseRefreshLock(lockPath, deps);
56097
+ }
56098
+ }
56099
+ }
55948
56100
  function getGitReviewStatusLabel(state, reviewDecision) {
55949
56101
  if (state === "MERGED")
55950
56102
  return "MERGED";
@@ -55964,20 +56116,28 @@ function truncateTitle(title, maxWidth) {
55964
56116
  return title;
55965
56117
  return `${title.slice(0, limit - 1)}โ€ฆ`;
55966
56118
  }
55967
- var GIT_REVIEW_CACHE_TTL = 30000, CLI_TIMEOUT = 5000, DEFAULT_TITLE_MAX_WIDTH = 30, GH_PR_METADATA_FIELDS = "url,number,title,state,reviewDecision", GH_PR_WITH_CHECKS_FIELDS, DEFAULT_GIT_REVIEW_CACHE_DEPS;
56119
+ var GIT_REVIEW_CACHE_TTL = 30000, CLI_TIMEOUT = 5000, REFRESH_LOCK_STALE_MS = 30000, DEFAULT_TITLE_MAX_WIDTH = 30, GH_PR_METADATA_FIELDS = "url,number,title,state,reviewDecision", GH_PR_WITH_CHECKS_FIELDS, GIT_REVIEW_REFRESH_FLAG = "--internal-refresh-git-review-cache", DEFAULT_GIT_REVIEW_CACHE_DEPS, GitReviewDeadlineError;
55968
56120
  var init_git_review_cache = __esm(() => {
55969
56121
  init_git_remote();
55970
56122
  GH_PR_WITH_CHECKS_FIELDS = `${GH_PR_METADATA_FIELDS},statusCheckRollup`;
55971
56123
  DEFAULT_GIT_REVIEW_CACHE_DEPS = {
56124
+ closeSync,
55972
56125
  execFileSync: execFileSync2,
55973
56126
  existsSync: existsSync2,
56127
+ getExecPath: () => process.execPath,
55974
56128
  mkdirSync: mkdirSync2,
56129
+ openSync,
55975
56130
  readFileSync: readFileSync3,
56131
+ getScriptPath: () => process.argv[1],
56132
+ spawn,
55976
56133
  statSync: statSync2,
56134
+ unlinkSync,
55977
56135
  writeFileSync: writeFileSync2,
55978
56136
  getHomedir: os4.homedir,
55979
56137
  now: Date.now
55980
56138
  };
56139
+ GitReviewDeadlineError = class GitReviewDeadlineError extends Error {
56140
+ };
55981
56141
  });
55982
56142
 
55983
56143
  // src/widgets/GitCiStatus.ts
@@ -56029,7 +56189,7 @@ class GitCiStatusWidget {
56029
56189
  return isHideNoGitEnabled(item) ? null : "(no git)";
56030
56190
  }
56031
56191
  const cwd2 = this.deps.resolveGitCwd(context) ?? this.deps.getProcessCwd();
56032
- const checks3 = this.deps.fetchGitReviewData(cwd2)?.checks;
56192
+ const checks3 = this.deps.getCachedGitReviewData(cwd2, { includeChecks: true })?.checks;
56033
56193
  if (!checks3) {
56034
56194
  return NO_CHECKS;
56035
56195
  }
@@ -56057,7 +56217,7 @@ var init_GitCiStatus = __esm(() => {
56057
56217
  pending: "โ—"
56058
56218
  };
56059
56219
  DEFAULT_DEPS = {
56060
- fetchGitReviewData,
56220
+ getCachedGitReviewData,
56061
56221
  getProcessCwd: () => process.cwd(),
56062
56222
  isInsideGitWorkTree,
56063
56223
  resolveGitCwd
@@ -56334,7 +56494,7 @@ class GitPrWidget {
56334
56494
  return hideNoGit ? null : `(no ${resolvePrNoun(null, context, this.deps)})`;
56335
56495
  }
56336
56496
  const cwd2 = this.deps.resolveGitCwd(context) ?? this.deps.getProcessCwd();
56337
- const prData = this.deps.fetchGitReviewData(cwd2);
56497
+ const prData = this.deps.getCachedGitReviewData(cwd2, { includeChecks: context.gitReviewNeedsChecks ?? false });
56338
56498
  if (!prData) {
56339
56499
  return hideNoGit ? null : `(no ${resolvePrNoun(null, context, this.deps)})`;
56340
56500
  }
@@ -56362,7 +56522,7 @@ var init_GitPr = __esm(() => {
56362
56522
  init_hyperlink();
56363
56523
  init_git_no_git();
56364
56524
  DEFAULT_GIT_PR_WIDGET_DEPS = {
56365
- fetchGitReviewData,
56525
+ getCachedGitReviewData,
56366
56526
  getProcessCwd: () => process.cwd(),
56367
56527
  getRemoteInfo,
56368
56528
  isInsideGitWorkTree,
@@ -58127,7 +58287,7 @@ function getTerminalWidth() {
58127
58287
  function canDetectTerminalWidth() {
58128
58288
  return probeTerminalWidth() !== null;
58129
58289
  }
58130
- var __dirname = "/home/runner/work/ccstatusline/ccstatusline/src/utils", PACKAGE_VERSION = "2.2.24";
58290
+ var __dirname = "/home/runner/work/ccstatusline/ccstatusline/src/utils", PACKAGE_VERSION = "2.2.26";
58131
58291
  var init_terminal = () => {};
58132
58292
 
58133
58293
  // src/utils/format-tokens.ts
@@ -58601,6 +58761,9 @@ function formatSeparator(sep) {
58601
58761
  }
58602
58762
  return sep;
58603
58763
  }
58764
+ function isSpacingSeparator(widget, defaultSeparator) {
58765
+ return widget?.type === "separator" && (widget.character ?? defaultSeparator ?? "|").trim().length === 0;
58766
+ }
58604
58767
  function countPowerlineStartCapSlots(widgets, preRenderedWidgets) {
58605
58768
  let pendingFlexAfterRenderedSegment = false;
58606
58769
  let hasRenderedSegment = false;
@@ -58754,27 +58917,44 @@ function renderStatusLine(widgets, settings, context, preRenderedWidgets, preCal
58754
58917
  if (!widget)
58755
58918
  continue;
58756
58919
  if (widget.type === "separator") {
58757
- let hasContentBefore = false;
58920
+ let contentBeforeIndex = null;
58921
+ let replacesSpacingSeparator = false;
58922
+ let crossedEmptyWidget = false;
58758
58923
  for (let j = i - 1;j >= 0; j--) {
58759
58924
  const prevWidget = widgets[j];
58760
58925
  if (!prevWidget)
58761
58926
  continue;
58762
- if (prevWidget.type === "separator" || prevWidget.type === "flex-separator")
58927
+ if (prevWidget.type === "separator") {
58928
+ if (!isSpacingSeparator(prevWidget, settings.defaultSeparator))
58929
+ break;
58930
+ replacesSpacingSeparator = true;
58763
58931
  continue;
58764
- hasContentBefore = Boolean(preRenderedWidgets[j]?.content);
58765
- break;
58932
+ }
58933
+ if (prevWidget.type === "flex-separator")
58934
+ break;
58935
+ if (preRenderedWidgets[j]?.content) {
58936
+ if (!prevWidget.merge || !crossedEmptyWidget)
58937
+ contentBeforeIndex = j;
58938
+ break;
58939
+ }
58940
+ crossedEmptyWidget = true;
58766
58941
  }
58767
- if (!hasContentBefore)
58942
+ if (contentBeforeIndex === null)
58768
58943
  continue;
58944
+ if (replacesSpacingSeparator) {
58945
+ while (isSpacingSeparator(elements[elements.length - 1]?.widget, settings.defaultSeparator)) {
58946
+ elements.pop();
58947
+ }
58948
+ }
58769
58949
  const sepChar = widget.character ?? (settings.defaultSeparator ?? "|");
58770
58950
  const formattedSep = formatSeparator(sepChar);
58771
58951
  let separatorColor = widget.color ?? "gray";
58772
58952
  let separatorBg = widget.backgroundColor;
58773
58953
  let separatorBold = widget.bold;
58774
58954
  let separatorDim = widget.dim;
58775
- if (settings.inheritSeparatorColors && i > 0 && !widget.color && !widget.backgroundColor) {
58776
- const prevWidget = widgets[i - 1];
58777
- if (prevWidget && prevWidget.type !== "separator" && prevWidget.type !== "flex-separator") {
58955
+ if (settings.inheritSeparatorColors && !widget.color && !widget.backgroundColor) {
58956
+ const prevWidget = widgets[contentBeforeIndex];
58957
+ if (prevWidget) {
58778
58958
  let widgetColor = prevWidget.color;
58779
58959
  if (!widgetColor) {
58780
58960
  const widgetImpl = getWidget(prevWidget.type);
@@ -61720,12 +61900,20 @@ var init_dist5 = __esm(() => {
61720
61900
  });
61721
61901
 
61722
61902
  // src/utils/usage-types.ts
61723
- var FIVE_HOUR_BLOCK_MS, SEVEN_DAY_WINDOW_MS, UsageErrorSchema;
61903
+ function setUsageField(target, field, value) {
61904
+ target[field] = value;
61905
+ }
61906
+ var FIVE_HOUR_BLOCK_MS, SEVEN_DAY_WINDOW_MS, UsageErrorSchema, WEEKLY_MODEL_USAGE_BUCKETS;
61724
61907
  var init_usage_types = __esm(() => {
61725
61908
  init_zod();
61726
61909
  FIVE_HOUR_BLOCK_MS = 5 * 60 * 60 * 1000;
61727
61910
  SEVEN_DAY_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
61728
61911
  UsageErrorSchema = exports_external.enum(["no-credentials", "timeout", "rate-limited", "api-error", "parse-error"]);
61912
+ WEEKLY_MODEL_USAGE_BUCKETS = [
61913
+ { widgetType: "weekly-sonnet-usage", modelDisplayName: "Sonnet", apiBucketKey: "seven_day_sonnet", usageField: "weeklySonnetUsage", resetField: "weeklySonnetResetAt" },
61914
+ { widgetType: "weekly-opus-usage", modelDisplayName: "Opus", apiBucketKey: "seven_day_opus", usageField: "weeklyOpusUsage", resetField: "weeklyOpusResetAt" },
61915
+ { widgetType: "fable-weekly-usage", modelDisplayName: "Fable", usageField: "fableUsage", resetField: "fableResetAt" }
61916
+ ];
61729
61917
  });
61730
61918
 
61731
61919
  // src/utils/usage-fetch.ts
@@ -61738,6 +61926,22 @@ import * as path4 from "path";
61738
61926
  function getUsageApiBucketUtilization(bucket) {
61739
61927
  return bucket === null ? 0 : bucket?.utilization ?? undefined;
61740
61928
  }
61929
+ function findUsageApiLimit(limits, kind) {
61930
+ return limits?.find((limit) => limit.kind === kind);
61931
+ }
61932
+ function isPlaceholderUsageApiLimit(limit) {
61933
+ return (limit.percent ?? 0) === 0 && (limit.resets_at ?? null) === null;
61934
+ }
61935
+ function getUsageApiLimitPercent(limit) {
61936
+ return limit && !isPlaceholderUsageApiLimit(limit) ? limit.percent ?? undefined : undefined;
61937
+ }
61938
+ function getUsageApiLimitResetAt(limit) {
61939
+ return limit && !isPlaceholderUsageApiLimit(limit) ? limit.resets_at ?? undefined : undefined;
61940
+ }
61941
+ function findWeeklyScopedLimit(limits, modelDisplayName) {
61942
+ const normalizedModelName = modelDisplayName.toLowerCase();
61943
+ return limits?.find((limit) => limit.kind === "weekly_scoped" && (limit.scope?.model?.display_name ?? "").toLowerCase().includes(normalizedModelName));
61944
+ }
61741
61945
  function parseJsonWithSchema(rawJson, schema) {
61742
61946
  try {
61743
61947
  const parsed = schema.safeParse(JSON.parse(rawJson));
@@ -61765,6 +61969,8 @@ function parseCachedUsageData(rawJson) {
61765
61969
  weeklySonnetResetAt: parsed.weeklySonnetResetAt ?? undefined,
61766
61970
  weeklyOpusUsage: parsed.weeklyOpusUsage ?? undefined,
61767
61971
  weeklyOpusResetAt: parsed.weeklyOpusResetAt ?? undefined,
61972
+ fableUsage: parsed.fableUsage ?? undefined,
61973
+ fableResetAt: parsed.fableResetAt ?? undefined,
61768
61974
  extraUsageEnabled: parsed.extraUsageEnabled ?? undefined,
61769
61975
  extraUsageLimit: parsed.extraUsageLimit ?? undefined,
61770
61976
  extraUsageUsed: parsed.extraUsageUsed ?? undefined,
@@ -61785,26 +61991,34 @@ function tokenHashMatches(cachedHash, currentHash) {
61785
61991
  }
61786
61992
  return cachedHash === currentHash;
61787
61993
  }
61994
+ function getLegacyUsageApiBucket(parsed, apiBucketKey) {
61995
+ return parsed[apiBucketKey];
61996
+ }
61788
61997
  function parseUsageApiResponse(rawJson) {
61789
61998
  const parsed = parseJsonWithSchema(rawJson, UsageApiResponseSchema);
61790
61999
  if (!parsed) {
61791
62000
  return null;
61792
62001
  }
61793
- return {
61794
- sessionUsage: getUsageApiBucketUtilization(parsed.five_hour),
61795
- sessionResetAt: parsed.five_hour?.resets_at ?? undefined,
61796
- weeklyUsage: getUsageApiBucketUtilization(parsed.seven_day),
61797
- weeklyResetAt: parsed.seven_day?.resets_at ?? undefined,
61798
- weeklySonnetUsage: getUsageApiBucketUtilization(parsed.seven_day_sonnet),
61799
- weeklySonnetResetAt: parsed.seven_day_sonnet?.resets_at ?? undefined,
61800
- weeklyOpusUsage: getUsageApiBucketUtilization(parsed.seven_day_opus),
61801
- weeklyOpusResetAt: parsed.seven_day_opus?.resets_at ?? undefined,
62002
+ const sessionLimit = findUsageApiLimit(parsed.limits, "session");
62003
+ const weeklyLimit = findUsageApiLimit(parsed.limits, "weekly_all");
62004
+ const result2 = {
62005
+ sessionUsage: getUsageApiBucketUtilization(parsed.five_hour) ?? getUsageApiLimitPercent(sessionLimit),
62006
+ sessionResetAt: parsed.five_hour?.resets_at ?? getUsageApiLimitResetAt(sessionLimit),
62007
+ weeklyUsage: getUsageApiBucketUtilization(parsed.seven_day) ?? getUsageApiLimitPercent(weeklyLimit),
62008
+ weeklyResetAt: parsed.seven_day?.resets_at ?? getUsageApiLimitResetAt(weeklyLimit),
61802
62009
  extraUsageEnabled: parsed.extra_usage?.is_enabled ?? undefined,
61803
62010
  extraUsageLimit: parsed.extra_usage?.monthly_limit ?? undefined,
61804
62011
  extraUsageUsed: parsed.extra_usage?.used_credits ?? undefined,
61805
62012
  extraUsageUtilization: parsed.extra_usage?.utilization ?? undefined,
61806
62013
  extraUsageCurrency: parsed.extra_usage?.currency ?? undefined
61807
62014
  };
62015
+ for (const bucket of WEEKLY_MODEL_USAGE_BUCKETS) {
62016
+ const scopedLimit = findWeeklyScopedLimit(parsed.limits, bucket.modelDisplayName);
62017
+ const legacyBucket = bucket.apiBucketKey ? getLegacyUsageApiBucket(parsed, bucket.apiBucketKey) : undefined;
62018
+ setUsageField(result2, bucket.usageField, getUsageApiLimitPercent(scopedLimit) ?? getUsageApiBucketUtilization(legacyBucket));
62019
+ setUsageField(result2, bucket.resetField, getUsageApiLimitResetAt(scopedLimit) ?? legacyBucket?.resets_at ?? undefined);
62020
+ }
62021
+ return result2;
61808
62022
  }
61809
62023
  function ensureCacheDirExists() {
61810
62024
  if (!fs4.existsSync(CACHE_DIR)) {
@@ -61832,7 +62046,10 @@ function hasRequiredUsageField(data, field) {
61832
62046
  if (windowSentinel !== undefined && data[windowSentinel] !== undefined) {
61833
62047
  return true;
61834
62048
  }
61835
- return data.extraUsageEnabled !== undefined && EXTRA_USAGE_DETAIL_FIELDS.has(field);
62049
+ if (data.extraUsageEnabled !== undefined && EXTRA_USAGE_DETAIL_FIELDS.has(field)) {
62050
+ return true;
62051
+ }
62052
+ return (data.sessionUsage !== undefined || data.weeklyUsage !== undefined) && FABLE_USAGE_FIELDS.has(field);
61836
62053
  }
61837
62054
  function hasRequiredUsageFields(data, requiredFields = []) {
61838
62055
  return requiredFields.every((field) => hasRequiredUsageField(data, field));
@@ -62165,7 +62382,7 @@ async function fetchUsageData(options = {}) {
62165
62382
  return getStaleUsageOrError("parse-error", now2, currentTokenHash, LOCK_MAX_AGE, requiredFields);
62166
62383
  }
62167
62384
  }
62168
- var CACHE_DIR, CACHE_FILE, LOCK_FILE, CACHE_MAX_AGE = 180, LOCK_MAX_AGE = 30, DEFAULT_RATE_LIMIT_BACKOFF = 300, MACOS_USAGE_CREDENTIALS_SERVICE = "Claude Code-credentials", MACOS_SECURITY_DUMP_MAX_BUFFER, EXTRA_USAGE_DETAIL_FIELDS, WINDOW_RESET_FIELD_SENTINELS, UsageCredentialsSchema, UsageLockErrorSchema, UsageLockSchema, CachedUsageDataSchema, CachedTokenHashSchema, UsageApiBucketSchema, UsageApiResponseSchema, cachedUsageData = null, usageCacheTime = 0, usageErrorCacheMaxAge, USAGE_API_HOST = "api.anthropic.com", USAGE_API_PATH = "/api/oauth/usage", USAGE_API_TIMEOUT_MS = 5000;
62385
+ var CACHE_DIR, CACHE_FILE, LOCK_FILE, CACHE_MAX_AGE = 180, LOCK_MAX_AGE = 30, DEFAULT_RATE_LIMIT_BACKOFF = 300, MACOS_USAGE_CREDENTIALS_SERVICE = "Claude Code-credentials", MACOS_SECURITY_DUMP_MAX_BUFFER, EXTRA_USAGE_DETAIL_FIELDS, FABLE_USAGE_FIELDS, WINDOW_RESET_FIELD_SENTINELS, UsageCredentialsSchema, UsageLockErrorSchema, UsageLockSchema, CachedUsageDataSchema, CachedTokenHashSchema, UsageApiBucketSchema, UsageApiLimitSchema, UsageApiResponseSchema, cachedUsageData = null, usageCacheTime = 0, usageErrorCacheMaxAge, USAGE_API_HOST = "api.anthropic.com", USAGE_API_PATH = "/api/oauth/usage", USAGE_API_TIMEOUT_MS = 5000;
62169
62386
  var init_usage_fetch = __esm(async () => {
62170
62387
  init_dist5();
62171
62388
  init_zod();
@@ -62180,11 +62397,14 @@ var init_usage_fetch = __esm(async () => {
62180
62397
  "extraUsageUsed",
62181
62398
  "extraUsageUtilization"
62182
62399
  ]);
62400
+ FABLE_USAGE_FIELDS = new Set([
62401
+ "fableUsage",
62402
+ "fableResetAt"
62403
+ ]);
62183
62404
  WINDOW_RESET_FIELD_SENTINELS = {
62184
62405
  sessionResetAt: "sessionUsage",
62185
62406
  weeklyResetAt: "weeklyUsage",
62186
- weeklySonnetResetAt: "weeklySonnetUsage",
62187
- weeklyOpusResetAt: "weeklyOpusUsage"
62407
+ ...Object.fromEntries(WEEKLY_MODEL_USAGE_BUCKETS.map((bucket) => [bucket.resetField, bucket.usageField]))
62188
62408
  };
62189
62409
  UsageCredentialsSchema = exports_external.object({ claudeAiOauth: exports_external.object({ accessToken: exports_external.string().nullable().optional() }).optional() });
62190
62410
  UsageLockErrorSchema = exports_external.enum(["timeout", "rate-limited", "parse-error"]);
@@ -62201,6 +62421,8 @@ var init_usage_fetch = __esm(async () => {
62201
62421
  weeklySonnetResetAt: exports_external.string().nullable().optional(),
62202
62422
  weeklyOpusUsage: exports_external.number().nullable().optional(),
62203
62423
  weeklyOpusResetAt: exports_external.string().nullable().optional(),
62424
+ fableUsage: exports_external.number().nullable().optional(),
62425
+ fableResetAt: exports_external.string().nullable().optional(),
62204
62426
  extraUsageEnabled: exports_external.boolean().nullable().optional(),
62205
62427
  extraUsageLimit: exports_external.number().nullable().optional(),
62206
62428
  extraUsageUsed: exports_external.number().nullable().optional(),
@@ -62213,11 +62435,18 @@ var init_usage_fetch = __esm(async () => {
62213
62435
  utilization: exports_external.number().nullable().optional(),
62214
62436
  resets_at: exports_external.string().nullable().optional()
62215
62437
  }).nullable().optional();
62438
+ UsageApiLimitSchema = exports_external.looseObject({
62439
+ kind: exports_external.string().nullable().optional(),
62440
+ percent: exports_external.number().nullable().optional(),
62441
+ resets_at: exports_external.string().nullable().optional(),
62442
+ scope: exports_external.looseObject({ model: exports_external.looseObject({ display_name: exports_external.string().nullable().optional() }).nullable().optional() }).nullable().catch(null).optional()
62443
+ });
62216
62444
  UsageApiResponseSchema = exports_external.looseObject({
62217
62445
  five_hour: UsageApiBucketSchema,
62218
62446
  seven_day: UsageApiBucketSchema,
62219
62447
  seven_day_sonnet: UsageApiBucketSchema,
62220
62448
  seven_day_opus: UsageApiBucketSchema,
62449
+ limits: exports_external.array(UsageApiLimitSchema).nullable().optional(),
62221
62450
  extra_usage: exports_external.looseObject({
62222
62451
  is_enabled: exports_external.boolean().nullable().optional(),
62223
62452
  monthly_limit: exports_external.number().nullable().optional(),
@@ -64973,8 +65202,79 @@ var init_jsonl_cache = __esm(async () => {
64973
65202
  existsSync6 = fs7.existsSync;
64974
65203
  });
64975
65204
 
64976
- // src/utils/jsonl-metrics.ts
65205
+ // src/utils/compaction.ts
64977
65206
  import * as fs8 from "fs";
65207
+ function isCompactBoundary(record2) {
65208
+ if (typeof record2 !== "object" || record2 === null) {
65209
+ return false;
65210
+ }
65211
+ const r = record2;
65212
+ return r.type === "system" && r.subtype === "compact_boundary" && r.isSidechain !== true;
65213
+ }
65214
+ function getCompactBoundaryPostTokens(record2) {
65215
+ if (!isCompactBoundary(record2)) {
65216
+ return null;
65217
+ }
65218
+ const meta3 = record2.compactMetadata;
65219
+ const post = typeof meta3 === "object" && meta3 !== null ? meta3.postTokens : undefined;
65220
+ return typeof post === "number" && Number.isFinite(post) ? Math.max(0, post) : null;
65221
+ }
65222
+ function computeCompactionStats(lines) {
65223
+ const stats = {
65224
+ count: 0,
65225
+ byTrigger: { auto: 0, manual: 0, unknown: 0 },
65226
+ tokensReclaimed: 0
65227
+ };
65228
+ for (const line of lines) {
65229
+ const record2 = parseJsonlLine(line);
65230
+ if (!isCompactBoundary(record2)) {
65231
+ continue;
65232
+ }
65233
+ stats.count += 1;
65234
+ const meta3 = record2.compactMetadata;
65235
+ const metaRecord = typeof meta3 === "object" && meta3 !== null ? meta3 : null;
65236
+ const trigger = metaRecord?.trigger;
65237
+ if (trigger === "auto") {
65238
+ stats.byTrigger.auto += 1;
65239
+ } else if (trigger === "manual") {
65240
+ stats.byTrigger.manual += 1;
65241
+ } else {
65242
+ stats.byTrigger.unknown += 1;
65243
+ }
65244
+ const pre = metaRecord?.preTokens;
65245
+ const post = metaRecord?.postTokens;
65246
+ if (typeof pre === "number" && typeof post === "number") {
65247
+ const reclaimed = pre - post;
65248
+ if (Number.isFinite(reclaimed)) {
65249
+ stats.tokensReclaimed += Math.max(0, reclaimed);
65250
+ }
65251
+ }
65252
+ }
65253
+ return stats;
65254
+ }
65255
+ async function getCompactionStats(transcriptPath) {
65256
+ try {
65257
+ if (!fs8.existsSync(transcriptPath)) {
65258
+ return ZERO_COMPACTION_STATS;
65259
+ }
65260
+ const lines = await readJsonlLines(transcriptPath);
65261
+ return computeCompactionStats(lines);
65262
+ } catch {
65263
+ return ZERO_COMPACTION_STATS;
65264
+ }
65265
+ }
65266
+ var ZERO_COMPACTION_STATS;
65267
+ var init_compaction = __esm(() => {
65268
+ init_jsonl_lines();
65269
+ ZERO_COMPACTION_STATS = Object.freeze({
65270
+ count: 0,
65271
+ byTrigger: Object.freeze({ auto: 0, manual: 0, unknown: 0 }),
65272
+ tokensReclaimed: 0
65273
+ });
65274
+ });
65275
+
65276
+ // src/utils/jsonl-metrics.ts
65277
+ import * as fs9 from "fs";
64978
65278
  import path7 from "node:path";
64979
65279
  function collectAgentIds(value, agentIds) {
64980
65280
  if (!value || typeof value !== "object") {
@@ -65007,7 +65307,7 @@ function getReferencedSubagentIds(lines) {
65007
65307
  }
65008
65308
  async function getSessionDuration(transcriptPath) {
65009
65309
  try {
65010
- if (!fs8.existsSync(transcriptPath)) {
65310
+ if (!fs9.existsSync(transcriptPath)) {
65011
65311
  return null;
65012
65312
  }
65013
65313
  const lines = await readJsonlLines(transcriptPath);
@@ -65057,7 +65357,7 @@ async function getSessionDuration(transcriptPath) {
65057
65357
  }
65058
65358
  async function getTokenMetrics(transcriptPath) {
65059
65359
  try {
65060
- if (!fs8.existsSync(transcriptPath)) {
65360
+ if (!fs9.existsSync(transcriptPath)) {
65061
65361
  return { inputTokens: 0, outputTokens: 0, cachedTokens: 0, cacheReadTokens: 0, cacheCreationTokens: 0, totalTokens: 0, contextLength: 0 };
65062
65362
  }
65063
65363
  const lines = await readJsonlLines(transcriptPath);
@@ -65068,22 +65368,30 @@ async function getTokenMetrics(transcriptPath) {
65068
65368
  let contextLength = 0;
65069
65369
  let mostRecentMainChainEntry = null;
65070
65370
  let mostRecentTimestamp = null;
65371
+ let mostRecentPostCompactionEntry = null;
65372
+ let mostRecentPostCompactionTimestamp = null;
65373
+ let lastCompactBoundaryLineIndex = -1;
65374
+ let lastCompactBoundaryPostTokens = null;
65071
65375
  const parsedEntries = [];
65072
65376
  let hasStopReasonField = false;
65073
- for (const line of lines) {
65377
+ for (const [lineIndex, line] of lines.entries()) {
65074
65378
  const data = parseJsonlLine(line);
65379
+ if (isCompactBoundary(data)) {
65380
+ lastCompactBoundaryLineIndex = lineIndex;
65381
+ lastCompactBoundaryPostTokens = getCompactBoundaryPostTokens(data);
65382
+ }
65075
65383
  if (data?.message?.usage) {
65076
- parsedEntries.push(data);
65384
+ parsedEntries.push({ data, lineIndex });
65077
65385
  if (Object.hasOwn(data.message, "stop_reason")) {
65078
65386
  hasStopReasonField = true;
65079
65387
  }
65080
65388
  }
65081
65389
  }
65082
- const entriesToCount = hasStopReasonField ? parsedEntries.filter((data, index) => {
65083
- const stopReason = data.message?.stop_reason;
65390
+ const entriesToCount = hasStopReasonField ? parsedEntries.filter((entry, index) => {
65391
+ const stopReason = entry.data.message?.stop_reason;
65084
65392
  return Boolean(stopReason) || stopReason === null && index === parsedEntries.length - 1;
65085
65393
  }) : parsedEntries;
65086
- for (const data of entriesToCount) {
65394
+ for (const { data, lineIndex } of entriesToCount) {
65087
65395
  const usage = data.message?.usage;
65088
65396
  if (!usage) {
65089
65397
  continue;
@@ -65098,12 +65406,20 @@ async function getTokenMetrics(transcriptPath) {
65098
65406
  mostRecentTimestamp = entryTime;
65099
65407
  mostRecentMainChainEntry = data;
65100
65408
  }
65409
+ if (lineIndex > lastCompactBoundaryLineIndex && (!mostRecentPostCompactionTimestamp || entryTime > mostRecentPostCompactionTimestamp)) {
65410
+ mostRecentPostCompactionTimestamp = entryTime;
65411
+ mostRecentPostCompactionEntry = data;
65412
+ }
65101
65413
  }
65102
65414
  }
65103
- if (mostRecentMainChainEntry?.message?.usage) {
65104
- const usage = mostRecentMainChainEntry.message.usage;
65105
- contextLength = (usage.input_tokens || 0) + (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0);
65106
- }
65415
+ const contextLengthFromEntry = (entry) => {
65416
+ const usage = entry?.message?.usage;
65417
+ if (!usage) {
65418
+ return null;
65419
+ }
65420
+ return (usage.input_tokens || 0) + (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0);
65421
+ };
65422
+ contextLength = lastCompactBoundaryLineIndex >= 0 ? contextLengthFromEntry(mostRecentPostCompactionEntry) ?? lastCompactBoundaryPostTokens ?? 0 : contextLengthFromEntry(mostRecentMainChainEntry) ?? 0;
65107
65423
  const cachedTokens = cacheReadTokens + cacheCreationTokens;
65108
65424
  const totalTokens = inputTokens + outputTokens + cachedTokens;
65109
65425
  return { inputTokens, outputTokens, cachedTokens, cacheReadTokens, cacheCreationTokens, totalTokens, contextLength };
@@ -65282,11 +65598,11 @@ function getSubagentTranscriptPaths(transcriptPath, referencedAgentIds) {
65282
65598
  const seenPaths = new Set;
65283
65599
  const matchedPaths = [];
65284
65600
  for (const subagentsDir of candidateDirs) {
65285
- if (!fs8.existsSync(subagentsDir)) {
65601
+ if (!fs9.existsSync(subagentsDir)) {
65286
65602
  continue;
65287
65603
  }
65288
65604
  try {
65289
- const dirEntries = fs8.readdirSync(subagentsDir, { withFileTypes: true });
65605
+ const dirEntries = fs9.readdirSync(subagentsDir, { withFileTypes: true });
65290
65606
  for (const entry of dirEntries) {
65291
65607
  if (!entry.isFile()) {
65292
65608
  continue;
@@ -65315,7 +65631,7 @@ async function getSpeedMetricsCollection(transcriptPath, options = {}) {
65315
65631
  const normalizedWindows = Array.from(new Set((options.windowSeconds ?? []).map((window2) => normalizeWindowSeconds(window2)).filter((window2) => window2 !== null)));
65316
65632
  const emptyWindowedMetrics = buildEmptyWindowedMetrics(normalizedWindows);
65317
65633
  try {
65318
- if (!fs8.existsSync(transcriptPath)) {
65634
+ if (!fs9.existsSync(transcriptPath)) {
65319
65635
  return {
65320
65636
  sessionAverage: createEmptySpeedMetrics(),
65321
65637
  windowed: emptyWindowedMetrics
@@ -65360,6 +65676,7 @@ async function getSpeedMetricsCollection(transcriptPath, options = {}) {
65360
65676
  }
65361
65677
  }
65362
65678
  var init_jsonl_metrics = __esm(() => {
65679
+ init_compaction();
65363
65680
  init_jsonl_lines();
65364
65681
  });
65365
65682
 
@@ -65498,6 +65815,9 @@ function resolveWeeklySonnetUsageWindow(usageData, nowMs = Date.now()) {
65498
65815
  function resolveWeeklyOpusUsageWindow(usageData, nowMs = Date.now()) {
65499
65816
  return getWeeklyUsageWindowFromResetAt(usageData.weeklyOpusResetAt ?? usageData.weeklyResetAt, nowMs);
65500
65817
  }
65818
+ function resolveFableUsageWindow(usageData, nowMs = Date.now()) {
65819
+ return getWeeklyUsageWindowFromResetAt(usageData.fableResetAt ?? usageData.weeklyResetAt, nowMs);
65820
+ }
65501
65821
  function formatUsageDuration(durationMs, compact2 = false, useDays = true) {
65502
65822
  const clampedMs = Math.max(0, durationMs);
65503
65823
  const totalHours = Math.floor(clampedMs / (1000 * 60 * 60));
@@ -66712,7 +67032,7 @@ var init_JjRevision = __esm(() => {
66712
67032
  });
66713
67033
 
66714
67034
  // src/widgets/ClaudeAccountEmail.ts
66715
- import * as fs9 from "fs";
67035
+ import * as fs10 from "fs";
66716
67036
 
66717
67037
  class ClaudeAccountEmailWidget {
66718
67038
  getDefaultColor() {
@@ -66735,7 +67055,7 @@ class ClaudeAccountEmailWidget {
66735
67055
  return item.rawValue ? "you@example.com" : "Account: you@example.com";
66736
67056
  }
66737
67057
  try {
66738
- const content = fs9.readFileSync(getClaudeJsonPath(), "utf-8");
67058
+ const content = fs10.readFileSync(getClaudeJsonPath(), "utf-8");
66739
67059
  const data = JSON.parse(content);
66740
67060
  const email3 = data.oauthAccount?.emailAddress;
66741
67061
  if (typeof email3 !== "string" || email3.length === 0) {
@@ -67173,7 +67493,7 @@ class FreeMemoryWidget {
67173
67493
  var init_FreeMemory = () => {};
67174
67494
 
67175
67495
  // src/widgets/SessionName.ts
67176
- import * as fs10 from "fs";
67496
+ import * as fs11 from "fs";
67177
67497
 
67178
67498
  class SessionNameWidget {
67179
67499
  getDefaultColor() {
@@ -67200,7 +67520,7 @@ class SessionNameWidget {
67200
67520
  return null;
67201
67521
  }
67202
67522
  try {
67203
- const content = fs10.readFileSync(transcriptPath, "utf-8");
67523
+ const content = fs11.readFileSync(transcriptPath, "utf-8");
67204
67524
  const lines = content.split(`
67205
67525
  `);
67206
67526
  for (let i = lines.length - 1;i >= 0; i--) {
@@ -67879,6 +68199,102 @@ var init_WeeklyOpusUsage = __esm(async () => {
67879
68199
  await init_usage();
67880
68200
  });
67881
68201
 
68202
+ // src/widgets/FableWeeklyUsage.ts
68203
+ class FableWeeklyUsageWidget {
68204
+ getDefaultColor() {
68205
+ return "brightBlue";
68206
+ }
68207
+ getDescription() {
68208
+ return "Shows Fable-only weekly usage percentage";
68209
+ }
68210
+ getDisplayName() {
68211
+ return "Weekly Fable Usage";
68212
+ }
68213
+ getCategory() {
68214
+ return "Usage";
68215
+ }
68216
+ getEditorDisplay(item) {
68217
+ return {
68218
+ displayText: this.getDisplayName(),
68219
+ modifierText: getUsageDisplayModifierText(item, { showUsageDirection: true })
68220
+ };
68221
+ }
68222
+ handleEditorAction(action, item) {
68223
+ if (action === "toggle-progress") {
68224
+ return cycleUsageDisplayMode(item, [], true, true);
68225
+ }
68226
+ if (action === "toggle-invert") {
68227
+ return toggleUsageInverted(item);
68228
+ }
68229
+ if (action === "toggle-cursor") {
68230
+ return toggleUsageCursor(item);
68231
+ }
68232
+ return null;
68233
+ }
68234
+ render(item, context, settings) {
68235
+ const displayMode = getUsageDisplayMode(item);
68236
+ const inverted = isUsageInverted(item);
68237
+ const showCursor = isUsageCursorEnabled(item);
68238
+ if (context.isPreview) {
68239
+ const previewPercent = 4;
68240
+ const renderedPercent2 = inverted ? 100 - previewPercent : previewPercent;
68241
+ if (isUsageProgressMode(displayMode)) {
68242
+ const width = getUsageProgressBarWidth(displayMode);
68243
+ const progressBar = makeTimerProgressBar2(renderedPercent2, width, showCursor ? { cursorPercent: 50 } : undefined);
68244
+ const progressDisplay = `[${progressBar}] ${renderedPercent2.toFixed(1)}%`;
68245
+ return formatRawOrLabeledValue(item, LABEL3, progressDisplay);
68246
+ }
68247
+ if (isUsageSliderMode(displayMode)) {
68248
+ const slider = makeSliderBar(renderedPercent2, undefined, showCursor ? { cursorPercent: 50 } : undefined);
68249
+ const sliderDisplay = displayMode === "slider" ? `${slider} ${renderedPercent2.toFixed(1)}%` : slider;
68250
+ return formatRawOrLabeledValue(item, LABEL3, sliderDisplay);
68251
+ }
68252
+ return formatRawOrLabeledValue(item, LABEL3, `${renderedPercent2.toFixed(1)}%`);
68253
+ }
68254
+ const data = context.usageData ?? {};
68255
+ if (data.fableUsage === undefined) {
68256
+ if (data.error)
68257
+ return getUsageErrorMessage(data.error);
68258
+ return null;
68259
+ }
68260
+ const percent = Math.max(0, Math.min(100, data.fableUsage));
68261
+ const renderedPercent = inverted ? 100 - percent : percent;
68262
+ const getCursorOptions = () => {
68263
+ if (!showCursor) {
68264
+ return;
68265
+ }
68266
+ const window2 = resolveFableUsageWindow(data);
68267
+ return window2 ? { cursorPercent: window2.elapsedPercent } : undefined;
68268
+ };
68269
+ if (isUsageProgressMode(displayMode)) {
68270
+ const width = getUsageProgressBarWidth(displayMode);
68271
+ const progressBar = makeTimerProgressBar2(renderedPercent, width, getCursorOptions());
68272
+ const progressDisplay = `[${progressBar}] ${renderedPercent.toFixed(1)}%`;
68273
+ return formatRawOrLabeledValue(item, LABEL3, progressDisplay);
68274
+ }
68275
+ if (isUsageSliderMode(displayMode)) {
68276
+ const slider = makeSliderBar(renderedPercent, undefined, getCursorOptions());
68277
+ const sliderDisplay = displayMode === "slider" ? `${slider} ${renderedPercent.toFixed(1)}%` : slider;
68278
+ return formatRawOrLabeledValue(item, LABEL3, sliderDisplay);
68279
+ }
68280
+ return formatRawOrLabeledValue(item, LABEL3, `${renderedPercent.toFixed(1)}%`);
68281
+ }
68282
+ getCustomKeybinds(item) {
68283
+ return getUsagePercentCustomKeybinds(item);
68284
+ }
68285
+ supportsRawValue() {
68286
+ return true;
68287
+ }
68288
+ supportsColors(item) {
68289
+ return true;
68290
+ }
68291
+ }
68292
+ var LABEL3 = "Fable Weekly: ";
68293
+ var init_FableWeeklyUsage = __esm(async () => {
68294
+ init_usage_display();
68295
+ await init_usage();
68296
+ });
68297
+
67882
68298
  // src/widgets/shared/locale-editor.tsx
67883
68299
  function getInitialSelectedIndex(options, currentLocale) {
67884
68300
  const selectedValue = currentLocale ? canonicalizeLocale(currentLocale) : DEFAULT_RESET_LOCALE;
@@ -69515,69 +69931,6 @@ class GitWorktreeOriginalBranchWidget {
69515
69931
  }
69516
69932
  }
69517
69933
 
69518
- // src/utils/compaction.ts
69519
- import * as fs11 from "fs";
69520
- function isCompactBoundary(record2) {
69521
- if (typeof record2 !== "object" || record2 === null) {
69522
- return false;
69523
- }
69524
- const r = record2;
69525
- return r.type === "system" && r.subtype === "compact_boundary" && r.isSidechain !== true;
69526
- }
69527
- function computeCompactionStats(lines) {
69528
- const stats = {
69529
- count: 0,
69530
- byTrigger: { auto: 0, manual: 0, unknown: 0 },
69531
- tokensReclaimed: 0
69532
- };
69533
- for (const line of lines) {
69534
- const record2 = parseJsonlLine(line);
69535
- if (!isCompactBoundary(record2)) {
69536
- continue;
69537
- }
69538
- stats.count += 1;
69539
- const meta3 = record2.compactMetadata;
69540
- const metaRecord = typeof meta3 === "object" && meta3 !== null ? meta3 : null;
69541
- const trigger = metaRecord?.trigger;
69542
- if (trigger === "auto") {
69543
- stats.byTrigger.auto += 1;
69544
- } else if (trigger === "manual") {
69545
- stats.byTrigger.manual += 1;
69546
- } else {
69547
- stats.byTrigger.unknown += 1;
69548
- }
69549
- const pre = metaRecord?.preTokens;
69550
- const post = metaRecord?.postTokens;
69551
- if (typeof pre === "number" && typeof post === "number") {
69552
- const reclaimed = pre - post;
69553
- if (Number.isFinite(reclaimed)) {
69554
- stats.tokensReclaimed += Math.max(0, reclaimed);
69555
- }
69556
- }
69557
- }
69558
- return stats;
69559
- }
69560
- async function getCompactionStats(transcriptPath) {
69561
- try {
69562
- if (!fs11.existsSync(transcriptPath)) {
69563
- return ZERO_COMPACTION_STATS;
69564
- }
69565
- const lines = await readJsonlLines(transcriptPath);
69566
- return computeCompactionStats(lines);
69567
- } catch {
69568
- return ZERO_COMPACTION_STATS;
69569
- }
69570
- }
69571
- var ZERO_COMPACTION_STATS;
69572
- var init_compaction = __esm(() => {
69573
- init_jsonl_lines();
69574
- ZERO_COMPACTION_STATS = Object.freeze({
69575
- count: 0,
69576
- byTrigger: Object.freeze({ auto: 0, manual: 0, unknown: 0 }),
69577
- tokensReclaimed: 0
69578
- });
69579
- });
69580
-
69581
69934
  // src/widgets/CompactionCounter.ts
69582
69935
  function getFormat2(item) {
69583
69936
  const format = item.metadata?.format;
@@ -70547,6 +70900,7 @@ var init_widgets = __esm(async () => {
70547
70900
  init_ExtraUsageUsed(),
70548
70901
  init_WeeklySonnetUsage(),
70549
70902
  init_WeeklyOpusUsage(),
70903
+ init_FableWeeklyUsage(),
70550
70904
  init_BlockResetTimer(),
70551
70905
  init_WeeklyResetTimer(),
70552
70906
  init_ContextBar(),
@@ -70639,6 +70993,7 @@ var init_widget_manifest = __esm(async () => {
70639
70993
  { type: "extra-usage-used", create: () => new ExtraUsageUsedWidget },
70640
70994
  { type: "weekly-sonnet-usage", create: () => new WeeklySonnetUsageWidget },
70641
70995
  { type: "weekly-opus-usage", create: () => new WeeklyOpusUsageWidget },
70996
+ { type: "fable-weekly-usage", create: () => new FableWeeklyUsageWidget },
70642
70997
  { type: "reset-timer", create: () => new BlockResetTimerWidget },
70643
70998
  { type: "weekly-reset-timer", create: () => new WeeklyResetTimerWidget },
70644
70999
  { type: "context-bar", create: () => new ContextBarWidget },
@@ -79925,6 +80280,7 @@ var StatusJSONSchema = exports_external.looseObject({
79925
80280
  init_ansi();
79926
80281
  init_colors();
79927
80282
  init_compaction();
80283
+ init_git_review_cache();
79928
80284
  await init_config();
79929
80285
 
79930
80286
  // src/utils/hook-handler.ts
@@ -80020,28 +80376,28 @@ await init_renderer2();
80020
80376
  init_terminal();
80021
80377
 
80022
80378
  // src/utils/usage-prefetch.ts
80379
+ init_usage_types();
80023
80380
  await init_usage();
80024
- var USAGE_WIDGET_TYPES = new Set([
80381
+ var BASE_USAGE_WIDGET_TYPES = [
80025
80382
  "session-usage",
80026
80383
  "weekly-usage",
80027
- "weekly-sonnet-usage",
80028
- "weekly-opus-usage",
80029
80384
  "block-timer",
80030
80385
  "reset-timer",
80031
80386
  "weekly-reset-timer",
80032
80387
  "extra-usage-utilization",
80033
80388
  "extra-usage-remaining",
80034
80389
  "extra-usage-used"
80390
+ ];
80391
+ var USAGE_WIDGET_TYPES = new Set([
80392
+ ...BASE_USAGE_WIDGET_TYPES,
80393
+ ...WEEKLY_MODEL_USAGE_BUCKETS.map((bucket) => bucket.widgetType)
80035
80394
  ]);
80036
80395
  var USAGE_DATA_FIELDS = [
80037
80396
  "sessionUsage",
80038
80397
  "sessionResetAt",
80039
80398
  "weeklyUsage",
80040
80399
  "weeklyResetAt",
80041
- "weeklySonnetUsage",
80042
- "weeklySonnetResetAt",
80043
- "weeklyOpusUsage",
80044
- "weeklyOpusResetAt",
80400
+ ...WEEKLY_MODEL_USAGE_BUCKETS.flatMap((bucket) => [bucket.usageField, bucket.resetField]),
80045
80401
  "extraUsageEnabled",
80046
80402
  "extraUsageLimit",
80047
80403
  "extraUsageUsed",
@@ -80052,8 +80408,7 @@ var EMPTY_USAGE_REQUIREMENTS = [];
80052
80408
  var USAGE_WIDGET_REQUIREMENTS = {
80053
80409
  "session-usage": [{ field: "sessionUsage" }],
80054
80410
  "weekly-usage": [{ field: "weeklyUsage" }],
80055
- "weekly-sonnet-usage": [{ field: "weeklySonnetUsage" }],
80056
- "weekly-opus-usage": [{ field: "weeklyOpusUsage" }],
80411
+ ...Object.fromEntries(WEEKLY_MODEL_USAGE_BUCKETS.map((bucket) => [bucket.widgetType, [{ field: bucket.usageField }]])),
80057
80412
  "block-timer": [{ field: "sessionResetAt", suppressFetchError: true }],
80058
80413
  "reset-timer": [{ field: "sessionResetAt", suppressFetchError: true }],
80059
80414
  "weekly-reset-timer": [{ field: "weeklyResetAt", suppressFetchError: true }],
@@ -80074,8 +80429,7 @@ var USAGE_WIDGET_REQUIREMENTS = {
80074
80429
  var USAGE_CURSOR_REQUIREMENTS = {
80075
80430
  "session-usage": { field: "sessionResetAt" },
80076
80431
  "weekly-usage": { field: "weeklyResetAt" },
80077
- "weekly-sonnet-usage": { field: "weeklySonnetResetAt", alternatives: ["weeklyResetAt"] },
80078
- "weekly-opus-usage": { field: "weeklyOpusResetAt", alternatives: ["weeklyResetAt"] }
80432
+ ...Object.fromEntries(WEEKLY_MODEL_USAGE_BUCKETS.map((bucket) => [bucket.widgetType, { field: bucket.resetField, alternatives: ["weeklyResetAt"] }]))
80079
80433
  };
80080
80434
  function hasUsageDependentWidgets(lines) {
80081
80435
  return lines.some((line) => line.some((item) => USAGE_WIDGET_TYPES.has(item.type)));
@@ -80125,21 +80479,14 @@ function hasAnyUsageDataField(data) {
80125
80479
  return USAGE_DATA_FIELDS.some((field) => data?.[field] !== undefined);
80126
80480
  }
80127
80481
  function pickDefinedUsageFields(data) {
80128
- return {
80129
- ...data?.sessionUsage !== undefined ? { sessionUsage: data.sessionUsage } : {},
80130
- ...data?.sessionResetAt !== undefined ? { sessionResetAt: data.sessionResetAt } : {},
80131
- ...data?.weeklyUsage !== undefined ? { weeklyUsage: data.weeklyUsage } : {},
80132
- ...data?.weeklyResetAt !== undefined ? { weeklyResetAt: data.weeklyResetAt } : {},
80133
- ...data?.weeklySonnetUsage !== undefined ? { weeklySonnetUsage: data.weeklySonnetUsage } : {},
80134
- ...data?.weeklySonnetResetAt !== undefined ? { weeklySonnetResetAt: data.weeklySonnetResetAt } : {},
80135
- ...data?.weeklyOpusUsage !== undefined ? { weeklyOpusUsage: data.weeklyOpusUsage } : {},
80136
- ...data?.weeklyOpusResetAt !== undefined ? { weeklyOpusResetAt: data.weeklyOpusResetAt } : {},
80137
- ...data?.extraUsageEnabled !== undefined ? { extraUsageEnabled: data.extraUsageEnabled } : {},
80138
- ...data?.extraUsageLimit !== undefined ? { extraUsageLimit: data.extraUsageLimit } : {},
80139
- ...data?.extraUsageUsed !== undefined ? { extraUsageUsed: data.extraUsageUsed } : {},
80140
- ...data?.extraUsageUtilization !== undefined ? { extraUsageUtilization: data.extraUsageUtilization } : {},
80141
- ...data?.extraUsageCurrency !== undefined ? { extraUsageCurrency: data.extraUsageCurrency } : {}
80142
- };
80482
+ const picked = {};
80483
+ for (const field of USAGE_DATA_FIELDS) {
80484
+ const value = data?.[field];
80485
+ if (value !== undefined) {
80486
+ setUsageField(picked, field, value);
80487
+ }
80488
+ }
80489
+ return picked;
80143
80490
  }
80144
80491
  function mergeUsageData(rateLimitsData, apiData) {
80145
80492
  return {
@@ -80154,28 +80501,28 @@ function epochSecondsToIsoString(epochSeconds) {
80154
80501
  }
80155
80502
  return new Date(epochSeconds * 1000).toISOString();
80156
80503
  }
80504
+ function getRateLimitBucketUsage(bucket) {
80505
+ return bucket?.used_percentage ?? undefined;
80506
+ }
80157
80507
  function extractUsageDataFromRateLimits(rateLimits) {
80158
80508
  if (!rateLimits) {
80159
80509
  return null;
80160
80510
  }
80161
- const sessionUsage = rateLimits.five_hour?.used_percentage ?? undefined;
80162
- const sessionResetAt = epochSecondsToIsoString(rateLimits.five_hour?.resets_at);
80163
- const weeklyUsage = rateLimits.seven_day?.used_percentage ?? undefined;
80164
- const weeklyResetAt = epochSecondsToIsoString(rateLimits.seven_day?.resets_at);
80165
- const weeklySonnetUsage = rateLimits.seven_day_sonnet === null ? 0 : rateLimits.seven_day_sonnet?.used_percentage ?? undefined;
80166
- const weeklySonnetResetAt = epochSecondsToIsoString(rateLimits.seven_day_sonnet?.resets_at);
80167
- const weeklyOpusUsage = rateLimits.seven_day_opus === null ? 0 : rateLimits.seven_day_opus?.used_percentage ?? undefined;
80168
- const weeklyOpusResetAt = epochSecondsToIsoString(rateLimits.seven_day_opus?.resets_at);
80511
+ const rateLimitBuckets = rateLimits;
80169
80512
  const usageData = {
80170
- sessionUsage,
80171
- sessionResetAt,
80172
- weeklyUsage,
80173
- weeklyResetAt,
80174
- weeklySonnetUsage,
80175
- weeklySonnetResetAt,
80176
- weeklyOpusUsage,
80177
- weeklyOpusResetAt
80513
+ sessionUsage: rateLimits.five_hour?.used_percentage ?? undefined,
80514
+ sessionResetAt: epochSecondsToIsoString(rateLimits.five_hour?.resets_at),
80515
+ weeklyUsage: rateLimits.seven_day?.used_percentage ?? undefined,
80516
+ weeklyResetAt: epochSecondsToIsoString(rateLimits.seven_day?.resets_at)
80178
80517
  };
80518
+ for (const bucket of WEEKLY_MODEL_USAGE_BUCKETS) {
80519
+ if (!bucket.apiBucketKey) {
80520
+ continue;
80521
+ }
80522
+ const rateLimitBucket = rateLimitBuckets[bucket.apiBucketKey];
80523
+ setUsageField(usageData, bucket.usageField, getRateLimitBucketUsage(rateLimitBucket));
80524
+ setUsageField(usageData, bucket.resetField, epochSecondsToIsoString(rateLimitBucket?.resets_at));
80525
+ }
80179
80526
  return hasAnyUsageDataField(usageData) ? usageData : null;
80180
80527
  }
80181
80528
  async function prefetchUsageDataIfNeeded(lines, data) {
@@ -80286,7 +80633,8 @@ async function renderMultipleLines(data) {
80286
80633
  terminalWidth: getTerminalWidth(),
80287
80634
  isPreview: false,
80288
80635
  minimalist: settings.minimalistMode,
80289
- gitCacheTtlSeconds: settings.gitCacheTtlSeconds
80636
+ gitCacheTtlSeconds: settings.gitCacheTtlSeconds,
80637
+ gitReviewNeedsChecks: lines.some((line) => line.some((item) => item.type === "git-ci-status"))
80290
80638
  };
80291
80639
  const preRenderedLines = preRenderAllWidgets(lines, settings, context);
80292
80640
  const preCalculatedMaxWidths = calculateMaxWidthsFromPreRendered(preRenderedLines, settings);
@@ -80361,7 +80709,24 @@ async function handleHook() {
80361
80709
  const input = await readStdin();
80362
80710
  handleHookInput(input);
80363
80711
  }
80712
+ function handleGitReviewRefresh() {
80713
+ const flagIndex = process.argv.indexOf(GIT_REVIEW_REFRESH_FLAG);
80714
+ if (flagIndex === -1) {
80715
+ return false;
80716
+ }
80717
+ const cwd2 = process.argv[flagIndex + 1];
80718
+ const mode = process.argv[flagIndex + 2];
80719
+ const lockPath = process.argv[flagIndex + 3];
80720
+ if (!cwd2 || mode !== "metadata" && mode !== "checks" || !lockPath) {
80721
+ return true;
80722
+ }
80723
+ refreshGitReviewCacheFromCli(cwd2, { includeChecks: mode === "checks" }, lockPath);
80724
+ return true;
80725
+ }
80364
80726
  async function main() {
80727
+ if (handleGitReviewRefresh()) {
80728
+ return;
80729
+ }
80365
80730
  if (process.argv.includes("--version")) {
80366
80731
  console.log(getPackageVersion());
80367
80732
  process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccstatusline",
3
- "version": "2.2.24",
3
+ "version": "2.2.26",
4
4
  "bugs": {
5
5
  "url": "https://github.com/sirmalloc/ccstatusline/issues"
6
6
  },