@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.
- package/README.md +8 -0
- package/dist/index.js +31 -120
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,6 +37,14 @@ Add the following to `~/.claude/settings.json`:
|
|
|
37
37
|
|
|
38
38
|

|
|
39
39
|
|
|
40
|
+
### Worktree
|
|
41
|
+
|
|
42
|
+

|
|
43
|
+
|
|
44
|
+
### Worktree + Usage Metrics
|
|
45
|
+
|
|
46
|
+

|
|
47
|
+
|
|
40
48
|
### Usage Metrics
|
|
41
49
|
|
|
42
50
|

|
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
|
-
|
|
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.
|
|
158
|
+
if (ctx.rateLimits) {
|
|
169
159
|
const parts = [];
|
|
170
|
-
if (ctx.
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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.
|
|
181
|
-
const
|
|
182
|
-
|
|
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(
|
|
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,
|
|
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
|
-
|
|
313
|
-
showUsage,
|
|
224
|
+
rateLimits: claudeJson.rate_limits ?? null,
|
|
314
225
|
mainProjectName
|
|
315
226
|
});
|
|
316
227
|
for (const line of lines) {
|