cc-context-stats 1.6.2 → 1.8.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/CHANGELOG.md +39 -0
- package/CLAUDE.md +12 -0
- package/README.md +34 -24
- package/docs/ARCHITECTURE.md +52 -25
- package/docs/CSV_FORMAT.md +2 -0
- package/docs/DEPLOYMENT.md +19 -8
- package/docs/DEVELOPMENT.md +48 -12
- package/docs/MODEL_INTELLIGENCE.md +396 -0
- package/docs/configuration.md +35 -0
- package/docs/context-stats.md +12 -1
- package/docs/installation.md +82 -22
- package/docs/scripts.md +47 -23
- package/docs/troubleshooting.md +93 -4
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/scripts/statusline-full.sh +171 -37
- package/scripts/statusline.js +214 -32
- package/scripts/statusline.py +195 -47
- package/src/claude_statusline/__init__.py +1 -1
- package/src/claude_statusline/cli/context_stats.py +85 -13
- package/src/claude_statusline/cli/explain.py +228 -0
- package/src/claude_statusline/cli/statusline.py +41 -30
- package/src/claude_statusline/core/colors.py +78 -9
- package/src/claude_statusline/core/config.py +68 -9
- package/src/claude_statusline/core/git.py +16 -5
- package/src/claude_statusline/graphs/intelligence.py +162 -0
- package/src/claude_statusline/graphs/renderer.py +38 -3
- package/tests/bash/test_statusline_full.bats +5 -5
- package/tests/fixtures/mi_test_vectors.json +140 -0
- package/tests/node/intelligence.test.js +98 -0
- package/tests/node/statusline.test.js +4 -4
- package/tests/python/test_colors.py +105 -0
- package/tests/python/test_config_colors.py +78 -0
- package/tests/python/test_explain.py +177 -0
- package/tests/python/test_intelligence.py +314 -0
- package/tests/python/test_layout.py +4 -4
- package/tests/python/test_statusline.py +4 -4
package/docs/scripts.md
CHANGED
|
@@ -2,18 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
| Script | Platform | Requirements | Features |
|
|
6
|
-
| ----------------------- | ------------ | ------------ | --------------------------------- |
|
|
7
|
-
| `statusline-full.sh` | macOS, Linux | `jq` | Full-featured with all indicators |
|
|
8
|
-
| `statusline-git.sh` | macOS, Linux | `jq` | Git branch and changes |
|
|
9
|
-
| `statusline-minimal.sh` | macOS, Linux | `jq` | Model + directory only |
|
|
10
|
-
| `statusline.py` | All | Python 3 | Cross-platform, full-featured |
|
|
11
|
-
| `statusline.js` | All | Node.js
|
|
12
|
-
| `context-stats.sh` | macOS, Linux |
|
|
5
|
+
| Script | Platform | Requirements | State Writes | Features |
|
|
6
|
+
| ----------------------- | ------------ | ------------ | ------------ | --------------------------------- |
|
|
7
|
+
| `statusline-full.sh` | macOS, Linux | `jq` | No | Full-featured with all indicators |
|
|
8
|
+
| `statusline-git.sh` | macOS, Linux | `jq` | No | Git branch and changes |
|
|
9
|
+
| `statusline-minimal.sh` | macOS, Linux | `jq` | No | Model + directory only |
|
|
10
|
+
| `statusline.py` | All | Python 3 | Yes | Cross-platform, full-featured |
|
|
11
|
+
| `statusline.js` | All | Node.js 18+ | Yes | Cross-platform, full-featured |
|
|
12
|
+
| `context-stats.sh` | macOS, Linux | Bash | No | Token usage visualization (CLI) |
|
|
13
|
+
|
|
14
|
+
## Installation Methods
|
|
15
|
+
|
|
16
|
+
| Method | Statusline Command | Context Stats Command |
|
|
17
|
+
| ------ | ------------------ | --------------------- |
|
|
18
|
+
| `pip install cc-context-stats` | `claude-statusline` | `context-stats` |
|
|
19
|
+
| `npm install -g cc-context-stats` | `claude-statusline` | `context-stats` |
|
|
20
|
+
| Shell installer (`install.sh`) | `~/.claude/statusline.sh` | `~/.local/bin/context-stats` |
|
|
13
21
|
|
|
14
22
|
## Bash Scripts
|
|
15
23
|
|
|
16
|
-
### statusline-full.sh (Recommended)
|
|
24
|
+
### statusline-full.sh (Recommended for bash users)
|
|
17
25
|
|
|
18
26
|
Complete status line with all features:
|
|
19
27
|
|
|
@@ -25,6 +33,8 @@ Complete status line with all features:
|
|
|
25
33
|
- Autocompact indicator
|
|
26
34
|
- Session ID
|
|
27
35
|
|
|
36
|
+
> **Note:** Does not write state files. For context-stats CLI support, use the Python or Node.js script instead.
|
|
37
|
+
|
|
28
38
|
### statusline-git.sh
|
|
29
39
|
|
|
30
40
|
Lighter version with git info:
|
|
@@ -46,15 +56,27 @@ Minimal footprint:
|
|
|
46
56
|
|
|
47
57
|
Python implementation matching `statusline-full.sh` functionality. Works on Windows, macOS, and Linux without additional dependencies beyond Python 3.
|
|
48
58
|
|
|
59
|
+
Features beyond bash scripts:
|
|
60
|
+
- Writes state files for context-stats CLI
|
|
61
|
+
- Duplicate-entry deduplication
|
|
62
|
+
- State file rotation (10k/5k threshold)
|
|
63
|
+
- 5-second git command timeout
|
|
64
|
+
|
|
49
65
|
### statusline.js
|
|
50
66
|
|
|
51
|
-
Node.js implementation matching `statusline-full.sh` functionality. Works on all platforms with Node.js installed.
|
|
67
|
+
Node.js implementation matching `statusline-full.sh` functionality. Works on all platforms with Node.js 18+ installed.
|
|
68
|
+
|
|
69
|
+
Features beyond bash scripts:
|
|
70
|
+
- Writes state files for context-stats CLI
|
|
71
|
+
- Duplicate-entry deduplication
|
|
72
|
+
- State file rotation (10k/5k threshold)
|
|
73
|
+
- 5-second git command timeout
|
|
52
74
|
|
|
53
75
|
## Utility Scripts
|
|
54
76
|
|
|
55
77
|
### context-stats.sh
|
|
56
78
|
|
|
57
|
-
Standalone CLI tool for visualizing token consumption. See [Context Stats](context-stats.md) for details.
|
|
79
|
+
Standalone bash CLI tool for visualizing token consumption. Reads state files written by the Python or Node.js statusline scripts. See [Context Stats](context-stats.md) for details.
|
|
58
80
|
|
|
59
81
|
## Output Format
|
|
60
82
|
|
|
@@ -102,15 +124,17 @@ Scripts receive JSON via stdin from Claude Code:
|
|
|
102
124
|
|
|
103
125
|
## Color Codes
|
|
104
126
|
|
|
105
|
-
All scripts use consistent ANSI colors:
|
|
106
|
-
|
|
107
|
-
| Color | Code | Usage |
|
|
108
|
-
| ------- | ------------ | -------------------------- |
|
|
109
|
-
| Blue | `\033[0;34m` | Directory |
|
|
110
|
-
| Magenta | `\033[0;35m` | Git branch |
|
|
111
|
-
| Cyan | `\033[0;36m` | Changes count |
|
|
112
|
-
| Green | `\033[0;32m` | High availability (>50%) |
|
|
113
|
-
| Yellow | `\033[0;33m` | Medium availability (>25%) |
|
|
114
|
-
| Red | `\033[0;31m` | Low availability (<=25%) |
|
|
115
|
-
| Dim | `\033[2m` | Model, AC indicator |
|
|
116
|
-
| Reset | `\033[0m` | Reset formatting |
|
|
127
|
+
All scripts use consistent ANSI colors (defaults, overridable via `~/.claude/statusline.conf`):
|
|
128
|
+
|
|
129
|
+
| Color | Code | Usage | Config Key |
|
|
130
|
+
| ------- | ------------ | -------------------------- | --------------- |
|
|
131
|
+
| Blue | `\033[0;34m` | Directory | `color_blue` |
|
|
132
|
+
| Magenta | `\033[0;35m` | Git branch | `color_magenta` |
|
|
133
|
+
| Cyan | `\033[0;36m` | Changes count | `color_cyan` |
|
|
134
|
+
| Green | `\033[0;32m` | High availability (>50%) | `color_green` |
|
|
135
|
+
| Yellow | `\033[0;33m` | Medium availability (>25%) | `color_yellow` |
|
|
136
|
+
| Red | `\033[0;31m` | Low availability (<=25%) | `color_red` |
|
|
137
|
+
| Dim | `\033[2m` | Model, AC indicator | — |
|
|
138
|
+
| Reset | `\033[0m` | Reset formatting | — |
|
|
139
|
+
|
|
140
|
+
See [Configuration](configuration.md#custom-colors) for details on overriding colors with named colors or hex codes.
|
package/docs/troubleshooting.md
CHANGED
|
@@ -24,6 +24,22 @@
|
|
|
24
24
|
cat ~/.claude/settings.json
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
+
**pip/npm install:**
|
|
28
|
+
|
|
29
|
+
1. Verify the command is available:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
which claude-statusline
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
2. Test it:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
echo '{"model":{"display_name":"Test"}}' | claude-statusline
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
3. Ensure your settings.json uses `"command": "claude-statusline"` (not a file path).
|
|
42
|
+
|
|
27
43
|
**Windows (Python):**
|
|
28
44
|
|
|
29
45
|
```powershell
|
|
@@ -32,7 +48,7 @@ echo {"model":{"display_name":"Test"}} | python %USERPROFILE%\.claude\statusline
|
|
|
32
48
|
|
|
33
49
|
### jq not found
|
|
34
50
|
|
|
35
|
-
The bash scripts require `jq` for JSON parsing.
|
|
51
|
+
The bash scripts require `jq` for JSON parsing. Python and Node.js scripts do **not** need `jq`.
|
|
36
52
|
|
|
37
53
|
**macOS:**
|
|
38
54
|
|
|
@@ -56,6 +72,22 @@ Alternatively, use the Python or Node.js version which don't require `jq`.
|
|
|
56
72
|
|
|
57
73
|
### context-stats command not found
|
|
58
74
|
|
|
75
|
+
**If installed via pip or npm:**
|
|
76
|
+
|
|
77
|
+
1. Verify installation:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
which context-stats
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
2. Reinstall if missing:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
pip install cc-context-stats # or: npm install -g cc-context-stats
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**If installed via shell installer:**
|
|
90
|
+
|
|
59
91
|
1. Verify installation:
|
|
60
92
|
|
|
61
93
|
```bash
|
|
@@ -80,13 +112,58 @@ Alternatively, use the Python or Node.js version which don't require `jq`.
|
|
|
80
112
|
source ~/.bashrc
|
|
81
113
|
```
|
|
82
114
|
|
|
115
|
+
### pip install fails
|
|
116
|
+
|
|
117
|
+
1. Ensure Python 3.9+:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
python3 --version
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
2. Try with `--user` flag:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
pip install --user cc-context-stats
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
3. Or use `uv`:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
uv pip install cc-context-stats
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### npm install fails
|
|
136
|
+
|
|
137
|
+
1. Ensure Node.js 18+:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
node --version
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
2. Try with sudo (Linux/macOS):
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
sudo npm install -g cc-context-stats
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
3. Or fix npm permissions:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
mkdir -p ~/.npm-global
|
|
153
|
+
npm config set prefix '~/.npm-global'
|
|
154
|
+
echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> ~/.zshrc
|
|
155
|
+
source ~/.zshrc
|
|
156
|
+
npm install -g cc-context-stats
|
|
157
|
+
```
|
|
158
|
+
|
|
83
159
|
### No token graph data
|
|
84
160
|
|
|
85
161
|
Token history requires:
|
|
86
162
|
|
|
87
|
-
1.
|
|
88
|
-
2.
|
|
89
|
-
3.
|
|
163
|
+
1. Python or Node.js statusline script (bash scripts do **not** write state files)
|
|
164
|
+
2. `show_delta=true` in `~/.claude/statusline.conf` (default)
|
|
165
|
+
3. Active Claude Code session generating state files
|
|
166
|
+
4. State files at `~/.claude/statusline/statusline.<session_id>.state`
|
|
90
167
|
|
|
91
168
|
Check for state files:
|
|
92
169
|
|
|
@@ -108,6 +185,8 @@ ls -la ~/.claude/statusline/statusline.*.state
|
|
|
108
185
|
which git
|
|
109
186
|
```
|
|
110
187
|
|
|
188
|
+
3. Git commands have a 5-second timeout. If your repo is very large, git operations may time out silently.
|
|
189
|
+
|
|
111
190
|
### Wrong token colors
|
|
112
191
|
|
|
113
192
|
Token colors depend on availability percentage:
|
|
@@ -124,6 +203,12 @@ If colors look wrong, check terminal color support.
|
|
|
124
203
|
|
|
125
204
|
Token delta requires multiple statusline refreshes. The first refresh establishes a baseline; subsequent refreshes show the delta.
|
|
126
205
|
|
|
206
|
+
If delta is always zero after multiple refreshes, check that the state file is being written:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
wc -l ~/.claude/statusline/statusline.*.state
|
|
210
|
+
```
|
|
211
|
+
|
|
127
212
|
### Configuration not taking effect
|
|
128
213
|
|
|
129
214
|
1. Check config file location:
|
|
@@ -167,6 +252,9 @@ EOF
|
|
|
167
252
|
cat /tmp/test-input.json | ~/.claude/statusline.sh
|
|
168
253
|
cat /tmp/test-input.json | python3 ~/.claude/statusline.py
|
|
169
254
|
cat /tmp/test-input.json | node ~/.claude/statusline.js
|
|
255
|
+
|
|
256
|
+
# Test pip/npm installed version
|
|
257
|
+
cat /tmp/test-input.json | claude-statusline
|
|
170
258
|
```
|
|
171
259
|
|
|
172
260
|
### Check state files
|
|
@@ -185,5 +273,6 @@ watch -n 1 'tail -5 ~/.claude/statusline/statusline.*.state'
|
|
|
185
273
|
- Open a new issue with:
|
|
186
274
|
- Operating system
|
|
187
275
|
- Shell type (bash/zsh)
|
|
276
|
+
- Installation method (pip, npm, shell installer, manual)
|
|
188
277
|
- Script version being used
|
|
189
278
|
- Error messages or unexpected behavior
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "cc-context-stats"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.8.0"
|
|
8
8
|
description = "Monitor your Claude Code session context in real-time - track token usage and never run out of context"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
# State file format (CSV):
|
|
23
23
|
# timestamp,total_input_tokens,total_output_tokens,current_usage_input_tokens,current_usage_output_tokens,current_usage_cache_creation,current_usage_cache_read,total_cost_usd,total_lines_added,total_lines_removed,session_id,model_id,workspace_project_dir
|
|
24
24
|
|
|
25
|
-
# Colors
|
|
25
|
+
# Colors (defaults, overridable via config)
|
|
26
26
|
BLUE='\033[0;34m'
|
|
27
27
|
MAGENTA='\033[0;35m'
|
|
28
28
|
CYAN='\033[0;36m'
|
|
@@ -32,6 +32,92 @@ RED='\033[0;31m'
|
|
|
32
32
|
DIM='\033[2m'
|
|
33
33
|
RESET='\033[0m'
|
|
34
34
|
|
|
35
|
+
# Named colors for config parsing
|
|
36
|
+
declare -A COLOR_NAMES=(
|
|
37
|
+
[black]='\033[0;30m' [red]='\033[0;31m' [green]='\033[0;32m'
|
|
38
|
+
[yellow]='\033[0;33m' [blue]='\033[0;34m' [magenta]='\033[0;35m'
|
|
39
|
+
[cyan]='\033[0;36m' [white]='\033[0;37m'
|
|
40
|
+
[bright_black]='\033[0;90m' [bright_red]='\033[0;91m' [bright_green]='\033[0;92m'
|
|
41
|
+
[bright_yellow]='\033[0;93m' [bright_blue]='\033[0;94m' [bright_magenta]='\033[0;95m'
|
|
42
|
+
[bright_cyan]='\033[0;96m' [bright_white]='\033[0;97m'
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Color config key to slot mapping
|
|
46
|
+
declare -A COLOR_KEYS=(
|
|
47
|
+
[color_green]=GREEN [color_yellow]=YELLOW [color_red]=RED
|
|
48
|
+
[color_blue]=BLUE [color_magenta]=MAGENTA [color_cyan]=CYAN
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Parse a color name or #rrggbb hex into an ANSI escape code
|
|
52
|
+
parse_color() {
|
|
53
|
+
local value
|
|
54
|
+
value=$(echo "$1" | tr '[:upper:]' '[:lower:]' | xargs)
|
|
55
|
+
if [[ -n "${COLOR_NAMES[$value]+x}" ]]; then
|
|
56
|
+
echo "${COLOR_NAMES[$value]}"
|
|
57
|
+
return
|
|
58
|
+
fi
|
|
59
|
+
if [[ "$value" =~ ^#[0-9a-f]{6}$ ]]; then
|
|
60
|
+
local r=$((16#${value:1:2}))
|
|
61
|
+
local g=$((16#${value:3:2}))
|
|
62
|
+
local b=$((16#${value:5:2}))
|
|
63
|
+
echo "\033[38;2;${r};${g};${b}m"
|
|
64
|
+
return
|
|
65
|
+
fi
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# State file rotation constants
|
|
69
|
+
ROTATION_THRESHOLD=10000
|
|
70
|
+
ROTATION_KEEP=5000
|
|
71
|
+
|
|
72
|
+
# Rotate state file if it exceeds threshold
|
|
73
|
+
maybe_rotate_state_file() {
|
|
74
|
+
local state_file="$1"
|
|
75
|
+
[[ -f "$state_file" ]] || return
|
|
76
|
+
local line_count
|
|
77
|
+
line_count=$(wc -l < "$state_file" | tr -d ' ')
|
|
78
|
+
if [[ "$line_count" -gt "$ROTATION_THRESHOLD" ]]; then
|
|
79
|
+
local tmp_file="${state_file}.tmp.$$"
|
|
80
|
+
tail -n "$ROTATION_KEEP" "$state_file" > "$tmp_file" && mv "$tmp_file" "$state_file" || rm -f "$tmp_file"
|
|
81
|
+
fi
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# Model Intelligence computation (uses awk for float math)
|
|
85
|
+
compute_mi() {
|
|
86
|
+
local used_tokens=$1 context_window=$2 cache_read_val=$3 total_context=$4
|
|
87
|
+
local delta_lines=$5 delta_output=$6 beta=$7
|
|
88
|
+
awk -v used="$used_tokens" -v cw="$context_window" -v cr="$cache_read_val" \
|
|
89
|
+
-v tc="$total_context" -v dl="$delta_lines" -v do_val="$delta_output" -v b="$beta" '
|
|
90
|
+
BEGIN {
|
|
91
|
+
if (cw == 0) { printf "1.00 1.000 1.000 0.500"; exit }
|
|
92
|
+
# CPS
|
|
93
|
+
u = used / cw
|
|
94
|
+
if (u <= 0) cps = 1.0
|
|
95
|
+
else { cps = 1.0 - (u ^ b); if (cps < 0) cps = 0.0 }
|
|
96
|
+
# ES
|
|
97
|
+
if (tc == 0) es = 1.0
|
|
98
|
+
else es = 0.3 + 0.7 * (cr / tc)
|
|
99
|
+
# PS
|
|
100
|
+
if (do_val == "" || do_val + 0 <= 0) ps = 0.5
|
|
101
|
+
else {
|
|
102
|
+
ratio = dl / do_val
|
|
103
|
+
normalized = ratio / 0.2
|
|
104
|
+
if (normalized > 1.0) normalized = 1.0
|
|
105
|
+
ps = 0.2 + 0.8 * normalized
|
|
106
|
+
}
|
|
107
|
+
mi = 0.60 * cps + 0.25 * es + 0.15 * ps
|
|
108
|
+
printf "%.2f %.3f %.3f %.3f", mi, cps, es, ps
|
|
109
|
+
}'
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
get_mi_color() {
|
|
113
|
+
local mi_val="$1"
|
|
114
|
+
awk -v mi="$mi_val" 'BEGIN {
|
|
115
|
+
if (mi + 0 > 0.65) print "green"
|
|
116
|
+
else if (mi + 0 > 0.35) print "yellow"
|
|
117
|
+
else print "red"
|
|
118
|
+
}'
|
|
119
|
+
}
|
|
120
|
+
|
|
35
121
|
# Read JSON input from stdin
|
|
36
122
|
input=$(cat)
|
|
37
123
|
|
|
@@ -63,12 +149,11 @@ autocompact_enabled=true
|
|
|
63
149
|
token_detail_enabled=true
|
|
64
150
|
show_delta_enabled=true
|
|
65
151
|
show_session_enabled=true
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
show_delta="" # Will be set by sourced config
|
|
69
|
-
show_session="" # Will be set by sourced config
|
|
152
|
+
show_mi_enabled=true
|
|
153
|
+
mi_curve_beta=1.5
|
|
70
154
|
ac_info=""
|
|
71
155
|
delta_info=""
|
|
156
|
+
mi_info=""
|
|
72
157
|
session_info=""
|
|
73
158
|
|
|
74
159
|
# Create config file with defaults if it doesn't exist
|
|
@@ -87,24 +172,44 @@ show_delta=true
|
|
|
87
172
|
|
|
88
173
|
# Show session_id in status line
|
|
89
174
|
show_session=true
|
|
175
|
+
|
|
176
|
+
# Model Intelligence (MI) score display
|
|
177
|
+
show_mi=true
|
|
178
|
+
|
|
179
|
+
# MI degradation curve shape (higher = steeper initial drop)
|
|
180
|
+
# mi_curve_beta=1.5
|
|
90
181
|
EOF
|
|
91
182
|
fi
|
|
92
183
|
|
|
93
184
|
if [[ -f ~/.claude/statusline.conf ]]; then
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
185
|
+
while IFS= read -r line; do
|
|
186
|
+
line=$(echo "$line" | xargs)
|
|
187
|
+
[[ -z "$line" || "$line" == \#* ]] && continue
|
|
188
|
+
[[ "$line" != *=* ]] && continue
|
|
189
|
+
key="${line%%=*}"
|
|
190
|
+
key=$(echo "$key" | xargs)
|
|
191
|
+
raw_value="${line#*=}"
|
|
192
|
+
raw_value=$(echo "$raw_value" | xargs)
|
|
193
|
+
value_lower=$(echo "$raw_value" | tr '[:upper:]' '[:lower:]')
|
|
194
|
+
case "$key" in
|
|
195
|
+
autocompact) [[ "$value_lower" == "false" ]] && autocompact_enabled=false ;;
|
|
196
|
+
token_detail) [[ "$value_lower" == "false" ]] && token_detail_enabled=false ;;
|
|
197
|
+
show_delta) [[ "$value_lower" == "false" ]] && show_delta_enabled=false ;;
|
|
198
|
+
show_session) [[ "$value_lower" == "false" ]] && show_session_enabled=false ;;
|
|
199
|
+
show_mi) [[ "$value_lower" == "false" ]] && show_mi_enabled=false ;;
|
|
200
|
+
mi_curve_beta) mi_curve_beta="$raw_value" ;;
|
|
201
|
+
color_*)
|
|
202
|
+
if [[ -n "${COLOR_KEYS[$key]+x}" ]]; then
|
|
203
|
+
local slot="${COLOR_KEYS[$key]}"
|
|
204
|
+
local ansi
|
|
205
|
+
ansi=$(parse_color "$raw_value")
|
|
206
|
+
if [[ -n "$ansi" ]]; then
|
|
207
|
+
eval "$slot='$ansi'"
|
|
208
|
+
fi
|
|
209
|
+
fi
|
|
210
|
+
;;
|
|
211
|
+
esac
|
|
212
|
+
done < ~/.claude/statusline.conf
|
|
108
213
|
fi
|
|
109
214
|
|
|
110
215
|
# Width-fitting helpers
|
|
@@ -229,10 +334,10 @@ if [[ "$total_size" -gt 0 && "$current_usage" != "null" ]]; then
|
|
|
229
334
|
ctx_color="$RED"
|
|
230
335
|
fi
|
|
231
336
|
|
|
232
|
-
context_info=" | ${ctx_color}${free_display}
|
|
337
|
+
context_info=" | ${ctx_color}${free_display} (${free_pct}%)${RESET}"
|
|
233
338
|
|
|
234
|
-
#
|
|
235
|
-
if [[ "$show_delta_enabled" == "true" ]]; then
|
|
339
|
+
# Read previous entry if needed for delta OR MI
|
|
340
|
+
if [[ "$show_delta_enabled" == "true" || "$show_mi_enabled" == "true" ]]; then
|
|
236
341
|
# Use session_id for per-session state (avoids conflicts with parallel sessions)
|
|
237
342
|
state_dir=~/.claude/statusline
|
|
238
343
|
mkdir -p "$state_dir"
|
|
@@ -245,7 +350,6 @@ if [[ "$total_size" -gt 0 && "$current_usage" != "null" ]]; then
|
|
|
245
350
|
if [[ ! -f "$new_file" ]]; then
|
|
246
351
|
mv "$old_file" "$new_file" 2>/dev/null || true
|
|
247
352
|
else
|
|
248
|
-
# New file exists, just remove old one
|
|
249
353
|
rm -f "$old_file" 2>/dev/null || true
|
|
250
354
|
fi
|
|
251
355
|
fi
|
|
@@ -258,37 +362,67 @@ if [[ "$total_size" -gt 0 && "$current_usage" != "null" ]]; then
|
|
|
258
362
|
fi
|
|
259
363
|
has_prev=false
|
|
260
364
|
prev_tokens=0
|
|
365
|
+
prev_output_tokens=0
|
|
366
|
+
prev_lines_added=0
|
|
367
|
+
prev_lines_removed=0
|
|
261
368
|
if [[ -f "$state_file" ]]; then
|
|
262
369
|
has_prev=true
|
|
263
|
-
# Read last line and calculate previous
|
|
264
|
-
# CSV: ts[0],in[1],out[2],cur_in[3],cur_out[4],cache_create[5],cache_read[6]
|
|
370
|
+
# Read last line and calculate previous state
|
|
371
|
+
# CSV: ts[0],in[1],out[2],cur_in[3],cur_out[4],cache_create[5],cache_read[6],
|
|
372
|
+
# cost[7],+lines[8],-lines[9],session[10],model[11],dir[12],size[13]
|
|
265
373
|
last_line=$(tail -1 "$state_file" 2>/dev/null)
|
|
266
374
|
if [[ -n "$last_line" ]]; then
|
|
267
375
|
prev_cur_in=$(echo "$last_line" | cut -d',' -f4)
|
|
268
376
|
prev_cache_create=$(echo "$last_line" | cut -d',' -f6)
|
|
269
377
|
prev_cache_read=$(echo "$last_line" | cut -d',' -f7)
|
|
270
378
|
prev_tokens=$(( ${prev_cur_in:-0} + ${prev_cache_create:-0} + ${prev_cache_read:-0} ))
|
|
379
|
+
prev_output_tokens=$(echo "$last_line" | cut -d',' -f3)
|
|
380
|
+
prev_lines_added=$(echo "$last_line" | cut -d',' -f9)
|
|
381
|
+
prev_lines_removed=$(echo "$last_line" | cut -d',' -f10)
|
|
271
382
|
fi
|
|
272
383
|
fi
|
|
273
|
-
|
|
274
|
-
delta
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
384
|
+
|
|
385
|
+
# Calculate and display token delta if enabled
|
|
386
|
+
if [[ "$show_delta_enabled" == "true" ]]; then
|
|
387
|
+
delta=$((used_tokens - prev_tokens))
|
|
388
|
+
if [[ "$has_prev" == "true" && "$delta" -gt 0 ]]; then
|
|
389
|
+
if [[ "$token_detail_enabled" == "true" ]]; then
|
|
390
|
+
delta_display=$(awk -v n="$delta" 'BEGIN { printf "%\047d", n }')
|
|
391
|
+
else
|
|
392
|
+
delta_display=$(awk "BEGIN {printf \"%.1fk\", $delta / 1000}")
|
|
393
|
+
fi
|
|
394
|
+
delta_info=" ${DIM}[+${delta_display}]${RESET}"
|
|
395
|
+
fi
|
|
396
|
+
fi
|
|
397
|
+
|
|
398
|
+
# Calculate and display MI score if enabled
|
|
399
|
+
if [[ "$show_mi_enabled" == "true" ]]; then
|
|
400
|
+
if [[ "$has_prev" == "true" ]]; then
|
|
401
|
+
delta_la=$(( ${lines_added:-0} - ${prev_lines_added:-0} ))
|
|
402
|
+
delta_lr=$(( ${lines_removed:-0} - ${prev_lines_removed:-0} ))
|
|
403
|
+
mi_delta_lines=$(( delta_la + delta_lr ))
|
|
404
|
+
mi_delta_output=$(( ${total_output_tokens:-0} - ${prev_output_tokens:-0} ))
|
|
280
405
|
else
|
|
281
|
-
|
|
406
|
+
mi_delta_lines=0
|
|
407
|
+
mi_delta_output=""
|
|
282
408
|
fi
|
|
283
|
-
|
|
409
|
+
mi_result=$(compute_mi "$used_tokens" "$total_size" "$cache_read" "$used_tokens" "$mi_delta_lines" "$mi_delta_output" "$mi_curve_beta")
|
|
410
|
+
mi_val=$(echo "$mi_result" | cut -d' ' -f1)
|
|
411
|
+
mi_color_name=$(get_mi_color "$mi_val")
|
|
412
|
+
case "$mi_color_name" in
|
|
413
|
+
green) mi_color="$GREEN" ;;
|
|
414
|
+
yellow) mi_color="$YELLOW" ;;
|
|
415
|
+
red) mi_color="$RED" ;;
|
|
416
|
+
esac
|
|
417
|
+
mi_info=" ${mi_color}MI:${mi_val}${RESET}"
|
|
284
418
|
fi
|
|
419
|
+
|
|
285
420
|
# Only append if context usage changed (avoid duplicates from multiple refreshes)
|
|
286
421
|
cur_input_tokens=$(echo "$current_usage" | jq -r '.input_tokens // 0')
|
|
287
422
|
cur_output_tokens=$(echo "$current_usage" | jq -r '.output_tokens // 0')
|
|
288
423
|
if [[ "$has_prev" != "true" || "$used_tokens" != "$prev_tokens" ]]; then
|
|
289
|
-
# Append current usage with comprehensive format
|
|
290
|
-
# Format: ts,in,out,cur_in,cur_out,cache_create,cache_read,cost,+lines,-lines,session,model,dir,size
|
|
291
424
|
echo "$(date +%s),$total_input_tokens,$total_output_tokens,$cur_input_tokens,$cur_output_tokens,$cache_creation,$cache_read,$cost_usd,$lines_added,$lines_removed,$session_id,$model_id,$workspace_project_dir,$total_size" >>"$state_file"
|
|
425
|
+
maybe_rotate_state_file "$state_file"
|
|
292
426
|
fi
|
|
293
427
|
fi
|
|
294
428
|
fi
|
|
@@ -301,4 +435,4 @@ fi
|
|
|
301
435
|
# Output: [Model] directory | branch [changes] | XXk free (XX%) [+delta] [AC] [S:session_id]
|
|
302
436
|
base="${DIM}[${model}]${RESET} ${BLUE}${dir_name}${RESET}"
|
|
303
437
|
max_width=$(get_terminal_width)
|
|
304
|
-
fit_to_width "$max_width" "$base" "$git_info" "$context_info" "$delta_info" "$ac_info" "$session_info"
|
|
438
|
+
fit_to_width "$max_width" "$base" "$git_info" "$context_info" "$delta_info" "$mi_info" "$ac_info" "$session_info"
|