cc-context-stats 1.7.0 → 1.8.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 (102) hide show
  1. package/package.json +9 -1
  2. package/scripts/context-stats.sh +1 -1
  3. package/scripts/statusline.js +128 -18
  4. package/.editorconfig +0 -60
  5. package/.eslintrc.json +0 -35
  6. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -49
  7. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -31
  8. package/.github/PULL_REQUEST_TEMPLATE.md +0 -33
  9. package/.github/dependabot.yml +0 -44
  10. package/.github/workflows/ci.yml +0 -294
  11. package/.github/workflows/release.yml +0 -151
  12. package/.pre-commit-config.yaml +0 -74
  13. package/.prettierrc +0 -33
  14. package/.shellcheckrc +0 -10
  15. package/CHANGELOG.md +0 -163
  16. package/CLAUDE.md +0 -66
  17. package/CODE_OF_CONDUCT.md +0 -59
  18. package/CONTRIBUTING.md +0 -240
  19. package/RELEASE_NOTES.md +0 -19
  20. package/SECURITY.md +0 -44
  21. package/TODOS.md +0 -72
  22. package/assets/logo/favicon.svg +0 -19
  23. package/assets/logo/logo-black.svg +0 -24
  24. package/assets/logo/logo-full.svg +0 -40
  25. package/assets/logo/logo-icon.svg +0 -27
  26. package/assets/logo/logo-mark.svg +0 -28
  27. package/assets/logo/logo-white.svg +0 -24
  28. package/assets/logo/logo-wordmark.svg +0 -6
  29. package/config/settings-example.json +0 -7
  30. package/config/settings-node.json +0 -7
  31. package/config/settings-python.json +0 -7
  32. package/docs/ARCHITECTURE.md +0 -128
  33. package/docs/CSV_FORMAT.md +0 -42
  34. package/docs/DEPLOYMENT.md +0 -71
  35. package/docs/DEVELOPMENT.md +0 -161
  36. package/docs/configuration.md +0 -118
  37. package/docs/context-stats.md +0 -143
  38. package/docs/installation.md +0 -255
  39. package/docs/scripts.md +0 -140
  40. package/docs/troubleshooting.md +0 -278
  41. package/images/claude-statusline-token-graph.gif +0 -0
  42. package/images/claude-statusline.png +0 -0
  43. package/images/context-status-dumbzone.png +0 -0
  44. package/images/context-status.png +0 -0
  45. package/images/statusline-detail.png +0 -0
  46. package/images/token-graph.jpeg +0 -0
  47. package/images/token-graph.png +0 -0
  48. package/images/v1.6.1.png +0 -0
  49. package/install +0 -351
  50. package/install.sh +0 -298
  51. package/jest.config.js +0 -11
  52. package/pyproject.toml +0 -115
  53. package/requirements-dev.txt +0 -12
  54. package/scripts/statusline-full.sh +0 -304
  55. package/scripts/statusline-git.sh +0 -88
  56. package/scripts/statusline-minimal.sh +0 -67
  57. package/scripts/statusline.py +0 -485
  58. package/src/claude_statusline/__init__.py +0 -11
  59. package/src/claude_statusline/__main__.py +0 -6
  60. package/src/claude_statusline/cli/__init__.py +0 -1
  61. package/src/claude_statusline/cli/context_stats.py +0 -512
  62. package/src/claude_statusline/cli/explain.py +0 -228
  63. package/src/claude_statusline/cli/statusline.py +0 -169
  64. package/src/claude_statusline/core/__init__.py +0 -1
  65. package/src/claude_statusline/core/colors.py +0 -124
  66. package/src/claude_statusline/core/config.py +0 -148
  67. package/src/claude_statusline/core/git.py +0 -78
  68. package/src/claude_statusline/core/state.py +0 -323
  69. package/src/claude_statusline/formatters/__init__.py +0 -1
  70. package/src/claude_statusline/formatters/layout.py +0 -67
  71. package/src/claude_statusline/formatters/time.py +0 -50
  72. package/src/claude_statusline/formatters/tokens.py +0 -70
  73. package/src/claude_statusline/graphs/__init__.py +0 -1
  74. package/src/claude_statusline/graphs/renderer.py +0 -366
  75. package/src/claude_statusline/graphs/statistics.py +0 -92
  76. package/src/claude_statusline/ui/__init__.py +0 -1
  77. package/src/claude_statusline/ui/icons.py +0 -93
  78. package/src/claude_statusline/ui/waiting.py +0 -62
  79. package/tests/bash/test_delta_parity.bats +0 -199
  80. package/tests/bash/test_install.bats +0 -29
  81. package/tests/bash/test_parity.bats +0 -315
  82. package/tests/bash/test_statusline_full.bats +0 -139
  83. package/tests/bash/test_statusline_git.bats +0 -42
  84. package/tests/bash/test_statusline_minimal.bats +0 -37
  85. package/tests/fixtures/json/comma_in_path.json +0 -31
  86. package/tests/fixtures/json/high_usage.json +0 -17
  87. package/tests/fixtures/json/low_usage.json +0 -17
  88. package/tests/fixtures/json/medium_usage.json +0 -17
  89. package/tests/fixtures/json/valid_full.json +0 -30
  90. package/tests/fixtures/json/valid_minimal.json +0 -9
  91. package/tests/node/rotation.test.js +0 -89
  92. package/tests/node/statusline.test.js +0 -240
  93. package/tests/python/conftest.py +0 -84
  94. package/tests/python/test_colors.py +0 -105
  95. package/tests/python/test_config_colors.py +0 -78
  96. package/tests/python/test_data_pipeline.py +0 -446
  97. package/tests/python/test_explain.py +0 -177
  98. package/tests/python/test_icons.py +0 -152
  99. package/tests/python/test_layout.py +0 -127
  100. package/tests/python/test_state_rotation_validation.py +0 -232
  101. package/tests/python/test_statusline.py +0 -215
  102. package/tests/python/test_waiting.py +0 -127
@@ -1,304 +0,0 @@
1
- #!/bin/bash
2
- # Full-featured status line with context window usage
3
- # Usage: Copy to ~/.claude/statusline.sh and make executable
4
- #
5
- # Configuration:
6
- # Create/edit ~/.claude/statusline.conf and set:
7
- #
8
- # autocompact=true (when autocompact is enabled in Claude Code - default)
9
- # autocompact=false (when you disable autocompact via /config in Claude Code)
10
- #
11
- # token_detail=true (show exact token count like 64,000 - default)
12
- # token_detail=false (show abbreviated tokens like 64.0k)
13
- #
14
- # show_delta=true (show token delta since last refresh like [+2,500] - default)
15
- # show_delta=false (disable delta display - saves file I/O on every refresh)
16
- #
17
- # show_session=true (show session_id in status line - default)
18
- # show_session=false (hide session_id from status line)
19
- #
20
- # When AC is enabled, 22.5% of context window is reserved for autocompact buffer.
21
- #
22
- # State file format (CSV):
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
-
25
- # Colors
26
- BLUE='\033[0;34m'
27
- MAGENTA='\033[0;35m'
28
- CYAN='\033[0;36m'
29
- GREEN='\033[0;32m'
30
- YELLOW='\033[0;33m'
31
- RED='\033[0;31m'
32
- DIM='\033[2m'
33
- RESET='\033[0m'
34
-
35
- # Read JSON input from stdin
36
- input=$(cat)
37
-
38
- # Extract information from JSON
39
- cwd=$(echo "$input" | jq -r '.workspace.current_dir')
40
- project_dir=$(echo "$input" | jq -r '.workspace.project_dir')
41
- model=$(echo "$input" | jq -r '.model.display_name // "Claude"')
42
- session_id=$(echo "$input" | jq -r '.session_id // empty')
43
- dir_name=$(basename "$cwd")
44
-
45
- # Git information (skip optional locks for performance)
46
- git_info=""
47
- if [[ -d "$project_dir/.git" ]]; then
48
- git_branch=$(cd "$project_dir" 2>/dev/null && git --no-optional-locks rev-parse --abbrev-ref HEAD 2>/dev/null)
49
- git_status_count=$(cd "$project_dir" 2>/dev/null && git --no-optional-locks status --porcelain 2>/dev/null | wc -l | tr -d ' ')
50
-
51
- if [[ -n "$git_branch" ]]; then
52
- if [[ "$git_status_count" != "0" ]]; then
53
- git_info=" | ${MAGENTA}${git_branch}${RESET} ${CYAN}[${git_status_count}]${RESET}"
54
- else
55
- git_info=" | ${MAGENTA}${git_branch}${RESET}"
56
- fi
57
- fi
58
- fi
59
-
60
- # Read settings from ~/.claude/statusline.conf
61
- # Sync this manually when you change settings in Claude Code via /config
62
- autocompact_enabled=true
63
- token_detail_enabled=true
64
- show_delta_enabled=true
65
- 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
70
- ac_info=""
71
- delta_info=""
72
- session_info=""
73
-
74
- # Create config file with defaults if it doesn't exist
75
- if [[ ! -f ~/.claude/statusline.conf ]]; then
76
- mkdir -p ~/.claude
77
- cat >~/.claude/statusline.conf <<'EOF'
78
- # Autocompact setting - sync with Claude Code's /config
79
- autocompact=true
80
-
81
- # Token display format
82
- token_detail=true
83
-
84
- # Show token delta since last refresh (adds file I/O on every refresh)
85
- # Disable if you don't need it to reduce overhead
86
- show_delta=true
87
-
88
- # Show session_id in status line
89
- show_session=true
90
- EOF
91
- fi
92
-
93
- 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
108
- fi
109
-
110
- # Width-fitting helpers
111
- visible_width() {
112
- # Strip ANSI escape sequences (both literal \033 and actual ESC byte) and return string length
113
- local stripped
114
- stripped=$(printf '%s' "$1" | sed -e $'s/\033\[[0-9;]*m//g' -e 's/\\033\[[0-9;]*m//g')
115
- printf '%s' "$stripped" | wc -m | tr -d ' '
116
- }
117
-
118
- get_terminal_width() {
119
- # Return terminal width for fit_to_width truncation.
120
- # When running inside Claude Code's statusline subprocess, neither $COLUMNS
121
- # nor tput can detect the real terminal width (they always return 80).
122
- # If COLUMNS is explicitly set, trust it. Otherwise use 200 as default
123
- # so no parts are unnecessarily dropped; Claude Code handles overflow.
124
- if [[ -n "$COLUMNS" ]]; then
125
- echo "$COLUMNS"
126
- else
127
- local cols
128
- cols=$(tput cols 2>/dev/null || echo 80)
129
- if [[ "$cols" -eq 80 ]]; then
130
- echo 200
131
- else
132
- echo "$cols"
133
- fi
134
- fi
135
- }
136
-
137
- fit_to_width() {
138
- # Assemble parts into a single line that fits within max_width.
139
- # Usage: fit_to_width max_width part1 part2 part3 ...
140
- # First part (base) is always included. Subsequent parts are
141
- # included only if adding them does not exceed max_width.
142
- local max_width=$1
143
- shift
144
- local parts=("$@")
145
-
146
- if [[ ${#parts[@]} -eq 0 ]]; then
147
- echo ""
148
- return
149
- fi
150
-
151
- local result="${parts[0]}"
152
- local current_width
153
- current_width=$(visible_width "$result")
154
-
155
- for ((i = 1; i < ${#parts[@]}; i++)); do
156
- local part="${parts[$i]}"
157
- if [[ -z "$part" ]]; then
158
- continue
159
- fi
160
- local part_width
161
- part_width=$(visible_width "$part")
162
- if (( current_width + part_width <= max_width )); then
163
- result+="$part"
164
- (( current_width += part_width ))
165
- fi
166
- done
167
-
168
- echo -e "$result"
169
- }
170
-
171
- # Calculate context window - show remaining free space
172
- context_info=""
173
- total_size=$(echo "$input" | jq -r '.context_window.context_window_size // 0')
174
- current_usage=$(echo "$input" | jq '.context_window.current_usage')
175
- total_input_tokens=$(echo "$input" | jq -r '.context_window.total_input_tokens // 0')
176
- total_output_tokens=$(echo "$input" | jq -r '.context_window.total_output_tokens // 0')
177
- cost_usd=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
178
- lines_added=$(echo "$input" | jq -r '.cost.total_lines_added // 0')
179
- lines_removed=$(echo "$input" | jq -r '.cost.total_lines_removed // 0')
180
- model_id=$(echo "$input" | jq -r '.model.id // ""')
181
- workspace_project_dir=$(echo "$input" | jq -r '.workspace.project_dir // ""' | tr ',' '_')
182
-
183
- if [[ "$total_size" -gt 0 && "$current_usage" != "null" ]]; then
184
- # Get tokens from current_usage (includes cache)
185
- input_tokens=$(echo "$current_usage" | jq -r '.input_tokens // 0')
186
- cache_creation=$(echo "$current_usage" | jq -r '.cache_creation_input_tokens // 0')
187
- cache_read=$(echo "$current_usage" | jq -r '.cache_read_input_tokens // 0')
188
-
189
- # Total used from current request
190
- used_tokens=$((input_tokens + cache_creation + cache_read))
191
-
192
- # Calculate autocompact buffer (22.5% of context window = 45k for 200k)
193
- autocompact_buffer=$((total_size * 225 / 1000))
194
-
195
- # Free tokens calculation depends on autocompact setting
196
- if [[ "$autocompact_enabled" == "true" ]]; then
197
- # When AC enabled: subtract buffer to show actual usable space
198
- free_tokens=$((total_size - used_tokens - autocompact_buffer))
199
- buffer_k=$(awk "BEGIN {printf \"%.0f\", $autocompact_buffer / 1000}")
200
- ac_info=" ${DIM}[AC:${buffer_k}k]${RESET}"
201
- else
202
- # When AC disabled: show full free space
203
- free_tokens=$((total_size - used_tokens))
204
- ac_info=" ${DIM}[AC:off]${RESET}"
205
- fi
206
-
207
- if [[ "$free_tokens" -lt 0 ]]; then
208
- free_tokens=0
209
- fi
210
-
211
- # Calculate percentage with one decimal (relative to total size)
212
- free_pct=$(awk "BEGIN {printf \"%.1f\", ($free_tokens * 100.0 / $total_size)}")
213
- free_pct_int=${free_pct%.*}
214
-
215
- # Format tokens based on token_detail setting
216
- if [[ "$token_detail_enabled" == "true" ]]; then
217
- # Use awk for portable comma formatting (works regardless of locale)
218
- free_display=$(awk -v n="$free_tokens" 'BEGIN { printf "%\047d", n }')
219
- else
220
- free_display=$(awk "BEGIN {printf \"%.1fk\", $free_tokens / 1000}")
221
- fi
222
-
223
- # Color based on free percentage
224
- if [[ "$free_pct_int" -gt 50 ]]; then
225
- ctx_color="$GREEN"
226
- elif [[ "$free_pct_int" -gt 25 ]]; then
227
- ctx_color="$YELLOW"
228
- else
229
- ctx_color="$RED"
230
- fi
231
-
232
- context_info=" | ${ctx_color}${free_display} free (${free_pct}%)${RESET}"
233
-
234
- # Calculate and display token delta if enabled
235
- if [[ "$show_delta_enabled" == "true" ]]; then
236
- # Use session_id for per-session state (avoids conflicts with parallel sessions)
237
- state_dir=~/.claude/statusline
238
- mkdir -p "$state_dir"
239
-
240
- # Migrate old state files from ~/.claude/ to ~/.claude/statusline/ (one-time migration)
241
- old_state_dir=~/.claude
242
- for old_file in "$old_state_dir"/statusline*.state; do
243
- if [[ -f "$old_file" ]]; then
244
- new_file="${state_dir}/$(basename "$old_file")"
245
- if [[ ! -f "$new_file" ]]; then
246
- mv "$old_file" "$new_file" 2>/dev/null || true
247
- else
248
- # New file exists, just remove old one
249
- rm -f "$old_file" 2>/dev/null || true
250
- fi
251
- fi
252
- done
253
-
254
- if [[ -n "$session_id" ]]; then
255
- state_file=${state_dir}/statusline.${session_id}.state
256
- else
257
- state_file=${state_dir}/statusline.state
258
- fi
259
- has_prev=false
260
- prev_tokens=0
261
- if [[ -f "$state_file" ]]; then
262
- 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]
265
- last_line=$(tail -1 "$state_file" 2>/dev/null)
266
- if [[ -n "$last_line" ]]; then
267
- prev_cur_in=$(echo "$last_line" | cut -d',' -f4)
268
- prev_cache_create=$(echo "$last_line" | cut -d',' -f6)
269
- prev_cache_read=$(echo "$last_line" | cut -d',' -f7)
270
- prev_tokens=$(( ${prev_cur_in:-0} + ${prev_cache_create:-0} + ${prev_cache_read:-0} ))
271
- fi
272
- 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 }')
280
- else
281
- delta_display=$(awk "BEGIN {printf \"%.1fk\", $delta / 1000}")
282
- fi
283
- delta_info=" ${DIM}[+${delta_display}]${RESET}"
284
- fi
285
- # Only append if context usage changed (avoid duplicates from multiple refreshes)
286
- cur_input_tokens=$(echo "$current_usage" | jq -r '.input_tokens // 0')
287
- cur_output_tokens=$(echo "$current_usage" | jq -r '.output_tokens // 0')
288
- 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
- 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"
292
- fi
293
- fi
294
- fi
295
-
296
- # Display session_id if enabled
297
- if [[ "$show_session_enabled" == "true" && -n "$session_id" ]]; then
298
- session_info=" ${DIM}${session_id}${RESET}"
299
- fi
300
-
301
- # Output: [Model] directory | branch [changes] | XXk free (XX%) [+delta] [AC] [S:session_id]
302
- base="${DIM}[${model}]${RESET} ${BLUE}${dir_name}${RESET}"
303
- max_width=$(get_terminal_width)
304
- fit_to_width "$max_width" "$base" "$git_info" "$context_info" "$delta_info" "$ac_info" "$session_info"
@@ -1,88 +0,0 @@
1
- #!/bin/bash
2
- # Git-aware status line - shows model, directory, and git branch
3
- # Usage: Copy to ~/.claude/statusline.sh and make executable
4
-
5
- # Colors
6
- BLUE='\033[0;34m'
7
- MAGENTA='\033[0;35m'
8
- CYAN='\033[0;36m'
9
- RESET='\033[0m'
10
-
11
- input=$(cat)
12
-
13
- MODEL_DISPLAY=$(echo "$input" | jq -r '.model.display_name // "Claude"')
14
- CURRENT_DIR=$(echo "$input" | jq -r '.workspace.current_dir // "~"')
15
- DIR_NAME="${CURRENT_DIR##*/}"
16
-
17
- # Width-fitting helpers
18
- visible_width() {
19
- local stripped
20
- stripped=$(printf '%s' "$1" | sed -e $'s/\033\[[0-9;]*m//g' -e 's/\\033\[[0-9;]*m//g')
21
- printf '%s' "$stripped" | wc -m | tr -d ' '
22
- }
23
-
24
- get_terminal_width() {
25
- # When running inside Claude Code's statusline subprocess, $COLUMNS is not set
26
- # and tput falls back to 80. If COLUMNS is set, trust it. Otherwise use 200
27
- # so no parts are dropped; Claude Code handles overflow.
28
- if [[ -n "$COLUMNS" ]]; then
29
- echo "$COLUMNS"
30
- else
31
- local cols
32
- cols=$(tput cols 2>/dev/null || echo 80)
33
- if [[ "$cols" -eq 80 ]]; then
34
- echo 200
35
- else
36
- echo "$cols"
37
- fi
38
- fi
39
- }
40
-
41
- fit_to_width() {
42
- local max_width=$1
43
- shift
44
- local parts=("$@")
45
-
46
- if [[ ${#parts[@]} -eq 0 ]]; then
47
- echo ""
48
- return
49
- fi
50
-
51
- local result="${parts[0]}"
52
- local current_width
53
- current_width=$(visible_width "$result")
54
-
55
- for ((i = 1; i < ${#parts[@]}; i++)); do
56
- local part="${parts[$i]}"
57
- if [[ -z "$part" ]]; then
58
- continue
59
- fi
60
- local part_width
61
- part_width=$(visible_width "$part")
62
- if (( current_width + part_width <= max_width )); then
63
- result+="$part"
64
- (( current_width += part_width ))
65
- fi
66
- done
67
-
68
- echo -e "$result"
69
- }
70
-
71
- # Git branch detection
72
- GIT_INFO=""
73
- if git -C "$CURRENT_DIR" rev-parse --git-dir > /dev/null 2>&1; then
74
- BRANCH=$(git -C "$CURRENT_DIR" branch --show-current 2>/dev/null)
75
- if [ -n "$BRANCH" ]; then
76
- # Count uncommitted changes
77
- CHANGES=$(git -C "$CURRENT_DIR" status --porcelain 2>/dev/null | wc -l | tr -d ' ')
78
- if [ "$CHANGES" -gt 0 ]; then
79
- GIT_INFO=" | ${MAGENTA}${BRANCH}${RESET} ${CYAN}[${CHANGES}]${RESET}"
80
- else
81
- GIT_INFO=" | ${MAGENTA}${BRANCH}${RESET}"
82
- fi
83
- fi
84
- fi
85
-
86
- base="[${MODEL_DISPLAY}] ${BLUE}${DIR_NAME}${RESET}"
87
- max_width=$(get_terminal_width)
88
- fit_to_width "$max_width" "$base" "$GIT_INFO"
@@ -1,67 +0,0 @@
1
- #!/bin/bash
2
- # Minimal status line - shows model and current directory
3
- # Usage: Copy to ~/.claude/statusline.sh and make executable
4
-
5
- input=$(cat)
6
-
7
- MODEL_DISPLAY=$(echo "$input" | jq -r '.model.display_name // "Claude"')
8
- CURRENT_DIR=$(echo "$input" | jq -r '.workspace.current_dir // "~"')
9
- DIR_NAME="${CURRENT_DIR##*/}"
10
-
11
- # Width-fitting helpers
12
- visible_width() {
13
- local stripped
14
- stripped=$(printf '%s' "$1" | sed -e $'s/\033\[[0-9;]*m//g' -e 's/\\033\[[0-9;]*m//g')
15
- printf '%s' "$stripped" | wc -m | tr -d ' '
16
- }
17
-
18
- get_terminal_width() {
19
- # When running inside Claude Code's statusline subprocess, $COLUMNS is not set
20
- # and tput falls back to 80. If COLUMNS is set, trust it. Otherwise use 200
21
- # so no parts are dropped; Claude Code handles overflow.
22
- if [[ -n "$COLUMNS" ]]; then
23
- echo "$COLUMNS"
24
- else
25
- local cols
26
- cols=$(tput cols 2>/dev/null || echo 80)
27
- if [[ "$cols" -eq 80 ]]; then
28
- echo 200
29
- else
30
- echo "$cols"
31
- fi
32
- fi
33
- }
34
-
35
- fit_to_width() {
36
- local max_width=$1
37
- shift
38
- local parts=("$@")
39
-
40
- if [[ ${#parts[@]} -eq 0 ]]; then
41
- echo ""
42
- return
43
- fi
44
-
45
- local result="${parts[0]}"
46
- local current_width
47
- current_width=$(visible_width "$result")
48
-
49
- for ((i = 1; i < ${#parts[@]}; i++)); do
50
- local part="${parts[$i]}"
51
- if [[ -z "$part" ]]; then
52
- continue
53
- fi
54
- local part_width
55
- part_width=$(visible_width "$part")
56
- if (( current_width + part_width <= max_width )); then
57
- result+="$part"
58
- (( current_width += part_width ))
59
- fi
60
- done
61
-
62
- echo -e "$result"
63
- }
64
-
65
- base="[$MODEL_DISPLAY] $DIR_NAME"
66
- max_width=$(get_terminal_width)
67
- fit_to_width "$max_width" "$base"