@say8425/cc-statusline 2.1.0 → 2.2.0

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.
Files changed (3) hide show
  1. package/README.md +8 -0
  2. package/dist/index.js +31 -120
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -37,6 +37,14 @@ Add the following to `~/.claude/settings.json`:
37
37
 
38
38
  ![scenario3_diff_pr](docs/scenario3_diff_pr.png)
39
39
 
40
+ ### Worktree
41
+
42
+ ![worktree_diff](docs/worktree_diff.png)
43
+
44
+ ### Worktree + Usage Metrics
45
+
46
+ ![worktree_usage](docs/worktree_usage.png)
47
+
40
48
  ### Usage Metrics
41
49
 
42
50
  ![Screenshot of status line with usage metrics](docs/usage_metrics.png)
package/dist/index.js CHANGED
@@ -1,12 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
3
 
4
- // src/cli.ts
5
- function parseCliArgs(args) {
6
- const showUsage = args.includes("--show-usage");
7
- return { showUsage };
8
- }
9
-
10
4
  // src/git/branch.ts
11
5
  var {$ } = globalThis.Bun;
12
6
 
@@ -15,19 +9,12 @@ var cache = {
15
9
  branch: { value: "", timestamp: 0 },
16
10
  gitChanges: { files: 0, insertions: 0, deletions: 0, timestamp: 0 },
17
11
  prUrl: { value: null, timestamp: 0 },
18
- blockUsage: {
19
- value: null,
20
- timestamp: 0
21
- },
22
- accessToken: { value: null, timestamp: 0 },
23
12
  mainProjectName: { value: null, timestamp: 0 }
24
13
  };
25
14
  var CACHE_TTL = {
26
15
  branch: 5000,
27
16
  gitChanges: 0,
28
17
  prUrl: 30000,
29
- blockUsage: 120000,
30
- accessToken: 300000,
31
18
  mainProjectName: 300000
32
19
  };
33
20
 
@@ -148,11 +135,6 @@ function renderStatusLine(ctx) {
148
135
  const sessionHrs = Math.floor(sessionSec / 3600);
149
136
  const sessionMins = Math.floor(sessionSec % 3600 / 60);
150
137
  const costUsd = ctx.claudeJson.cost?.total_cost_usd || 0;
151
- const usage = ctx.claudeJson.context_window?.current_usage;
152
- const contextSize = ctx.claudeJson.context_window?.context_window_size || 200000;
153
- const totalTokens = usage ? usage.input_tokens + usage.output_tokens + usage.cache_creation_input_tokens + usage.cache_read_input_tokens : 0;
154
- const contextPct = Math.round(totalTokens / contextSize * 100);
155
- const ctxColor = getUsageColor(contextPct);
156
138
  let line1;
157
139
  if (ctx.mainProjectName) {
158
140
  line1 = `${C.WHITE}\uD83D\uDCC1 ${ctx.mainProjectName}${C.RESET} | ${C.WHITE}\uD83C\uDF32 ${folder}${C.RESET}`;
@@ -163,23 +145,37 @@ function renderStatusLine(ctx) {
163
145
  line1 += ` | ${C.WHITE}\uD83C\uDF3F ${ctx.branch}${C.RESET}`;
164
146
  }
165
147
  lines.push(line1);
166
- const line2 = `${C.WHITE}\u23F1\uFE0F ${formatTime(sessionHrs, sessionMins)}${C.RESET}` + ` | ${C.WHITE}\uD83D\uDCB0 $${costUsd.toFixed(2)}${C.RESET}` + ` | ${ctxColor}\uD83E\uDDE0 ${formatNumber(totalTokens)} (${contextPct}%)${C.RESET}`;
148
+ let line2 = `${C.WHITE}\u23F1\uFE0F ${formatTime(sessionHrs, sessionMins)}${C.RESET}` + ` | ${C.WHITE}\uD83D\uDCB0 $${costUsd.toFixed(2)}${C.RESET}`;
149
+ const usedPercentage = ctx.claudeJson.context_window?.used_percentage;
150
+ if (usedPercentage != null) {
151
+ const usage = ctx.claudeJson.context_window?.current_usage;
152
+ const totalTokens = usage ? usage.input_tokens + usage.output_tokens + usage.cache_creation_input_tokens + usage.cache_read_input_tokens : 0;
153
+ const contextPct = Math.round(usedPercentage);
154
+ const ctxColor = getUsageColor(contextPct);
155
+ line2 += ` | ${ctxColor}\uD83E\uDDE0 ${formatNumber(totalTokens)} (${contextPct}%)${C.RESET}`;
156
+ }
167
157
  lines.push(line2);
168
- if (ctx.showUsage && ctx.blockUsage) {
158
+ if (ctx.rateLimits) {
169
159
  const parts = [];
170
- if (ctx.blockUsage.resetTime) {
171
- const h = ctx.blockUsage.resetTime.getHours();
172
- const m = ctx.blockUsage.resetTime.getMinutes();
173
- parts.push(`${C.WHITE}\u23F3 ${formatTime(h, m)}${C.RESET}`);
174
- }
175
- const usageColor = getUsageColor(ctx.blockUsage.utilization);
176
- parts.push(`${usageColor}\uD83D\uDCCA ${Math.round(ctx.blockUsage.utilization)}/100${C.RESET}`);
177
- if (ctx.blockUsage.sevenDayResetTime) {
178
- parts.push(`${C.WHITE}\u23F0 ${formatResetDate(ctx.blockUsage.sevenDayResetTime)}${C.RESET}`);
160
+ if (ctx.rateLimits.five_hour) {
161
+ const { resets_at, used_percentage } = ctx.rateLimits.five_hour;
162
+ if (resets_at) {
163
+ const resetTime = new Date(resets_at * 1000);
164
+ const h = resetTime.getHours();
165
+ const m = resetTime.getMinutes();
166
+ parts.push(`${C.WHITE}\u23F3 ${formatTime(h, m)}${C.RESET}`);
167
+ }
168
+ const usageColor = getUsageColor(used_percentage);
169
+ parts.push(`${usageColor}\uD83D\uDCCA ${Math.round(used_percentage)}/100${C.RESET}`);
179
170
  }
180
- if (ctx.blockUsage.sevenDayUtilization !== null) {
181
- const weekColor = getUsageColor(ctx.blockUsage.sevenDayUtilization);
182
- parts.push(`${weekColor}\uD83D\uDCC5 ${Math.round(ctx.blockUsage.sevenDayUtilization)}/100${C.RESET}`);
171
+ if (ctx.rateLimits.seven_day) {
172
+ const { resets_at, used_percentage } = ctx.rateLimits.seven_day;
173
+ if (resets_at) {
174
+ const resetTime = new Date(resets_at * 1000);
175
+ parts.push(`${C.WHITE}\u23F0 ${formatResetDate(resetTime)}${C.RESET}`);
176
+ }
177
+ const weekColor = getUsageColor(used_percentage);
178
+ parts.push(`${weekColor}\uD83D\uDCC5 ${Math.round(used_percentage)}/100${C.RESET}`);
183
179
  }
184
180
  if (parts.length > 0) {
185
181
  lines.push(parts.join(" | "));
@@ -211,97 +207,13 @@ async function readStdin() {
211
207
  return Buffer.concat(chunks).toString("utf-8");
212
208
  }
213
209
 
214
- // src/usage/token.ts
215
- var {$: $5 } = globalThis.Bun;
216
- async function getAccessToken() {
217
- if (process.platform !== "darwin")
218
- return null;
219
- try {
220
- const result = await $5`security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null`.text();
221
- const creds = JSON.parse(result);
222
- return creds?.claudeAiOauth?.accessToken ?? null;
223
- } catch {
224
- return null;
225
- }
226
- }
227
- async function getAccessTokenCached() {
228
- if (Date.now() - cache.accessToken.timestamp < CACHE_TTL.accessToken) {
229
- return cache.accessToken.value;
230
- }
231
- const token = await getAccessToken();
232
- cache.accessToken = { value: token, timestamp: Date.now() };
233
- return token;
234
- }
235
-
236
- // src/usage/api.ts
237
- async function fetchUsageFromAPI(accessToken) {
238
- try {
239
- const controller = new AbortController;
240
- const timeout = setTimeout(() => controller.abort(), 5000);
241
- const response = await fetch("https://api.anthropic.com/api/oauth/usage", {
242
- headers: {
243
- Authorization: `Bearer ${accessToken}`,
244
- "anthropic-beta": "oauth-2025-04-20"
245
- },
246
- signal: controller.signal
247
- });
248
- clearTimeout(timeout);
249
- if (!response.ok)
250
- return null;
251
- return await response.json();
252
- } catch {
253
- return null;
254
- }
255
- }
256
- function usageResponseToBlockUsage(data) {
257
- const result = {
258
- resetTime: null,
259
- utilization: 0,
260
- sevenDayUtilization: null,
261
- sevenDayResetTime: null
262
- };
263
- if (data.five_hour) {
264
- result.utilization = data.five_hour.utilization;
265
- if (data.five_hour.resets_at) {
266
- result.resetTime = new Date(data.five_hour.resets_at);
267
- }
268
- }
269
- if (data.seven_day) {
270
- result.sevenDayUtilization = data.seven_day.utilization;
271
- if (data.seven_day.resets_at) {
272
- result.sevenDayResetTime = new Date(data.seven_day.resets_at);
273
- }
274
- }
275
- return result;
276
- }
277
- async function getUsageCached() {
278
- if (Date.now() - cache.blockUsage.timestamp < CACHE_TTL.blockUsage) {
279
- return cache.blockUsage.value;
280
- }
281
- const token = await getAccessTokenCached();
282
- if (!token) {
283
- cache.blockUsage = { value: null, timestamp: Date.now() };
284
- return null;
285
- }
286
- const data = await fetchUsageFromAPI(token);
287
- if (!data) {
288
- cache.blockUsage = { value: null, timestamp: Date.now() };
289
- return null;
290
- }
291
- const blockUsage = usageResponseToBlockUsage(data);
292
- cache.blockUsage = { value: blockUsage, timestamp: Date.now() };
293
- return blockUsage;
294
- }
295
210
  // src/index.ts
296
- async function main(cliArgs) {
297
- const args = cliArgs ?? process.argv.slice(2);
298
- const { showUsage } = parseCliArgs(args);
211
+ async function main() {
299
212
  const claudeJson = JSON.parse(await readStdin() || "{}");
300
- const [branch, gitChanges, prUrl, blockUsage, mainProjectName] = await Promise.all([
213
+ const [branch, gitChanges, prUrl, mainProjectName] = await Promise.all([
301
214
  getBranchCached(),
302
215
  getGitChangesCached(),
303
216
  getPrUrlCached(),
304
- showUsage ? getUsageCached() : Promise.resolve(null),
305
217
  getMainProjectNameCached()
306
218
  ]);
307
219
  const lines = renderStatusLine({
@@ -309,8 +221,7 @@ async function main(cliArgs) {
309
221
  branch,
310
222
  gitChanges,
311
223
  prUrl,
312
- blockUsage,
313
- showUsage,
224
+ rateLimits: claudeJson.rate_limits ?? null,
314
225
  mainProjectName
315
226
  });
316
227
  for (const line of lines) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@say8425/cc-statusline",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/say8425/cc-statusline.git"