ccburn 0.2.0__tar.gz → 0.2.2__tar.gz
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.
- {ccburn-0.2.0/src/ccburn.egg-info → ccburn-0.2.2}/PKG-INFO +4 -1
- {ccburn-0.2.0 → ccburn-0.2.2}/README.md +3 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/pyproject.toml +1 -1
- {ccburn-0.2.0 → ccburn-0.2.2}/src/ccburn/display/chart.py +21 -12
- {ccburn-0.2.0 → ccburn-0.2.2}/src/ccburn/display/gauges.py +31 -7
- {ccburn-0.2.0 → ccburn-0.2.2}/src/ccburn/utils/formatting.py +32 -11
- {ccburn-0.2.0 → ccburn-0.2.2/src/ccburn.egg-info}/PKG-INFO +4 -1
- {ccburn-0.2.0 → ccburn-0.2.2}/LICENSE +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/setup.cfg +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/src/ccburn/__init__.py +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/src/ccburn/app.py +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/src/ccburn/cli.py +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/src/ccburn/data/__init__.py +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/src/ccburn/data/credentials.py +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/src/ccburn/data/history.py +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/src/ccburn/data/models.py +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/src/ccburn/data/usage_client.py +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/src/ccburn/display/__init__.py +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/src/ccburn/display/layout.py +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/src/ccburn/main.py +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/src/ccburn/utils/__init__.py +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/src/ccburn/utils/calculator.py +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/src/ccburn.egg-info/SOURCES.txt +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/src/ccburn.egg-info/dependency_links.txt +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/src/ccburn.egg-info/entry_points.txt +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/src/ccburn.egg-info/requires.txt +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/src/ccburn.egg-info/top_level.txt +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/tests/test_calculator.py +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/tests/test_cli.py +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/tests/test_formatting.py +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/tests/test_history.py +0 -0
- {ccburn-0.2.0 → ccburn-0.2.2}/tests/test_models.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ccburn
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Terminal-based Claude Code usage limit visualizer with real-time burn-up charts
|
|
5
5
|
Author: JuanjoFuchs
|
|
6
6
|
License-Expression: MIT
|
|
@@ -46,6 +46,9 @@ Dynamic: license-file
|
|
|
46
46
|
[](https://pypi.org/project/ccburn/)
|
|
47
47
|
[](https://github.com/JuanjoFuchs/ccburn/releases)
|
|
48
48
|
[](https://github.com/microsoft/winget-pkgs/pulls?q=is%3Apr+ccburn)
|
|
49
|
+
[](https://www.npmjs.com/package/ccburn)
|
|
50
|
+
[](https://pepy.tech/project/ccburn)
|
|
51
|
+
[](https://github.com/JuanjoFuchs/ccburn/releases)
|
|
49
52
|
[](LICENSE)
|
|
50
53
|
|
|
51
54
|
<p align="center">
|
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
[](https://pypi.org/project/ccburn/)
|
|
8
8
|
[](https://github.com/JuanjoFuchs/ccburn/releases)
|
|
9
9
|
[](https://github.com/microsoft/winget-pkgs/pulls?q=is%3Apr+ccburn)
|
|
10
|
+
[](https://www.npmjs.com/package/ccburn)
|
|
11
|
+
[](https://pepy.tech/project/ccburn)
|
|
12
|
+
[](https://github.com/JuanjoFuchs/ccburn/releases)
|
|
10
13
|
[](LICENSE)
|
|
11
14
|
|
|
12
15
|
<p align="center">
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ccburn"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "Terminal-based Claude Code usage limit visualizer with real-time burn-up charts"
|
|
9
9
|
authors = [{name = "JuanjoFuchs"}]
|
|
10
10
|
readme = "README.md"
|
|
@@ -10,8 +10,10 @@ from rich.jupyter import JupyterMixin
|
|
|
10
10
|
|
|
11
11
|
try:
|
|
12
12
|
from ..data.models import LimitData, UsageSnapshot
|
|
13
|
+
from ..utils.formatting import get_utilization_color
|
|
13
14
|
except ImportError:
|
|
14
15
|
from ccburn.data.models import LimitData, UsageSnapshot
|
|
16
|
+
from ccburn.utils.formatting import get_utilization_color
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
class BurnupChart(JupyterMixin):
|
|
@@ -143,8 +145,11 @@ class BurnupChart(JupyterMixin):
|
|
|
143
145
|
values.append(util_pct)
|
|
144
146
|
|
|
145
147
|
if times:
|
|
146
|
-
#
|
|
147
|
-
|
|
148
|
+
# Calculate budget pace for color determination
|
|
149
|
+
elapsed_hours = (now - original_window_start).total_seconds() / 3600
|
|
150
|
+
budget_pace = min(elapsed_hours / original_window_hours, 1.0)
|
|
151
|
+
# Determine line color based on utilization AND burn rate
|
|
152
|
+
color = self._get_plotext_color(self.limit_data.utilization, budget_pace)
|
|
148
153
|
# Use fillx=True for area chart effect (fills down to x-axis)
|
|
149
154
|
plt.plot(
|
|
150
155
|
times,
|
|
@@ -258,23 +263,27 @@ class BurnupChart(JupyterMixin):
|
|
|
258
263
|
|
|
259
264
|
return plt.build()
|
|
260
265
|
|
|
261
|
-
def _get_plotext_color(
|
|
262
|
-
|
|
266
|
+
def _get_plotext_color(
|
|
267
|
+
self, utilization: float, budget_pace: float = 0.0
|
|
268
|
+
) -> tuple[int, int, int]:
|
|
269
|
+
"""Get plotext RGB color based on utilization and burn rate.
|
|
263
270
|
|
|
264
271
|
Args:
|
|
265
272
|
utilization: Current utilization (0-1)
|
|
273
|
+
budget_pace: How much of window has elapsed (0-1)
|
|
266
274
|
|
|
267
275
|
Returns:
|
|
268
276
|
RGB tuple for plotext - bright vivid colors matching Rich progress bars
|
|
269
277
|
"""
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
+
# Reuse shared color logic, map Rich color names to RGB
|
|
279
|
+
color_name = get_utilization_color(utilization, budget_pace)
|
|
280
|
+
color_map = {
|
|
281
|
+
"green": (0, 255, 0),
|
|
282
|
+
"yellow": (255, 255, 0),
|
|
283
|
+
"bright_red": (255, 165, 0), # Orange
|
|
284
|
+
"red": (255, 0, 0),
|
|
285
|
+
}
|
|
286
|
+
return color_map.get(color_name, (255, 255, 0))
|
|
278
287
|
|
|
279
288
|
|
|
280
289
|
def create_simple_chart(
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Progress bar gauges for ccburn TUI."""
|
|
2
2
|
|
|
3
|
+
import sys
|
|
4
|
+
|
|
3
5
|
from rich.progress import ProgressBar
|
|
4
6
|
from rich.style import Style
|
|
5
7
|
from rich.table import Table
|
|
@@ -15,26 +17,48 @@ except ImportError:
|
|
|
15
17
|
from ccburn.utils.formatting import format_reset_time, get_utilization_color
|
|
16
18
|
|
|
17
19
|
|
|
18
|
-
def
|
|
20
|
+
def _supports_emoji() -> bool:
|
|
21
|
+
"""Detect if the console supports emoji characters.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
True if emoji are likely supported, False otherwise.
|
|
25
|
+
"""
|
|
26
|
+
# Check if stdout encoding supports emoji
|
|
27
|
+
try:
|
|
28
|
+
encoding = getattr(sys.stdout, "encoding", None) or ""
|
|
29
|
+
if encoding.lower() in ("utf-8", "utf8"):
|
|
30
|
+
return True
|
|
31
|
+
# Try to encode an emoji to test
|
|
32
|
+
"🔥".encode(encoding)
|
|
33
|
+
return True
|
|
34
|
+
except (UnicodeEncodeError, LookupError):
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_pace_emoji(utilization: float, budget_pace: float, ascii_fallback: bool = False) -> str:
|
|
19
39
|
"""Get emoji indicator based on utilization vs budget pace.
|
|
20
40
|
|
|
21
41
|
Args:
|
|
22
42
|
utilization: Current utilization (0-1)
|
|
23
43
|
budget_pace: Expected budget pace (0-1)
|
|
44
|
+
ascii_fallback: If True, use ASCII characters instead of emoji
|
|
24
45
|
|
|
25
46
|
Returns:
|
|
26
47
|
Emoji: 🧊 (behind), 🔥 (on pace), 🚨 (ahead)
|
|
48
|
+
ASCII: [_] (behind), [=] (on pace), [!] (ahead)
|
|
27
49
|
"""
|
|
50
|
+
use_ascii = ascii_fallback or not _supports_emoji()
|
|
51
|
+
|
|
28
52
|
if budget_pace == 0:
|
|
29
|
-
return "🔥"
|
|
53
|
+
return "[=]" if use_ascii else "🔥"
|
|
30
54
|
|
|
31
55
|
ratio = utilization / budget_pace
|
|
32
56
|
if ratio < 0.85:
|
|
33
|
-
return "🧊" # Behind pace - ice cold, under budget
|
|
57
|
+
return "[_]" if use_ascii else "🧊" # Behind pace - ice cold, under budget
|
|
34
58
|
elif ratio > 1.15:
|
|
35
|
-
return "🚨" # Ahead of pace - alarm!
|
|
59
|
+
return "[!]" if use_ascii else "🚨" # Ahead of pace - alarm!
|
|
36
60
|
else:
|
|
37
|
-
return "🔥" # On pace - normal burn
|
|
61
|
+
return "[=]" if use_ascii else "🔥" # On pace - normal burn
|
|
38
62
|
|
|
39
63
|
|
|
40
64
|
def create_header(limit_type: LimitType, limit_data: LimitData | None) -> Table:
|
|
@@ -110,9 +134,9 @@ def create_gauge_section(
|
|
|
110
134
|
utilization_percent = limit_data.utilization * 100
|
|
111
135
|
pace_percent = budget_pace * 100
|
|
112
136
|
|
|
113
|
-
# Usage bar - color by threshold
|
|
137
|
+
# Usage bar - color by threshold AND burn rate
|
|
114
138
|
# complete_style = filled portion (bright), style = unfilled portion (dim)
|
|
115
|
-
usage_color = get_utilization_color(limit_data.utilization)
|
|
139
|
+
usage_color = get_utilization_color(limit_data.utilization, budget_pace)
|
|
116
140
|
usage_bar = ProgressBar(
|
|
117
141
|
total=100,
|
|
118
142
|
completed=utilization_percent,
|
|
@@ -87,24 +87,45 @@ def format_reset_time(resets_at: datetime, now: datetime | None = None) -> str:
|
|
|
87
87
|
return f"Resets {day_name} {time_str}"
|
|
88
88
|
|
|
89
89
|
|
|
90
|
-
def get_utilization_color(utilization: float) -> str:
|
|
91
|
-
"""Get color based on utilization
|
|
90
|
+
def get_utilization_color(utilization: float, budget_pace: float = 0.0) -> str:
|
|
91
|
+
"""Get color based on utilization and burn rate.
|
|
92
92
|
|
|
93
93
|
Args:
|
|
94
|
-
utilization:
|
|
94
|
+
utilization: Current usage (0-1)
|
|
95
|
+
budget_pace: How much of window has elapsed (0-1)
|
|
95
96
|
|
|
96
97
|
Returns:
|
|
97
|
-
Color name: "green", "yellow", "
|
|
98
|
+
Color name: "green", "yellow", "bright_red", or "red"
|
|
98
99
|
"""
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
elif utilization < 0.75:
|
|
102
|
-
return "yellow"
|
|
103
|
-
elif utilization < 0.9:
|
|
104
|
-
return "bright_red" # Rich uses "bright_red" for orange-like
|
|
105
|
-
else:
|
|
100
|
+
# Critical: always red at very high utilization
|
|
101
|
+
if utilization >= 0.9:
|
|
106
102
|
return "red"
|
|
107
103
|
|
|
104
|
+
# Calculate burn ratio if we have meaningful data
|
|
105
|
+
burn_ratio = 1.0
|
|
106
|
+
if budget_pace >= 0.05 and utilization >= 0.01:
|
|
107
|
+
burn_ratio = utilization / budget_pace
|
|
108
|
+
|
|
109
|
+
# High utilization: at least orange, red if also burning fast
|
|
110
|
+
if utilization >= 0.75:
|
|
111
|
+
return "red" if burn_ratio > 1.5 else "bright_red"
|
|
112
|
+
|
|
113
|
+
# Moderate utilization: color based on burn rate
|
|
114
|
+
if utilization >= 0.5:
|
|
115
|
+
if burn_ratio > 2.0:
|
|
116
|
+
return "red"
|
|
117
|
+
if burn_ratio > 1.5:
|
|
118
|
+
return "bright_red"
|
|
119
|
+
return "yellow"
|
|
120
|
+
|
|
121
|
+
# Low utilization: escalate only if burning very fast
|
|
122
|
+
if burn_ratio > 3.0:
|
|
123
|
+
return "bright_red" # Will hit limit at ~33% of window
|
|
124
|
+
if burn_ratio > 2.0:
|
|
125
|
+
return "yellow" # Will hit limit at ~50% of window
|
|
126
|
+
|
|
127
|
+
return "green"
|
|
128
|
+
|
|
108
129
|
|
|
109
130
|
def get_status_indicator(utilization: float, budget_pace: float) -> str:
|
|
110
131
|
"""Get status indicator for compact output.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ccburn
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Terminal-based Claude Code usage limit visualizer with real-time burn-up charts
|
|
5
5
|
Author: JuanjoFuchs
|
|
6
6
|
License-Expression: MIT
|
|
@@ -46,6 +46,9 @@ Dynamic: license-file
|
|
|
46
46
|
[](https://pypi.org/project/ccburn/)
|
|
47
47
|
[](https://github.com/JuanjoFuchs/ccburn/releases)
|
|
48
48
|
[](https://github.com/microsoft/winget-pkgs/pulls?q=is%3Apr+ccburn)
|
|
49
|
+
[](https://www.npmjs.com/package/ccburn)
|
|
50
|
+
[](https://pepy.tech/project/ccburn)
|
|
51
|
+
[](https://github.com/JuanjoFuchs/ccburn/releases)
|
|
49
52
|
[](LICENSE)
|
|
50
53
|
|
|
51
54
|
<p align="center">
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|