ccburn 0.1.6__tar.gz → 0.2.0__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.
Files changed (32) hide show
  1. {ccburn-0.1.6/src/ccburn.egg-info → ccburn-0.2.0}/PKG-INFO +23 -5
  2. {ccburn-0.1.6 → ccburn-0.2.0}/README.md +22 -4
  3. {ccburn-0.1.6 → ccburn-0.2.0}/pyproject.toml +1 -1
  4. {ccburn-0.1.6 → ccburn-0.2.0}/src/ccburn/data/credentials.py +66 -15
  5. {ccburn-0.1.6 → ccburn-0.2.0}/src/ccburn/display/gauges.py +2 -8
  6. {ccburn-0.1.6 → ccburn-0.2.0}/src/ccburn/utils/formatting.py +2 -1
  7. {ccburn-0.1.6 → ccburn-0.2.0/src/ccburn.egg-info}/PKG-INFO +23 -5
  8. {ccburn-0.1.6 → ccburn-0.2.0}/tests/test_cli.py +3 -1
  9. {ccburn-0.1.6 → ccburn-0.2.0}/LICENSE +0 -0
  10. {ccburn-0.1.6 → ccburn-0.2.0}/setup.cfg +0 -0
  11. {ccburn-0.1.6 → ccburn-0.2.0}/src/ccburn/__init__.py +0 -0
  12. {ccburn-0.1.6 → ccburn-0.2.0}/src/ccburn/app.py +0 -0
  13. {ccburn-0.1.6 → ccburn-0.2.0}/src/ccburn/cli.py +0 -0
  14. {ccburn-0.1.6 → ccburn-0.2.0}/src/ccburn/data/__init__.py +0 -0
  15. {ccburn-0.1.6 → ccburn-0.2.0}/src/ccburn/data/history.py +0 -0
  16. {ccburn-0.1.6 → ccburn-0.2.0}/src/ccburn/data/models.py +0 -0
  17. {ccburn-0.1.6 → ccburn-0.2.0}/src/ccburn/data/usage_client.py +0 -0
  18. {ccburn-0.1.6 → ccburn-0.2.0}/src/ccburn/display/__init__.py +0 -0
  19. {ccburn-0.1.6 → ccburn-0.2.0}/src/ccburn/display/chart.py +0 -0
  20. {ccburn-0.1.6 → ccburn-0.2.0}/src/ccburn/display/layout.py +0 -0
  21. {ccburn-0.1.6 → ccburn-0.2.0}/src/ccburn/main.py +0 -0
  22. {ccburn-0.1.6 → ccburn-0.2.0}/src/ccburn/utils/__init__.py +0 -0
  23. {ccburn-0.1.6 → ccburn-0.2.0}/src/ccburn/utils/calculator.py +0 -0
  24. {ccburn-0.1.6 → ccburn-0.2.0}/src/ccburn.egg-info/SOURCES.txt +0 -0
  25. {ccburn-0.1.6 → ccburn-0.2.0}/src/ccburn.egg-info/dependency_links.txt +0 -0
  26. {ccburn-0.1.6 → ccburn-0.2.0}/src/ccburn.egg-info/entry_points.txt +0 -0
  27. {ccburn-0.1.6 → ccburn-0.2.0}/src/ccburn.egg-info/requires.txt +0 -0
  28. {ccburn-0.1.6 → ccburn-0.2.0}/src/ccburn.egg-info/top_level.txt +0 -0
  29. {ccburn-0.1.6 → ccburn-0.2.0}/tests/test_calculator.py +0 -0
  30. {ccburn-0.1.6 → ccburn-0.2.0}/tests/test_formatting.py +0 -0
  31. {ccburn-0.1.6 → ccburn-0.2.0}/tests/test_history.py +0 -0
  32. {ccburn-0.1.6 → ccburn-0.2.0}/tests/test_models.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ccburn
3
- Version: 0.1.6
3
+ Version: 0.2.0
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
@@ -41,11 +41,12 @@ Dynamic: license-file
41
41
 
42
42
  [![CI](https://img.shields.io/github/actions/workflow/status/JuanjoFuchs/ccburn/ci.yml?branch=main&label=CI)](https://github.com/JuanjoFuchs/ccburn/actions/workflows/ci.yml)
43
43
  [![Release](https://img.shields.io/github/actions/workflow/status/JuanjoFuchs/ccburn/release.yml?label=Release)](https://github.com/JuanjoFuchs/ccburn/actions/workflows/release.yml)
44
+ [![npm](https://img.shields.io/npm/v/ccburn)](https://www.npmjs.com/package/ccburn)
44
45
  [![PyPI](https://img.shields.io/pypi/v/ccburn)](https://pypi.org/project/ccburn/)
45
46
  [![Python](https://img.shields.io/pypi/pyversions/ccburn)](https://pypi.org/project/ccburn/)
46
47
  [![GitHub Release](https://img.shields.io/github/v/release/JuanjoFuchs/ccburn)](https://github.com/JuanjoFuchs/ccburn/releases)
47
48
  [![WinGet](https://img.shields.io/badge/WinGet-pending-yellow)](https://github.com/microsoft/winget-pkgs/pulls?q=is%3Apr+ccburn)
48
- [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
49
+ [![License](https://img.shields.io/github/license/JuanjoFuchs/ccburn)](LICENSE)
49
50
 
50
51
  <p align="center">
51
52
  <img src="docs/cash1.png" alt="Burning tokens" width="140">
@@ -70,13 +71,27 @@ TUI and CLI for Claude Code usage limits — burn-up charts, compact mode for st
70
71
 
71
72
  ## Installation
72
73
 
73
- ### Windows (WinGet) *pending approval*
74
+ Run `claude` and login first to refresh credentials.
75
+
76
+ ### WinGet (*pending approval*)
74
77
 
75
78
  ```powershell
76
79
  winget install JuanjoFuchs.ccburn
77
80
  ```
78
81
 
79
- ### Cross-Platform (pip)
82
+ ### npx
83
+
84
+ ```bash
85
+ npx ccburn
86
+ ```
87
+
88
+ ### npm
89
+
90
+ ```bash
91
+ npm install -g ccburn
92
+ ```
93
+
94
+ ### pip
80
95
 
81
96
  ```bash
82
97
  pip install ccburn
@@ -92,7 +107,10 @@ pip install -e ".[dev]"
92
107
 
93
108
  ## Quick Start
94
109
 
95
- 1. **Ensure Claude Code is installed** ccburn reads credentials from Claude Code's config
110
+ 1. **Run Claude Code first** to ensure credentials are fresh:
111
+ ```bash
112
+ claude
113
+ ```
96
114
  2. **Run ccburn:**
97
115
  ```bash
98
116
  ccburn # Session limit (default)
@@ -2,11 +2,12 @@
2
2
 
3
3
  [![CI](https://img.shields.io/github/actions/workflow/status/JuanjoFuchs/ccburn/ci.yml?branch=main&label=CI)](https://github.com/JuanjoFuchs/ccburn/actions/workflows/ci.yml)
4
4
  [![Release](https://img.shields.io/github/actions/workflow/status/JuanjoFuchs/ccburn/release.yml?label=Release)](https://github.com/JuanjoFuchs/ccburn/actions/workflows/release.yml)
5
+ [![npm](https://img.shields.io/npm/v/ccburn)](https://www.npmjs.com/package/ccburn)
5
6
  [![PyPI](https://img.shields.io/pypi/v/ccburn)](https://pypi.org/project/ccburn/)
6
7
  [![Python](https://img.shields.io/pypi/pyversions/ccburn)](https://pypi.org/project/ccburn/)
7
8
  [![GitHub Release](https://img.shields.io/github/v/release/JuanjoFuchs/ccburn)](https://github.com/JuanjoFuchs/ccburn/releases)
8
9
  [![WinGet](https://img.shields.io/badge/WinGet-pending-yellow)](https://github.com/microsoft/winget-pkgs/pulls?q=is%3Apr+ccburn)
9
- [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
10
+ [![License](https://img.shields.io/github/license/JuanjoFuchs/ccburn)](LICENSE)
10
11
 
11
12
  <p align="center">
12
13
  <img src="docs/cash1.png" alt="Burning tokens" width="140">
@@ -31,13 +32,27 @@ TUI and CLI for Claude Code usage limits — burn-up charts, compact mode for st
31
32
 
32
33
  ## Installation
33
34
 
34
- ### Windows (WinGet) *pending approval*
35
+ Run `claude` and login first to refresh credentials.
36
+
37
+ ### WinGet (*pending approval*)
35
38
 
36
39
  ```powershell
37
40
  winget install JuanjoFuchs.ccburn
38
41
  ```
39
42
 
40
- ### Cross-Platform (pip)
43
+ ### npx
44
+
45
+ ```bash
46
+ npx ccburn
47
+ ```
48
+
49
+ ### npm
50
+
51
+ ```bash
52
+ npm install -g ccburn
53
+ ```
54
+
55
+ ### pip
41
56
 
42
57
  ```bash
43
58
  pip install ccburn
@@ -53,7 +68,10 @@ pip install -e ".[dev]"
53
68
 
54
69
  ## Quick Start
55
70
 
56
- 1. **Ensure Claude Code is installed** ccburn reads credentials from Claude Code's config
71
+ 1. **Run Claude Code first** to ensure credentials are fresh:
72
+ ```bash
73
+ claude
74
+ ```
57
75
  2. **Run ccburn:**
58
76
  ```bash
59
77
  ccburn # Session limit (default)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ccburn"
7
- version = "0.1.6"
7
+ version = "0.2.0"
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"
@@ -2,6 +2,8 @@
2
2
 
3
3
  import json
4
4
  import os
5
+ import platform
6
+ import subprocess
5
7
  from datetime import datetime, timezone
6
8
  from pathlib import Path
7
9
 
@@ -44,32 +46,81 @@ def get_credentials_path() -> Path:
44
46
  return Path.home() / ".claude" / ".credentials.json"
45
47
 
46
48
 
47
- def read_credentials() -> dict:
48
- """Read the raw credentials file.
49
+ def _read_credentials_from_keychain() -> dict | None:
50
+ """Read credentials from macOS Keychain.
49
51
 
50
52
  Returns:
51
- Parsed credentials dictionary
53
+ Parsed credentials dictionary or None if not found/failed.
54
+ """
55
+ try:
56
+ result = subprocess.run(
57
+ ["security", "find-generic-password", "-s", "Claude Code-credentials", "-w"],
58
+ capture_output=True,
59
+ text=True,
60
+ check=True,
61
+ )
62
+ return json.loads(result.stdout.strip())
63
+ except (subprocess.CalledProcessError, json.JSONDecodeError, FileNotFoundError):
64
+ return None
52
65
 
53
- Raises:
54
- CredentialsNotFoundError: If file doesn't exist
55
- InvalidCredentialsError: If file is malformed
66
+
67
+ def _read_credentials_from_file() -> dict | None:
68
+ """Read credentials from file.
69
+
70
+ Returns:
71
+ Parsed credentials dictionary or None if not found/failed.
56
72
  """
57
73
  creds_path = get_credentials_path()
58
74
 
59
75
  if not creds_path.exists():
60
- raise CredentialsNotFoundError(
61
- f"Credentials file not found at {creds_path}\n"
62
- "Please ensure Claude Code is installed and you are logged in.\n"
63
- "Run 'claude' to log in if needed."
64
- )
76
+ return None
65
77
 
66
78
  try:
67
79
  with open(creds_path) as f:
68
80
  return json.load(f)
69
- except json.JSONDecodeError as e:
70
- raise InvalidCredentialsError(f"Invalid JSON in credentials file: {e}") from e
71
- except PermissionError as e:
72
- raise CredentialsError(f"Permission denied reading {creds_path}") from e
81
+ except (json.JSONDecodeError, PermissionError):
82
+ return None
83
+
84
+
85
+ def read_credentials() -> dict:
86
+ """Read credentials from the appropriate storage.
87
+
88
+ On macOS, tries Keychain first, then falls back to file.
89
+ On Linux/Windows, reads from file.
90
+
91
+ Returns:
92
+ Parsed credentials dictionary
93
+
94
+ Raises:
95
+ CredentialsNotFoundError: If credentials not found
96
+ InvalidCredentialsError: If credentials are malformed
97
+ """
98
+ credentials = None
99
+
100
+ # On macOS, try Keychain first
101
+ if platform.system() == "Darwin":
102
+ credentials = _read_credentials_from_keychain()
103
+
104
+ # Fall back to file (or use file on non-macOS)
105
+ if credentials is None:
106
+ credentials = _read_credentials_from_file()
107
+
108
+ if credentials is None:
109
+ creds_path = get_credentials_path()
110
+ if platform.system() == "Darwin":
111
+ raise CredentialsNotFoundError(
112
+ "Credentials not found in macOS Keychain or file.\n"
113
+ "Please ensure Claude Code is installed and you are logged in.\n"
114
+ "Run 'claude' to log in if needed."
115
+ )
116
+ else:
117
+ raise CredentialsNotFoundError(
118
+ f"Credentials file not found at {creds_path}\n"
119
+ "Please ensure Claude Code is installed and you are logged in.\n"
120
+ "Run 'claude' to log in if needed."
121
+ )
122
+
123
+ return credentials
73
124
 
74
125
 
75
126
  def check_token_expired(credentials: dict) -> bool:
@@ -120,15 +120,9 @@ def create_gauge_section(
120
120
  complete_style=Style(color=usage_color), # Bright color for filled
121
121
  )
122
122
 
123
- # Usage label with emoji based on status
124
- usage_emoji = "📊"
125
- if limit_data.utilization >= 0.9:
126
- usage_emoji = "🚨"
127
- elif limit_data.utilization >= 0.75:
128
- usage_emoji = "⚠️"
129
-
123
+ # Usage label - keep emoji consistent (⚠️ has inconsistent width across terminals)
130
124
  usage_label = Text()
131
- usage_label.append(f"{usage_emoji} ", style="")
125
+ usage_label.append("📊 ", style="")
132
126
  usage_label.append("Usage", style=f"bold {usage_color}")
133
127
 
134
128
  table.add_row(
@@ -82,7 +82,8 @@ def format_reset_time(resets_at: datetime, now: datetime | None = None) -> str:
82
82
  # Convert to local time for display
83
83
  local_time = resets_at.astimezone()
84
84
  day_name = local_time.strftime("%a") # "Tue"
85
- time_str = local_time.strftime("%-I:%M %p") # "4:00 PM"
85
+ # Use %I and strip leading zero (%-I is Unix-only, %#I is Windows-only)
86
+ time_str = local_time.strftime("%I:%M %p").lstrip("0") # "4:00 PM"
86
87
  return f"Resets {day_name} {time_str}"
87
88
 
88
89
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ccburn
3
- Version: 0.1.6
3
+ Version: 0.2.0
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
@@ -41,11 +41,12 @@ Dynamic: license-file
41
41
 
42
42
  [![CI](https://img.shields.io/github/actions/workflow/status/JuanjoFuchs/ccburn/ci.yml?branch=main&label=CI)](https://github.com/JuanjoFuchs/ccburn/actions/workflows/ci.yml)
43
43
  [![Release](https://img.shields.io/github/actions/workflow/status/JuanjoFuchs/ccburn/release.yml?label=Release)](https://github.com/JuanjoFuchs/ccburn/actions/workflows/release.yml)
44
+ [![npm](https://img.shields.io/npm/v/ccburn)](https://www.npmjs.com/package/ccburn)
44
45
  [![PyPI](https://img.shields.io/pypi/v/ccburn)](https://pypi.org/project/ccburn/)
45
46
  [![Python](https://img.shields.io/pypi/pyversions/ccburn)](https://pypi.org/project/ccburn/)
46
47
  [![GitHub Release](https://img.shields.io/github/v/release/JuanjoFuchs/ccburn)](https://github.com/JuanjoFuchs/ccburn/releases)
47
48
  [![WinGet](https://img.shields.io/badge/WinGet-pending-yellow)](https://github.com/microsoft/winget-pkgs/pulls?q=is%3Apr+ccburn)
48
- [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
49
+ [![License](https://img.shields.io/github/license/JuanjoFuchs/ccburn)](LICENSE)
49
50
 
50
51
  <p align="center">
51
52
  <img src="docs/cash1.png" alt="Burning tokens" width="140">
@@ -70,13 +71,27 @@ TUI and CLI for Claude Code usage limits — burn-up charts, compact mode for st
70
71
 
71
72
  ## Installation
72
73
 
73
- ### Windows (WinGet) *pending approval*
74
+ Run `claude` and login first to refresh credentials.
75
+
76
+ ### WinGet (*pending approval*)
74
77
 
75
78
  ```powershell
76
79
  winget install JuanjoFuchs.ccburn
77
80
  ```
78
81
 
79
- ### Cross-Platform (pip)
82
+ ### npx
83
+
84
+ ```bash
85
+ npx ccburn
86
+ ```
87
+
88
+ ### npm
89
+
90
+ ```bash
91
+ npm install -g ccburn
92
+ ```
93
+
94
+ ### pip
80
95
 
81
96
  ```bash
82
97
  pip install ccburn
@@ -92,7 +107,10 @@ pip install -e ".[dev]"
92
107
 
93
108
  ## Quick Start
94
109
 
95
- 1. **Ensure Claude Code is installed** ccburn reads credentials from Claude Code's config
110
+ 1. **Run Claude Code first** to ensure credentials are fresh:
111
+ ```bash
112
+ claude
113
+ ```
96
114
  2. **Run ccburn:**
97
115
  ```bash
98
116
  ccburn # Session limit (default)
@@ -51,10 +51,12 @@ class TestCLI:
51
51
  """Tests for CLI commands."""
52
52
 
53
53
  def test_version(self):
54
+ import re
54
55
  result = runner.invoke(app, ["--version"])
55
56
  assert result.exit_code == 0
56
57
  assert "ccburn" in result.stdout
57
- assert "0.1.1" in result.stdout
58
+ # Check for semantic version pattern (e.g., 0.1.7)
59
+ assert re.search(r"\d+\.\d+\.\d+", result.stdout)
58
60
 
59
61
  def test_help(self):
60
62
  result = runner.invoke(app, ["--help"])
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