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.
- package/.claude/settings.local.json +36 -1
- package/.github/workflows/ci.yml +4 -2
- package/.github/workflows/release.yml +3 -1
- package/CHANGELOG.md +17 -0
- package/README.md +33 -11
- package/RELEASE_NOTES.md +10 -0
- package/assets/logo/favicon.svg +19 -0
- package/assets/logo/logo-black.svg +24 -0
- package/assets/logo/logo-full.svg +40 -0
- package/assets/logo/logo-icon.svg +27 -0
- package/assets/logo/logo-mark.svg +28 -0
- package/assets/logo/logo-white.svg +24 -0
- package/assets/logo/logo-wordmark.svg +6 -0
- package/install.sh +21 -2
- package/package.json +7 -3
- package/pyproject.toml +6 -4
- package/scripts/context-stats.sh +194 -26
- package/scripts/statusline-full.sh +65 -2
- package/scripts/statusline-git.sh +57 -1
- package/scripts/statusline-minimal.sh +57 -1
- package/scripts/statusline.js +51 -4
- package/scripts/statusline.py +57 -3
- package/src/claude_statusline/__init__.py +1 -1
- package/src/claude_statusline/cli/context_stats.py +106 -34
- package/src/claude_statusline/cli/statusline.py +5 -4
- package/src/claude_statusline/core/config.py +7 -0
- package/src/claude_statusline/formatters/layout.py +67 -0
- package/src/claude_statusline/graphs/renderer.py +44 -24
- package/src/claude_statusline/graphs/statistics.py +34 -0
- package/src/claude_statusline/ui/__init__.py +1 -0
- package/src/claude_statusline/ui/icons.py +93 -0
- package/src/claude_statusline/ui/waiting.py +62 -0
- package/tests/bash/test_statusline_full.bats +30 -0
- package/tests/node/statusline.test.js +44 -3
- package/tests/python/test_icons.py +152 -0
- package/tests/python/test_layout.py +127 -0
- package/tests/python/test_statusline.py +64 -3
- package/tests/python/test_waiting.py +127 -0
- package/PUBLISHING_GUIDE.md +0 -69
- package/npm-publish.sh +0 -33
- package/publish.sh +0 -24
- 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
|
package/PUBLISHING_GUIDE.md
DELETED
|
@@ -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
|
-
});
|