cc-dev-template 0.1.72 → 0.1.73
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/package.json +1 -1
- package/src/scripts/statusline.js +80 -8
package/package.json
CHANGED
|
@@ -21,6 +21,7 @@ const { homedir } = require('os');
|
|
|
21
21
|
// Usage API cache
|
|
22
22
|
const USAGE_CACHE_PATH = join(homedir(), '.claude', '.usage-cache.json');
|
|
23
23
|
const USAGE_CACHE_TTL = 45000; // 45 seconds
|
|
24
|
+
const USAGE_HISTORY_MAX = 20; // ~15 min of readings at 45s intervals
|
|
24
25
|
|
|
25
26
|
// Background refresh mode: fetch usage data and write cache, then exit
|
|
26
27
|
if (process.argv.includes('--refresh')) {
|
|
@@ -72,6 +73,50 @@ function getContextGreyscale(percentage) {
|
|
|
72
73
|
return '\x1b[38;5;240m'; // Dark grey - safe
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Compute burn rate (percent per minute) from usage history
|
|
78
|
+
* Returns null if insufficient data
|
|
79
|
+
*/
|
|
80
|
+
function getUsageBurnRate(history, key) {
|
|
81
|
+
if (!history || history.length < 2) return null;
|
|
82
|
+
|
|
83
|
+
const oldest = history[0];
|
|
84
|
+
const newest = history[history.length - 1];
|
|
85
|
+
const minutesElapsed = (newest.t - oldest.t) / 60000;
|
|
86
|
+
|
|
87
|
+
// Need at least 2 minutes of data for a stable reading
|
|
88
|
+
if (minutesElapsed < 2) return null;
|
|
89
|
+
|
|
90
|
+
const deltaUtilization = newest[key] - oldest[key];
|
|
91
|
+
return deltaUtilization / minutesElapsed;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get color for usage bar based on current level and burn rate trend
|
|
96
|
+
* Returns red/yellow for danger, or greyscale for safe
|
|
97
|
+
*/
|
|
98
|
+
function getUsageColor(utilization, burnRate, windowType) {
|
|
99
|
+
const RED = '\x1b[38;5;196m';
|
|
100
|
+
const YELLOW = '\x1b[38;5;220m';
|
|
101
|
+
|
|
102
|
+
// Thresholds differ by window type
|
|
103
|
+
const redMinutes = windowType === '5h' ? 30 : 240; // 30min / 4hr
|
|
104
|
+
const yellowMinutes = windowType === '5h' ? 90 : 720; // 90min / 12hr
|
|
105
|
+
|
|
106
|
+
// Hard thresholds on current utilization
|
|
107
|
+
if (utilization >= 90) return RED;
|
|
108
|
+
if (utilization >= 75) return YELLOW;
|
|
109
|
+
|
|
110
|
+
// Trend-based: project time to hit 100%
|
|
111
|
+
if (burnRate && burnRate > 0) {
|
|
112
|
+
const minutesToLimit = (100 - utilization) / burnRate;
|
|
113
|
+
if (minutesToLimit <= redMinutes) return RED;
|
|
114
|
+
if (minutesToLimit <= yellowMinutes) return YELLOW;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return getContextGreyscale(utilization);
|
|
118
|
+
}
|
|
119
|
+
|
|
75
120
|
/**
|
|
76
121
|
* Count files in a directory recursively
|
|
77
122
|
*/
|
|
@@ -279,9 +324,29 @@ function refreshUsageCache() {
|
|
|
279
324
|
if (result.status === 0 && result.stdout) {
|
|
280
325
|
const data = JSON.parse(result.stdout.trim());
|
|
281
326
|
if (data.five_hour && data.seven_day) {
|
|
327
|
+
// Load existing history and append new reading
|
|
328
|
+
let history = [];
|
|
329
|
+
try {
|
|
330
|
+
const existing = JSON.parse(readFileSync(USAGE_CACHE_PATH, 'utf-8'));
|
|
331
|
+
if (Array.isArray(existing.history)) history = existing.history;
|
|
332
|
+
} catch {}
|
|
333
|
+
|
|
334
|
+
const now = Date.now();
|
|
335
|
+
history.push({
|
|
336
|
+
t: now,
|
|
337
|
+
five_hour: data.five_hour.utilization,
|
|
338
|
+
seven_day: data.seven_day.utilization,
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// Keep only the last N readings
|
|
342
|
+
if (history.length > USAGE_HISTORY_MAX) {
|
|
343
|
+
history = history.slice(-USAGE_HISTORY_MAX);
|
|
344
|
+
}
|
|
345
|
+
|
|
282
346
|
writeFileSync(USAGE_CACHE_PATH, JSON.stringify({
|
|
283
|
-
timestamp:
|
|
347
|
+
timestamp: now,
|
|
284
348
|
data,
|
|
349
|
+
history,
|
|
285
350
|
}));
|
|
286
351
|
}
|
|
287
352
|
}
|
|
@@ -295,12 +360,14 @@ function refreshUsageCache() {
|
|
|
295
360
|
*/
|
|
296
361
|
function getUsageData() {
|
|
297
362
|
let cacheData = null;
|
|
363
|
+
let cacheHistory = null;
|
|
298
364
|
let cacheAge = Infinity;
|
|
299
365
|
|
|
300
366
|
try {
|
|
301
367
|
const raw = readFileSync(USAGE_CACHE_PATH, 'utf-8');
|
|
302
368
|
const cache = JSON.parse(raw);
|
|
303
369
|
cacheData = cache.data;
|
|
370
|
+
cacheHistory = cache.history || null;
|
|
304
371
|
cacheAge = Date.now() - cache.timestamp;
|
|
305
372
|
} catch {}
|
|
306
373
|
|
|
@@ -315,7 +382,7 @@ function getUsageData() {
|
|
|
315
382
|
} catch {}
|
|
316
383
|
}
|
|
317
384
|
|
|
318
|
-
return cacheData;
|
|
385
|
+
return { data: cacheData, history: cacheHistory };
|
|
319
386
|
}
|
|
320
387
|
|
|
321
388
|
/**
|
|
@@ -474,14 +541,19 @@ function main() {
|
|
|
474
541
|
|
|
475
542
|
// Usage limits line (5-hour session + 7-day weekly)
|
|
476
543
|
const usageLines = [];
|
|
477
|
-
const
|
|
478
|
-
if (
|
|
479
|
-
const pct5h = Math.round(
|
|
480
|
-
const pct7d = Math.round(
|
|
544
|
+
const { data: usageApiData, history: usageHistory } = getUsageData();
|
|
545
|
+
if (usageApiData && usageApiData.five_hour && usageApiData.seven_day) {
|
|
546
|
+
const pct5h = Math.round(usageApiData.five_hour.utilization);
|
|
547
|
+
const pct7d = Math.round(usageApiData.seven_day.utilization);
|
|
481
548
|
const bar5h = generateBar(pct5h, 12);
|
|
482
549
|
const bar7d = generateBar(pct7d, 12);
|
|
483
|
-
|
|
484
|
-
|
|
550
|
+
|
|
551
|
+
// Compute burn rates from history for trend-based coloring
|
|
552
|
+
const rate5h = getUsageBurnRate(usageHistory, 'five_hour');
|
|
553
|
+
const rate7d = getUsageBurnRate(usageHistory, 'seven_day');
|
|
554
|
+
const color5h = getUsageColor(pct5h, rate5h, '5h');
|
|
555
|
+
const color7d = getUsageColor(pct7d, rate7d, '7d');
|
|
556
|
+
|
|
485
557
|
const str5h = pct5h.toString().padStart(3, ' ');
|
|
486
558
|
const str7d = pct7d.toString().padStart(3, ' ');
|
|
487
559
|
const usageDisplay = `5HR: ${color5h}[${bar5h}]${str5h}%${DIM_GREY} 7D: ${color7d}[${bar7d}]${str7d}%${DIM_GREY}`;
|