cc-costline 0.2.3 → 0.3.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.es.md +7 -1
- package/README.fr.md +7 -1
- package/README.ja.md +7 -1
- package/README.md +7 -1
- package/README.zh-CN.md +7 -1
- package/dist/statusline.d.ts +5 -0
- package/dist/statusline.js +82 -18
- package/package.json +3 -2
package/README.es.md
CHANGED
|
@@ -23,7 +23,7 @@ Abre una nueva sesión de Claude Code y verás la statusline mejorada. Requiere
|
|
|
23
23
|
| Segmento | Ejemplo | Descripción |
|
|
24
24
|
|----------|---------|-------------|
|
|
25
25
|
| Tokens ~ Costo / Contexto | `14.6k ~ $2.42 / 40% by Opus 4.6` | Tokens de la sesión, costo, uso de contexto y modelo |
|
|
26
|
-
| Límites de uso | `5h: 45% / 7d: 8%` | Utilización de Claude a 5 horas y 7 días (coloreado como el contexto) |
|
|
26
|
+
| Límites de uso | `5h: 45% / 7d: 8%` | Utilización de Claude a 5 horas y 7 días (coloreado como el contexto). Al 100%, muestra cuenta regresiva: `5h:-3:20` |
|
|
27
27
|
| Costo del período | `30d: $866` | Costo acumulado (configurable: 7d o 30d) |
|
|
28
28
|
| Ranking | `#2/22 $67.0` | Posición en [ccclub](https://github.com/mazzzystar/ccclub) (si está instalado) |
|
|
29
29
|
|
|
@@ -77,6 +77,12 @@ Los modelos desconocidos usan el precio de su familia, Sonnet por defecto.
|
|
|
77
77
|
|
|
78
78
|
</details>
|
|
79
79
|
|
|
80
|
+
## Desarrollo
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npm test # Build + ejecutar tests unitarios (node:test, sin dependencias)
|
|
84
|
+
```
|
|
85
|
+
|
|
80
86
|
## Desinstalación
|
|
81
87
|
|
|
82
88
|
```bash
|
package/README.fr.md
CHANGED
|
@@ -23,7 +23,7 @@ Ouvrez une nouvelle session Claude Code et la statusline enrichie apparaîtra. N
|
|
|
23
23
|
| Segment | Exemple | Description |
|
|
24
24
|
|---------|---------|-------------|
|
|
25
25
|
| Tokens ~ Coût / Contexte | `14.6k ~ $2.42 / 40% by Opus 4.6` | Nombre de tokens, coût, utilisation du contexte et modèle |
|
|
26
|
-
| Limites d'utilisation | `5h: 45% / 7d: 8%` | Utilisation Claude sur 5 heures et 7 jours (colorée comme le contexte) |
|
|
26
|
+
| Limites d'utilisation | `5h: 45% / 7d: 8%` | Utilisation Claude sur 5 heures et 7 jours (colorée comme le contexte). À 100 %, affiche un compte à rebours : `5h:-3:20` |
|
|
27
27
|
| Coût périodique | `30d: $866` | Coût cumulé glissant (configurable : 7j ou 30j) |
|
|
28
28
|
| Classement | `#2/22 $67.0` | Rang [ccclub](https://github.com/mazzzystar/ccclub) (si installé) |
|
|
29
29
|
|
|
@@ -77,6 +77,12 @@ Les modèles inconnus utilisent le prix de leur famille, Sonnet par défaut.
|
|
|
77
77
|
|
|
78
78
|
</details>
|
|
79
79
|
|
|
80
|
+
## Développement
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npm test # Build + exécuter les tests unitaires (node:test, zéro dépendance)
|
|
84
|
+
```
|
|
85
|
+
|
|
80
86
|
## Désinstallation
|
|
81
87
|
|
|
82
88
|
```bash
|
package/README.ja.md
CHANGED
|
@@ -23,7 +23,7 @@ npm i -g cc-costline && cc-costline install
|
|
|
23
23
|
| セグメント | 例 | 説明 |
|
|
24
24
|
|-----------|---|------|
|
|
25
25
|
| トークン ~ コスト / コンテキスト | `14.6k ~ $2.42 / 40% by Opus 4.6` | セッションのトークン数、コスト、コンテキスト使用率、モデル |
|
|
26
|
-
| 使用制限 | `5h: 45% / 7d: 8%` | Claude の 5 時間・7
|
|
26
|
+
| 使用制限 | `5h: 45% / 7d: 8%` | Claude の 5 時間・7 日間の使用率(コンテキストと同じ色分け)。100% 到達時はカウントダウン表示:`5h:-3:20` |
|
|
27
27
|
| 期間コスト | `30d: $866` | ローリングコスト合計(7d または 30d で設定可能) |
|
|
28
28
|
| リーダーボード | `#2/22 $67.0` | [ccclub](https://github.com/mazzzystar/ccclub) ランキング(インストール時) |
|
|
29
29
|
|
|
@@ -77,6 +77,12 @@ cc-costline config --period 7d # 7 日間のコストを表示
|
|
|
77
77
|
|
|
78
78
|
</details>
|
|
79
79
|
|
|
80
|
+
## 開発
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npm test # ビルド + ユニットテスト実行(node:test、依存関係なし)
|
|
84
|
+
```
|
|
85
|
+
|
|
80
86
|
## アンインストール
|
|
81
87
|
|
|
82
88
|
```bash
|
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Open a new Claude Code session and you'll see the enhanced statusline. Requires
|
|
|
23
23
|
| Segment | Example | Description |
|
|
24
24
|
|---------|---------|-------------|
|
|
25
25
|
| Tokens ~ Cost / Context | `14.6k ~ $2.42 / 40% by Opus 4.6` | Session token count, cost, context usage, and model |
|
|
26
|
-
| Usage limits | `5h: 45% / 7d: 8%` | Claude 5-hour and 7-day utilization (auto-colored like context) |
|
|
26
|
+
| Usage limits | `5h: 45% / 7d: 8%` | Claude 5-hour and 7-day utilization (auto-colored like context). At 100%, shows countdown: `5h:-3:20` |
|
|
27
27
|
| Period cost | `30d: $866` | Rolling cost total (configurable: 7d or 30d) |
|
|
28
28
|
| Leaderboard | `#2/22 $67.0` | [ccclub](https://github.com/mazzzystar/ccclub) rank (if installed) |
|
|
29
29
|
|
|
@@ -77,6 +77,12 @@ Unknown models fall back by family name, defaulting to Sonnet pricing.
|
|
|
77
77
|
|
|
78
78
|
</details>
|
|
79
79
|
|
|
80
|
+
## Development
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npm test # Build + run unit tests (node:test, zero dependencies)
|
|
84
|
+
```
|
|
85
|
+
|
|
80
86
|
## Uninstall
|
|
81
87
|
|
|
82
88
|
```bash
|
package/README.zh-CN.md
CHANGED
|
@@ -23,7 +23,7 @@ npm i -g cc-costline && cc-costline install
|
|
|
23
23
|
| 模块 | 示例 | 说明 |
|
|
24
24
|
|------|------|------|
|
|
25
25
|
| Token ~ 费用 / 上下文 | `14.6k ~ $2.42 / 40% by Opus 4.6` | 会话 token 数量、费用、上下文使用率和模型 |
|
|
26
|
-
| 使用限额 | `5h: 45% / 7d: 8%` | Claude 5 小时和 7
|
|
26
|
+
| 使用限额 | `5h: 45% / 7d: 8%` | Claude 5 小时和 7 天使用率(颜色同上下文)。达到 100% 时显示倒计时:`5h:-3:20` |
|
|
27
27
|
| 周期费用 | `30d: $866` | 滚动费用合计(可配置:7d 或 30d) |
|
|
28
28
|
| 排行榜 | `#2/22 $67.0` | [ccclub](https://github.com/mazzzystar/ccclub) 排名(需安装) |
|
|
29
29
|
|
|
@@ -77,6 +77,12 @@ cc-costline config --period 7d # 显示 7 天费用
|
|
|
77
77
|
|
|
78
78
|
</details>
|
|
79
79
|
|
|
80
|
+
## 开发
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npm test # 构建 + 运行单元测试(node:test,零依赖)
|
|
84
|
+
```
|
|
85
|
+
|
|
80
86
|
## 卸载
|
|
81
87
|
|
|
82
88
|
```bash
|
package/dist/statusline.d.ts
CHANGED
|
@@ -1 +1,6 @@
|
|
|
1
|
+
export declare function formatTokens(t: number): string;
|
|
2
|
+
export declare function formatCost(n: number): string;
|
|
3
|
+
export declare function ctxColor(pct: number): string;
|
|
4
|
+
export declare function formatCountdown(resetsAtMs: number): string;
|
|
5
|
+
export declare function rankColor(rank: number): string;
|
|
1
6
|
export declare function render(input: string): string;
|
package/dist/statusline.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFileSync, existsSync, statSync, writeFileSync } from "node:fs";
|
|
1
|
+
import { readFileSync, existsSync, statSync, writeFileSync, unlinkSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { execSync } from "node:child_process";
|
|
@@ -14,14 +14,14 @@ const FG_MODEL = "\x1b[38;2;202;124;94m";
|
|
|
14
14
|
const FG_CYAN = "\x1b[38;5;109m";
|
|
15
15
|
const FG_WHITE = "\x1b[38;5;255m";
|
|
16
16
|
const RESET = "\x1b[0m";
|
|
17
|
-
function formatTokens(t) {
|
|
17
|
+
export function formatTokens(t) {
|
|
18
18
|
if (t >= 1_000_000)
|
|
19
19
|
return (t / 1_000_000).toFixed(1) + "M";
|
|
20
20
|
if (t >= 1_000)
|
|
21
21
|
return (t / 1_000).toFixed(1) + "k";
|
|
22
22
|
return String(t);
|
|
23
23
|
}
|
|
24
|
-
function formatCost(n) {
|
|
24
|
+
export function formatCost(n) {
|
|
25
25
|
if (n >= 1000)
|
|
26
26
|
return "$" + Math.round(n).toLocaleString("en-US");
|
|
27
27
|
if (n >= 100)
|
|
@@ -30,13 +30,22 @@ function formatCost(n) {
|
|
|
30
30
|
return "$" + n.toFixed(1);
|
|
31
31
|
return "$" + n.toFixed(2);
|
|
32
32
|
}
|
|
33
|
-
function ctxColor(pct) {
|
|
33
|
+
export function ctxColor(pct) {
|
|
34
34
|
if (pct >= 80)
|
|
35
35
|
return FG_RED;
|
|
36
36
|
if (pct >= 60)
|
|
37
37
|
return FG_ORANGE;
|
|
38
38
|
return FG_GREEN;
|
|
39
39
|
}
|
|
40
|
+
export function formatCountdown(resetsAtMs) {
|
|
41
|
+
const remainingMs = resetsAtMs - Date.now();
|
|
42
|
+
if (remainingMs <= 0)
|
|
43
|
+
return "~0:00";
|
|
44
|
+
const totalMinutes = Math.ceil(remainingMs / 60000);
|
|
45
|
+
const hours = Math.floor(totalMinutes / 60);
|
|
46
|
+
const minutes = totalMinutes % 60;
|
|
47
|
+
return `-${hours}:${String(minutes).padStart(2, "0")}`;
|
|
48
|
+
}
|
|
40
49
|
// ccclub rank fetcher with 120s file cache
|
|
41
50
|
function getCcclubRank() {
|
|
42
51
|
const configPath = join(homedir(), ".ccclub", "config.json");
|
|
@@ -77,7 +86,7 @@ function getCcclubRank() {
|
|
|
77
86
|
return null;
|
|
78
87
|
}
|
|
79
88
|
}
|
|
80
|
-
function rankColor(rank) {
|
|
89
|
+
export function rankColor(rank) {
|
|
81
90
|
if (rank === 1)
|
|
82
91
|
return FG_YELLOW;
|
|
83
92
|
if (rank === 2)
|
|
@@ -89,10 +98,12 @@ function rankColor(rank) {
|
|
|
89
98
|
// Claude usage fetcher with 60s file cache
|
|
90
99
|
function getClaudeUsage() {
|
|
91
100
|
const cacheFile = "/tmp/sl-claude-usage";
|
|
92
|
-
const
|
|
101
|
+
const hitFile = "/tmp/sl-claude-usage-hit";
|
|
102
|
+
const now = Date.now();
|
|
103
|
+
const nowSec = now / 1000;
|
|
93
104
|
if (existsSync(cacheFile)) {
|
|
94
105
|
const mtime = statSync(cacheFile).mtimeMs / 1000;
|
|
95
|
-
if (
|
|
106
|
+
if (nowSec - mtime <= 60) {
|
|
96
107
|
try {
|
|
97
108
|
return JSON.parse(readFileSync(cacheFile, "utf-8"));
|
|
98
109
|
}
|
|
@@ -125,7 +136,42 @@ function getClaudeUsage() {
|
|
|
125
136
|
return Math.round(parseFloat(val.replace("%", "")));
|
|
126
137
|
return 0;
|
|
127
138
|
};
|
|
128
|
-
const
|
|
139
|
+
const fiveHour = parseUtil(data.five_hour?.utilization);
|
|
140
|
+
const sevenDay = parseUtil(data.seven_day?.utilization);
|
|
141
|
+
let fiveHourResetsAt;
|
|
142
|
+
// Strategy 1: Use reset time from API if available
|
|
143
|
+
const resetsAtRaw = data.five_hour?.resets_at ?? data.five_hour?.reset_at ?? data.five_hour?.next_reset;
|
|
144
|
+
if (resetsAtRaw) {
|
|
145
|
+
const ts = typeof resetsAtRaw === "string" ? new Date(resetsAtRaw).getTime() : resetsAtRaw * 1000;
|
|
146
|
+
if (!isNaN(ts) && ts > now)
|
|
147
|
+
fiveHourResetsAt = ts;
|
|
148
|
+
}
|
|
149
|
+
// Strategy 2: Fallback - track when we first saw 100%
|
|
150
|
+
if (fiveHour >= 100) {
|
|
151
|
+
if (!fiveHourResetsAt) {
|
|
152
|
+
if (existsSync(hitFile)) {
|
|
153
|
+
const hitTime = parseFloat(readFileSync(hitFile, "utf-8").trim());
|
|
154
|
+
if (!isNaN(hitTime)) {
|
|
155
|
+
fiveHourResetsAt = hitTime + 5 * 3600 * 1000;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
writeFileSync(hitFile, String(now), "utf-8");
|
|
160
|
+
fiveHourResetsAt = now + 5 * 3600 * 1000;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
// Usage dropped below 100%, clear hit tracker
|
|
166
|
+
try {
|
|
167
|
+
if (existsSync(hitFile))
|
|
168
|
+
unlinkSync(hitFile);
|
|
169
|
+
}
|
|
170
|
+
catch { }
|
|
171
|
+
}
|
|
172
|
+
const result = { fiveHour, sevenDay };
|
|
173
|
+
if (fiveHourResetsAt)
|
|
174
|
+
result.fiveHourResetsAt = fiveHourResetsAt;
|
|
129
175
|
writeFileSync(cacheFile, JSON.stringify(result), "utf-8");
|
|
130
176
|
return result;
|
|
131
177
|
}
|
|
@@ -169,24 +215,42 @@ export function render(input) {
|
|
|
169
215
|
const cache = readCache();
|
|
170
216
|
const config = readConfig();
|
|
171
217
|
const claudeUsage = getClaudeUsage();
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
218
|
+
const g = FG_GRAY_DIM;
|
|
219
|
+
const y = FG_YELLOW;
|
|
220
|
+
const m = FG_MODEL;
|
|
221
|
+
const gr = FG_GRAY;
|
|
222
|
+
const r = RESET;
|
|
223
|
+
const cx = ctxColor(contextPct);
|
|
224
|
+
const segments = [];
|
|
225
|
+
// tokens $cost · ctx% Model
|
|
226
|
+
segments.push(`${formatTokens(totalTokens)} ${y}${formatCost(cost)}${r} ${g}·${r} ${cx}${contextPct}%${r} ${m}${model}${r}`);
|
|
227
|
+
// 5h:100% · 7d:26% · 30d:$960
|
|
228
|
+
const usageParts = [];
|
|
176
229
|
if (claudeUsage) {
|
|
177
|
-
|
|
230
|
+
if (claudeUsage.fiveHour >= 100 && claudeUsage.fiveHourResetsAt) {
|
|
231
|
+
const countdown = formatCountdown(claudeUsage.fiveHourResetsAt);
|
|
232
|
+
usageParts.push(`${FG_RED}5h:${countdown}${r}`);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
const c5 = ctxColor(claudeUsage.fiveHour);
|
|
236
|
+
usageParts.push(`${c5}5h:${claudeUsage.fiveHour}%${r}`);
|
|
237
|
+
}
|
|
238
|
+
const c7 = ctxColor(claudeUsage.sevenDay);
|
|
239
|
+
usageParts.push(`${c7}7d:${claudeUsage.sevenDay}%${r}`);
|
|
178
240
|
}
|
|
179
|
-
// 3. Period cost (default 30d, configurable)
|
|
180
241
|
if (cache) {
|
|
181
242
|
const period = config.period || "30d";
|
|
182
243
|
const periodCost = period === "7d" ? cache.cost7d : cache.cost30d;
|
|
183
|
-
|
|
244
|
+
usageParts.push(`${y}${period}:${formatCost(periodCost)}${r}`);
|
|
245
|
+
}
|
|
246
|
+
if (usageParts.length > 0) {
|
|
247
|
+
segments.push(usageParts.join(` ${g}·${r} `));
|
|
184
248
|
}
|
|
185
|
-
//
|
|
249
|
+
// #2 $53.6
|
|
186
250
|
const ccclubRank = getCcclubRank();
|
|
187
251
|
if (ccclubRank) {
|
|
188
252
|
const rc = rankColor(ccclubRank.rank);
|
|
189
|
-
|
|
253
|
+
segments.push(`${rc}#${ccclubRank.rank} ${formatCost(ccclubRank.cost)}${r}`);
|
|
190
254
|
}
|
|
191
|
-
return "
|
|
255
|
+
return " " + segments.join(` ${gr}/${r} `);
|
|
192
256
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cc-costline",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Enhanced statusline for Claude Code with cost tracking, usage limits, and leaderboard",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "tsc",
|
|
11
|
-
"dev": "tsc --watch"
|
|
11
|
+
"dev": "tsc --watch",
|
|
12
|
+
"test": "tsc && node --experimental-strip-types --no-warnings --test test/*.test.ts"
|
|
12
13
|
},
|
|
13
14
|
"files": [
|
|
14
15
|
"dist"
|