@say8425/cc-statusline 2.0.2 → 2.1.1

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 +10 -0
  2. package/dist/index.js +53 -115
  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)
@@ -48,6 +56,7 @@ Add the following to `~/.claude/settings.json`:
48
56
  - **Context**: Token usage with percentage (color-coded)
49
57
  - **Git Diff**: File count, insertions, deletions
50
58
  - **PR URL**: Clickable OSC 8 hyperlink
59
+ - **Worktree Support**: Shows real project name when running in a `cc --worktree` session
51
60
  - **TrueColor**: Dynamic colors based on thresholds
52
61
  - **Limit Reset Time**: Reset time display (HH:MM)
53
62
  - **Block Usage**: 5-hour utilization percentage (from server API)
@@ -59,6 +68,7 @@ Add the following to `~/.claude/settings.json`:
59
68
  | Emoji | Description |
60
69
  | ----- | ------------------------ |
61
70
  | 📁 | Project folder name |
71
+ | 🌲 | Worktree name (shown in worktree sessions) |
62
72
  | 🌿 | Current Git branch |
63
73
  | ⏱️ | Session elapsed time |
64
74
  | 💰 | Session cost in USD |
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,18 +9,13 @@ 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 }
12
+ mainProjectName: { value: null, timestamp: 0 }
23
13
  };
24
14
  var CACHE_TTL = {
25
15
  branch: 5000,
26
16
  gitChanges: 0,
27
17
  prUrl: 30000,
28
- blockUsage: 120000,
29
- accessToken: 300000
18
+ mainProjectName: 300000
30
19
  };
31
20
 
32
21
  // src/git/branch.ts
@@ -80,6 +69,27 @@ async function getPrUrlCached() {
80
69
  return cache.prUrl.value;
81
70
  }
82
71
  }
72
+ // src/git/worktree.ts
73
+ var {$: $4 } = globalThis.Bun;
74
+ async function getMainProjectNameCached() {
75
+ if (Date.now() - cache.mainProjectName.timestamp < CACHE_TTL.mainProjectName) {
76
+ return cache.mainProjectName.value;
77
+ }
78
+ try {
79
+ const result = await $4`git rev-parse --git-common-dir 2>/dev/null`.text();
80
+ const gitCommonDir = result.trim();
81
+ if (gitCommonDir === ".git") {
82
+ cache.mainProjectName = { value: null, timestamp: Date.now() };
83
+ return null;
84
+ }
85
+ const projectRoot = gitCommonDir.replace(/\/.git$/, "");
86
+ const projectName = projectRoot.split("/").pop() || null;
87
+ cache.mainProjectName = { value: projectName, timestamp: Date.now() };
88
+ return projectName;
89
+ } catch {
90
+ return cache.mainProjectName.value;
91
+ }
92
+ }
83
93
  // src/colors.ts
84
94
  var C = {
85
95
  RESET: "\x1B[0m",
@@ -130,28 +140,39 @@ function renderStatusLine(ctx) {
130
140
  const totalTokens = usage ? usage.input_tokens + usage.output_tokens + usage.cache_creation_input_tokens + usage.cache_read_input_tokens : 0;
131
141
  const contextPct = Math.round(totalTokens / contextSize * 100);
132
142
  const ctxColor = getUsageColor(contextPct);
133
- let line1 = `${C.WHITE}\uD83D\uDCC1 ${folder}${C.RESET}`;
143
+ let line1;
144
+ if (ctx.mainProjectName) {
145
+ line1 = `${C.WHITE}\uD83D\uDCC1 ${ctx.mainProjectName}${C.RESET} | ${C.WHITE}\uD83C\uDF32 ${folder}${C.RESET}`;
146
+ } else {
147
+ line1 = `${C.WHITE}\uD83D\uDCC1 ${folder}${C.RESET}`;
148
+ }
134
149
  if (ctx.branch) {
135
150
  line1 += ` | ${C.WHITE}\uD83C\uDF3F ${ctx.branch}${C.RESET}`;
136
151
  }
137
152
  lines.push(line1);
138
153
  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}`;
139
154
  lines.push(line2);
140
- if (ctx.showUsage && ctx.blockUsage) {
155
+ if (ctx.rateLimits) {
141
156
  const parts = [];
142
- if (ctx.blockUsage.resetTime) {
143
- const h = ctx.blockUsage.resetTime.getHours();
144
- const m = ctx.blockUsage.resetTime.getMinutes();
145
- parts.push(`${C.WHITE}\u23F3 ${formatTime(h, m)}${C.RESET}`);
157
+ if (ctx.rateLimits.five_hour) {
158
+ const { resets_at, used_percentage } = ctx.rateLimits.five_hour;
159
+ if (resets_at) {
160
+ const resetTime = new Date(resets_at * 1000);
161
+ const h = resetTime.getHours();
162
+ const m = resetTime.getMinutes();
163
+ parts.push(`${C.WHITE}\u23F3 ${formatTime(h, m)}${C.RESET}`);
164
+ }
165
+ const usageColor = getUsageColor(used_percentage);
166
+ parts.push(`${usageColor}\uD83D\uDCCA ${Math.round(used_percentage)}/100${C.RESET}`);
146
167
  }
147
- const usageColor = getUsageColor(ctx.blockUsage.utilization);
148
- parts.push(`${usageColor}\uD83D\uDCCA ${Math.round(ctx.blockUsage.utilization)}/100${C.RESET}`);
149
- if (ctx.blockUsage.sevenDayResetTime) {
150
- parts.push(`${C.WHITE}\u23F0 ${formatResetDate(ctx.blockUsage.sevenDayResetTime)}${C.RESET}`);
151
- }
152
- if (ctx.blockUsage.sevenDayUtilization !== null) {
153
- const weekColor = getUsageColor(ctx.blockUsage.sevenDayUtilization);
154
- parts.push(`${weekColor}\uD83D\uDCC5 ${Math.round(ctx.blockUsage.sevenDayUtilization)}/100${C.RESET}`);
168
+ if (ctx.rateLimits.seven_day) {
169
+ const { resets_at, used_percentage } = ctx.rateLimits.seven_day;
170
+ if (resets_at) {
171
+ const resetTime = new Date(resets_at * 1000);
172
+ parts.push(`${C.WHITE}\u23F0 ${formatResetDate(resetTime)}${C.RESET}`);
173
+ }
174
+ const weekColor = getUsageColor(used_percentage);
175
+ parts.push(`${weekColor}\uD83D\uDCC5 ${Math.round(used_percentage)}/100${C.RESET}`);
155
176
  }
156
177
  if (parts.length > 0) {
157
178
  lines.push(parts.join(" | "));
@@ -183,105 +204,22 @@ async function readStdin() {
183
204
  return Buffer.concat(chunks).toString("utf-8");
184
205
  }
185
206
 
186
- // src/usage/token.ts
187
- var {$: $4 } = globalThis.Bun;
188
- async function getAccessToken() {
189
- if (process.platform !== "darwin")
190
- return null;
191
- try {
192
- const result = await $4`security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null`.text();
193
- const creds = JSON.parse(result);
194
- return creds?.claudeAiOauth?.accessToken ?? null;
195
- } catch {
196
- return null;
197
- }
198
- }
199
- async function getAccessTokenCached() {
200
- if (Date.now() - cache.accessToken.timestamp < CACHE_TTL.accessToken) {
201
- return cache.accessToken.value;
202
- }
203
- const token = await getAccessToken();
204
- cache.accessToken = { value: token, timestamp: Date.now() };
205
- return token;
206
- }
207
-
208
- // src/usage/api.ts
209
- async function fetchUsageFromAPI(accessToken) {
210
- try {
211
- const controller = new AbortController;
212
- const timeout = setTimeout(() => controller.abort(), 5000);
213
- const response = await fetch("https://api.anthropic.com/api/oauth/usage", {
214
- headers: {
215
- Authorization: `Bearer ${accessToken}`,
216
- "anthropic-beta": "oauth-2025-04-20"
217
- },
218
- signal: controller.signal
219
- });
220
- clearTimeout(timeout);
221
- if (!response.ok)
222
- return null;
223
- return await response.json();
224
- } catch {
225
- return null;
226
- }
227
- }
228
- function usageResponseToBlockUsage(data) {
229
- const result = {
230
- resetTime: null,
231
- utilization: 0,
232
- sevenDayUtilization: null,
233
- sevenDayResetTime: null
234
- };
235
- if (data.five_hour) {
236
- result.utilization = data.five_hour.utilization;
237
- if (data.five_hour.resets_at) {
238
- result.resetTime = new Date(data.five_hour.resets_at);
239
- }
240
- }
241
- if (data.seven_day) {
242
- result.sevenDayUtilization = data.seven_day.utilization;
243
- if (data.seven_day.resets_at) {
244
- result.sevenDayResetTime = new Date(data.seven_day.resets_at);
245
- }
246
- }
247
- return result;
248
- }
249
- async function getUsageCached() {
250
- if (Date.now() - cache.blockUsage.timestamp < CACHE_TTL.blockUsage) {
251
- return cache.blockUsage.value;
252
- }
253
- const token = await getAccessTokenCached();
254
- if (!token) {
255
- cache.blockUsage = { value: null, timestamp: Date.now() };
256
- return null;
257
- }
258
- const data = await fetchUsageFromAPI(token);
259
- if (!data) {
260
- cache.blockUsage = { value: null, timestamp: Date.now() };
261
- return null;
262
- }
263
- const blockUsage = usageResponseToBlockUsage(data);
264
- cache.blockUsage = { value: blockUsage, timestamp: Date.now() };
265
- return blockUsage;
266
- }
267
207
  // src/index.ts
268
- async function main(cliArgs) {
269
- const args = cliArgs ?? process.argv.slice(2);
270
- const { showUsage } = parseCliArgs(args);
208
+ async function main() {
271
209
  const claudeJson = JSON.parse(await readStdin() || "{}");
272
- const [branch, gitChanges, prUrl, blockUsage] = await Promise.all([
210
+ const [branch, gitChanges, prUrl, mainProjectName] = await Promise.all([
273
211
  getBranchCached(),
274
212
  getGitChangesCached(),
275
213
  getPrUrlCached(),
276
- showUsage ? getUsageCached() : Promise.resolve(null)
214
+ getMainProjectNameCached()
277
215
  ]);
278
216
  const lines = renderStatusLine({
279
217
  claudeJson,
280
218
  branch,
281
219
  gitChanges,
282
220
  prUrl,
283
- blockUsage,
284
- showUsage
221
+ rateLimits: claudeJson.rate_limits ?? null,
222
+ mainProjectName
285
223
  });
286
224
  for (const line of lines) {
287
225
  console.log(line);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@say8425/cc-statusline",
3
- "version": "2.0.2",
3
+ "version": "2.1.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/say8425/cc-statusline.git"