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.
Files changed (37) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/CLAUDE.md +12 -0
  3. package/README.md +34 -24
  4. package/docs/ARCHITECTURE.md +52 -25
  5. package/docs/CSV_FORMAT.md +2 -0
  6. package/docs/DEPLOYMENT.md +19 -8
  7. package/docs/DEVELOPMENT.md +48 -12
  8. package/docs/MODEL_INTELLIGENCE.md +396 -0
  9. package/docs/configuration.md +35 -0
  10. package/docs/context-stats.md +12 -1
  11. package/docs/installation.md +82 -22
  12. package/docs/scripts.md +47 -23
  13. package/docs/troubleshooting.md +93 -4
  14. package/package.json +1 -1
  15. package/pyproject.toml +1 -1
  16. package/scripts/statusline-full.sh +171 -37
  17. package/scripts/statusline.js +214 -32
  18. package/scripts/statusline.py +195 -47
  19. package/src/claude_statusline/__init__.py +1 -1
  20. package/src/claude_statusline/cli/context_stats.py +85 -13
  21. package/src/claude_statusline/cli/explain.py +228 -0
  22. package/src/claude_statusline/cli/statusline.py +41 -30
  23. package/src/claude_statusline/core/colors.py +78 -9
  24. package/src/claude_statusline/core/config.py +68 -9
  25. package/src/claude_statusline/core/git.py +16 -5
  26. package/src/claude_statusline/graphs/intelligence.py +162 -0
  27. package/src/claude_statusline/graphs/renderer.py +38 -3
  28. package/tests/bash/test_statusline_full.bats +5 -5
  29. package/tests/fixtures/mi_test_vectors.json +140 -0
  30. package/tests/node/intelligence.test.js +98 -0
  31. package/tests/node/statusline.test.js +4 -4
  32. package/tests/python/test_colors.py +105 -0
  33. package/tests/python/test_config_colors.py +78 -0
  34. package/tests/python/test_explain.py +177 -0
  35. package/tests/python/test_intelligence.py +314 -0
  36. package/tests/python/test_layout.py +4 -4
  37. 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 | Cross-platform, full-featured |
12
- | `context-stats.sh` | macOS, Linux | None | Token usage visualization |
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.
@@ -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. `show_delta=true` in `~/.claude/statusline.conf` (default)
88
- 2. Active Claude Code session generating state files
89
- 3. State files at `~/.claude/statusline/statusline.<session_id>.state`
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-context-stats",
3
- "version": "1.6.2",
3
+ "version": "1.8.0",
4
4
  "description": "Monitor your Claude Code session context in real-time - track token usage and never run out of context",
5
5
  "main": "scripts/statusline.js",
6
6
  "scripts": {
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.6.2"
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
- autocompact="" # Will be set by sourced config
67
- token_detail="" # Will be set by sourced config
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
- # shellcheck source=/dev/null
95
- source ~/.claude/statusline.conf
96
- if [[ "$autocompact" == "false" ]]; then
97
- autocompact_enabled=false
98
- fi
99
- if [[ "$token_detail" == "false" ]]; then
100
- token_detail_enabled=false
101
- fi
102
- if [[ "$show_delta" == "false" ]]; then
103
- show_delta_enabled=false
104
- fi
105
- if [[ "$show_session" == "false" ]]; then
106
- show_session_enabled=false
107
- fi
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} free (${free_pct}%)${RESET}"
337
+ context_info=" | ${ctx_color}${free_display} (${free_pct}%)${RESET}"
233
338
 
234
- # Calculate and display token delta if enabled
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 context usage
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
- # Calculate delta
274
- delta=$((used_tokens - prev_tokens))
275
- # delta calculated for display
276
- # Only show positive delta (and skip first run when no previous state)
277
- if [[ "$has_prev" == "true" && "$delta" -gt 0 ]]; then
278
- if [[ "$token_detail_enabled" == "true" ]]; then
279
- delta_display=$(awk -v n="$delta" 'BEGIN { printf "%\047d", n }')
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
- delta_display=$(awk "BEGIN {printf \"%.1fk\", $delta / 1000}")
406
+ mi_delta_lines=0
407
+ mi_delta_output=""
282
408
  fi
283
- delta_info=" ${DIM}[+${delta_display}]${RESET}"
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"