cc-context-stats 1.3.0 → 1.5.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 (42) hide show
  1. package/.claude/settings.local.json +36 -1
  2. package/.github/workflows/ci.yml +4 -2
  3. package/.github/workflows/release.yml +3 -1
  4. package/CHANGELOG.md +17 -0
  5. package/README.md +33 -11
  6. package/RELEASE_NOTES.md +10 -0
  7. package/assets/logo/favicon.svg +19 -0
  8. package/assets/logo/logo-black.svg +24 -0
  9. package/assets/logo/logo-full.svg +40 -0
  10. package/assets/logo/logo-icon.svg +27 -0
  11. package/assets/logo/logo-mark.svg +28 -0
  12. package/assets/logo/logo-white.svg +24 -0
  13. package/assets/logo/logo-wordmark.svg +6 -0
  14. package/install.sh +21 -2
  15. package/package.json +7 -3
  16. package/pyproject.toml +6 -4
  17. package/scripts/context-stats.sh +194 -26
  18. package/scripts/statusline-full.sh +65 -2
  19. package/scripts/statusline-git.sh +57 -1
  20. package/scripts/statusline-minimal.sh +57 -1
  21. package/scripts/statusline.js +51 -4
  22. package/scripts/statusline.py +57 -3
  23. package/src/claude_statusline/__init__.py +1 -1
  24. package/src/claude_statusline/cli/context_stats.py +106 -34
  25. package/src/claude_statusline/cli/statusline.py +5 -4
  26. package/src/claude_statusline/core/config.py +7 -0
  27. package/src/claude_statusline/formatters/layout.py +67 -0
  28. package/src/claude_statusline/graphs/renderer.py +44 -24
  29. package/src/claude_statusline/graphs/statistics.py +34 -0
  30. package/src/claude_statusline/ui/__init__.py +1 -0
  31. package/src/claude_statusline/ui/icons.py +93 -0
  32. package/src/claude_statusline/ui/waiting.py +62 -0
  33. package/tests/bash/test_statusline_full.bats +30 -0
  34. package/tests/node/statusline.test.js +44 -3
  35. package/tests/python/test_icons.py +152 -0
  36. package/tests/python/test_layout.py +127 -0
  37. package/tests/python/test_statusline.py +64 -3
  38. package/tests/python/test_waiting.py +127 -0
  39. package/PUBLISHING_GUIDE.md +0 -69
  40. package/npm-publish.sh +0 -33
  41. package/publish.sh +0 -24
  42. package/show_raw_claude_code_api.js +0 -11
@@ -0,0 +1,127 @@
1
+ """Tests for rotating waiting text and activity detection."""
2
+
3
+ import time
4
+
5
+ from claude_statusline.core.state import StateEntry
6
+ from claude_statusline.ui.waiting import (
7
+ STATIC_MESSAGE,
8
+ WAITING_MESSAGES,
9
+ get_waiting_text,
10
+ is_active,
11
+ )
12
+
13
+
14
+ def _make_entry(timestamp: int = 0) -> StateEntry:
15
+ """Helper to create a StateEntry for testing."""
16
+ return StateEntry(
17
+ timestamp=timestamp,
18
+ total_input_tokens=1000,
19
+ total_output_tokens=0,
20
+ current_input_tokens=1000,
21
+ current_output_tokens=0,
22
+ cache_creation=0,
23
+ cache_read=0,
24
+ cost_usd=0.0,
25
+ lines_added=0,
26
+ lines_removed=0,
27
+ session_id="test",
28
+ model_id="test-model",
29
+ workspace_project_dir="/tmp/test",
30
+ context_window_size=200_000,
31
+ )
32
+
33
+
34
+ class TestGetWaitingText:
35
+ """Tests for rotating waiting text."""
36
+
37
+ def test_returns_string(self):
38
+ text = get_waiting_text(0)
39
+ assert isinstance(text, str)
40
+ assert len(text) > 0
41
+
42
+ def test_rotates_every_two_cycles(self):
43
+ """Messages should change every 2 cycles."""
44
+ text_0 = get_waiting_text(0)
45
+ text_1 = get_waiting_text(1)
46
+ text_2 = get_waiting_text(2)
47
+
48
+ # Cycle 0 and 1 should be the same (same message index)
49
+ assert text_0 == text_1
50
+ # Cycle 2 should be different (next message)
51
+ assert text_0 != text_2
52
+
53
+ def test_wraps_around(self):
54
+ """Should cycle back to the first message."""
55
+ total_messages = len(WAITING_MESSAGES)
56
+ # After going through all messages (2 cycles each), should wrap
57
+ first = get_waiting_text(0)
58
+ wrapped = get_waiting_text(total_messages * 2)
59
+ assert first == wrapped
60
+
61
+ def test_all_messages_reachable(self):
62
+ """Every message should appear at some cycle."""
63
+ seen = set()
64
+ for i in range(len(WAITING_MESSAGES) * 2):
65
+ seen.add(get_waiting_text(i))
66
+ assert seen == set(WAITING_MESSAGES)
67
+
68
+ def test_reduced_motion_returns_static(self):
69
+ """With reduced_motion=True, always return static message."""
70
+ for i in range(10):
71
+ text = get_waiting_text(i, reduced_motion=True)
72
+ assert text == STATIC_MESSAGE
73
+
74
+ def test_reduced_motion_consistent(self):
75
+ """Static message should be the same regardless of cycle."""
76
+ texts = {get_waiting_text(i, reduced_motion=True) for i in range(20)}
77
+ assert len(texts) == 1
78
+
79
+
80
+ class TestIsActive:
81
+ """Tests for session activity detection."""
82
+
83
+ def test_empty_entries(self):
84
+ assert is_active([]) is False
85
+
86
+ def test_recent_entry_is_active(self):
87
+ """Entry within timeout is active."""
88
+ entry = _make_entry(timestamp=int(time.time()) - 5)
89
+ assert is_active([entry]) is True
90
+
91
+ def test_old_entry_is_not_active(self):
92
+ """Entry older than timeout is not active."""
93
+ entry = _make_entry(timestamp=int(time.time()) - 60)
94
+ assert is_active([entry]) is False
95
+
96
+ def test_exactly_at_timeout_is_active(self):
97
+ """Entry exactly at timeout boundary is still active."""
98
+ entry = _make_entry(timestamp=int(time.time()) - 30)
99
+ assert is_active([entry], timeout=30) is True
100
+
101
+ def test_just_past_timeout_is_not_active(self):
102
+ """Entry just past timeout boundary is not active."""
103
+ entry = _make_entry(timestamp=int(time.time()) - 31)
104
+ assert is_active([entry], timeout=30) is False
105
+
106
+ def test_custom_timeout(self):
107
+ """Custom timeout should be respected."""
108
+ entry = _make_entry(timestamp=int(time.time()) - 10)
109
+ assert is_active([entry], timeout=5) is False
110
+ assert is_active([entry], timeout=15) is True
111
+
112
+ def test_uses_last_entry(self):
113
+ """Should check the most recent (last) entry, not the first."""
114
+ old_entry = _make_entry(timestamp=int(time.time()) - 120)
115
+ recent_entry = _make_entry(timestamp=int(time.time()) - 5)
116
+ assert is_active([old_entry, recent_entry]) is True
117
+
118
+ def test_old_entries_with_recent_last(self):
119
+ """Multiple old entries don't matter if last one is recent."""
120
+ now = int(time.time())
121
+ entries = [
122
+ _make_entry(timestamp=now - 300),
123
+ _make_entry(timestamp=now - 200),
124
+ _make_entry(timestamp=now - 100),
125
+ _make_entry(timestamp=now - 2),
126
+ ]
127
+ assert is_active(entries) is True
@@ -1,69 +0,0 @@
1
- # npm Package Publishing Guide
2
-
3
- ## Step 1: Access Your npm Account with Recovery Code
4
-
5
- 1. Go to https://www.npmjs.com/login
6
- 2. Enter your credentials (luong.nguyen + password)
7
- 3. When prompted for 2FA, click "Use a recovery code or request a reset"
8
- 4. Enter one of your 8-character recovery codes
9
- 5. You should now be logged into your npm account
10
-
11
- ## Step 2: Set Up Microsoft Authenticator
12
-
13
- 1. Once logged in, click your profile picture (top right)
14
- 2. Go to "Account"
15
- 3. Under "Two-Factor Authentication", click "Modify 2FA"
16
- 4. Choose to add an "Authenticator app"
17
- 5. npm will show you a QR code
18
-
19
- ### In Microsoft Authenticator:
20
-
21
- 1. Open Microsoft Authenticator app
22
- 2. Tap the `+` button (top right)
23
- 3. Choose "Personal account" or "Work/school account"
24
- 4. Tap "Scan QR code"
25
- 5. Scan the QR code from npm website
26
- 6. The app will now show "npm" with a 6-digit code
27
-
28
- ## Step 3: Publish Your Package
29
-
30
- 1. Get the current 6-digit code from Microsoft Authenticator
31
- 2. Run this command in your terminal:
32
-
33
- ```bash
34
- cd /Users/montimage/buildspace/luongnv89/cc-context-stats
35
- ./publish.sh YOUR_6_DIGIT_CODE
36
- ```
37
-
38
- For example:
39
-
40
- ```bash
41
- ./publish.sh 123456
42
- ```
43
-
44
- ## Step 4: Verify Publication
45
-
46
- After successful publishing, you can verify with:
47
-
48
- ```bash
49
- npm view cc-context-stats
50
- ```
51
-
52
- ## Important Notes:
53
-
54
- - Recovery codes are single-use - once you use one, it's gone
55
- - Keep your remaining recovery codes safe
56
- - Microsoft Authenticator will provide new codes every 30 seconds
57
- - The package will be published as "cc-context-stats@1.3.0"
58
-
59
- ## Troubleshooting:
60
-
61
- If you get "Invalid OTP" error:
62
-
63
- - Wait for a new code to generate in Microsoft Authenticator (codes change every 30 seconds)
64
- - Make sure you're typing the code correctly
65
-
66
- If you can't log into the website:
67
-
68
- - Try a different recovery code
69
- - Make sure you're typing the 8-character code correctly (no spaces)
package/npm-publish.sh DELETED
@@ -1,33 +0,0 @@
1
- #!/bin/bash
2
-
3
- if [ $# -eq 0 ]; then
4
- echo "❌ Error: Please provide your 6-digit OTP code"
5
- echo ""
6
- echo "Usage: ./npm-publish.sh 123456"
7
- echo ""
8
- echo "Get your code from Microsoft Authenticator app"
9
- exit 1
10
- fi
11
-
12
- echo "🚀 Publishing cc-context-stats to npm..."
13
- echo "📦 Package: cc-context-stats@1.3.0"
14
- echo "🔐 Using OTP: $1"
15
- echo ""
16
-
17
- npm publish --otp=$1
18
-
19
- if [ $? -eq 0 ]; then
20
- echo ""
21
- echo "✅ SUCCESS! Package published to npm!"
22
- echo "📦 Package name: cc-context-stats"
23
- echo "📦 Version: 1.3.0"
24
- echo "📦 Install with: npm install cc-context-stats"
25
- echo "🌐 View at: https://www.npmjs.com/package/cc-context-stats"
26
- else
27
- echo ""
28
- echo "❌ FAILED! Publishing failed."
29
- echo "💡 Tips:"
30
- echo " - Make sure your OTP code is current (changes every 30 seconds)"
31
- echo " - Double-check the 6-digit code"
32
- echo " - If you lost access, use a recovery code on npm website"
33
- fi
package/publish.sh DELETED
@@ -1,24 +0,0 @@
1
- #!/bin/bash
2
-
3
- echo "npm publish with OTP"
4
- echo "Usage: ./publish.sh <6-digit-otp-code>"
5
- echo ""
6
-
7
- if [ $# -eq 0 ]; then
8
- echo "Please provide your 6-digit OTP code:"
9
- echo " ./publish.sh 123456"
10
- exit 1
11
- fi
12
-
13
- echo "Publishing cc-context-stats to npm..."
14
- npm publish --otp=$1
15
-
16
- if [ $? -eq 0 ]; then
17
- echo ""
18
- echo "✅ Successfully published to npm!"
19
- echo "Package: cc-context-stats@1.3.0"
20
- echo "Install with: npm install cc-context-stats"
21
- else
22
- echo ""
23
- echo "❌ Publishing failed. Please check your OTP code."
24
- fi
@@ -1,11 +0,0 @@
1
- #!/usr/bin/env node
2
- const fs = require('fs');
3
- let input = '';
4
- process.stdin.setEncoding('utf8');
5
- process.stdin.on('data', chunk => (input += chunk));
6
- process.stdin.on('end', () => {
7
- // Log raw JSON to a file
8
- fs.writeFileSync('/tmp/claude-raw-input.json', input);
9
- // Also output a simple status line
10
- console.log('[Claude] debug');
11
- });