skill-statusline 2.1.1 → 2.2.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/bin/statusline.sh CHANGED
@@ -1,121 +1,127 @@
1
- #!/usr/bin/env bash
2
- # skill-statusline v2.0 — Entry point
3
- # Delegates to modular v2 engine if installed, falls back to v1 inline
4
-
5
- STATUSLINE_DIR="${HOME}/.claude/statusline"
6
-
7
- if [ -f "${STATUSLINE_DIR}/core.sh" ]; then
8
- # v2: modular engine with themes, layouts, accurate context
9
- exec bash "${STATUSLINE_DIR}/core.sh"
10
- fi
11
-
12
- # ── v1 fallback (inline legacy script) ──
13
- # This runs if only statusline-command.sh was copied without the statusline/ directory
14
-
15
- input=$(cat)
16
-
17
- json_val() {
18
- echo "$input" | grep -o "\"$1\"[[:space:]]*:[[:space:]]*\"[^\"]*\"" | head -1 | sed 's/.*:.*"\(.*\)"/\1/'
19
- }
20
- json_num() {
21
- echo "$input" | grep -o "\"$1\"[[:space:]]*:[[:space:]]*[0-9.]*" | head -1 | sed 's/.*:[[:space:]]*//'
22
- }
23
- to_fwd() {
24
- echo "$1" | tr '\\' '/' | sed 's|//\+|/|g'
25
- }
26
- rpad() {
27
- local str="$1" w="$2"
28
- local plain
29
- plain=$(printf '%b' "$str" | sed $'s/\033\\[[0-9;]*m//g')
30
- local vlen=${#plain}
31
- local need=$(( w - vlen ))
32
- printf '%b' "$str"
33
- [ "$need" -gt 0 ] && printf "%${need}s" ""
34
- }
35
-
36
- RST='\033[0m'; BOLD='\033[1m'
37
- CYAN='\033[38;2;6;182;212m'; PURPLE='\033[38;2;168;85;247m'
38
- GREEN='\033[38;2;34;197;94m'; YELLOW='\033[38;2;245;158;11m'
39
- RED='\033[38;2;239;68;68m'; ORANGE='\033[38;2;251;146;60m'
40
- WHITE='\033[38;2;228;228;231m'; PINK='\033[38;2;236;72;153m'
41
- SEP_C='\033[38;2;55;55;62m'; DIM_BAR='\033[38;2;40;40;45m'
42
-
43
- cwd=$(json_val "current_dir")
44
- [ -z "$cwd" ] && cwd=$(json_val "cwd")
45
- if [ -z "$cwd" ]; then dir_label="~"; clean_cwd=""
46
- else
47
- clean_cwd=$(to_fwd "$cwd")
48
- dir_label=$(echo "$clean_cwd" | awk -F'/' '{if(NF>3) print $(NF-2)"/"$(NF-1)"/"$NF; else if(NF>2) print $(NF-1)"/"$NF; else print $0}')
49
- [ -z "$dir_label" ] && dir_label="~"
50
- fi
51
-
52
- model_display=$(json_val "display_name"); model_id=$(json_val "id")
53
- [ -z "$model_display" ] && model_display="unknown"
54
- model_ver=""; [ -n "$model_id" ] && model_ver=$(echo "$model_id" | sed -n 's/.*-\([0-9]*\)-\([0-9]*\)$/\1.\2/p')
55
- if [ -n "$model_ver" ] && ! echo "$model_display" | grep -q '[0-9]'; then model_full="${model_display} ${model_ver}"; else model_full="$model_display"; fi
56
-
57
- pct=$(json_num "used_percentage"); [ -z "$pct" ] && pct="0"; pct=$(echo "$pct" | cut -d. -f1)
58
- if [ "$pct" -gt 75 ] 2>/dev/null; then CTX_CLR="$RED"
59
- elif [ "$pct" -gt 40 ] 2>/dev/null; then CTX_CLR="$ORANGE"
60
- else CTX_CLR="$WHITE"; fi
61
- BAR_WIDTH=40; filled=$(( pct * BAR_WIDTH / 100 )); [ "$filled" -gt "$BAR_WIDTH" ] && filled=$BAR_WIDTH
62
- empty=$(( BAR_WIDTH - filled )); bar_filled=""; bar_empty=""
63
- i=0; while [ $i -lt $filled ]; do bar_filled="${bar_filled}█"; i=$((i+1)); done
64
- i=0; while [ $i -lt $empty ]; do bar_empty="${bar_empty}░"; i=$((i+1)); done
65
- ctx_bar="${CTX_CLR}${bar_filled}${RST}${DIM_BAR}${bar_empty}${RST} ${CTX_CLR}${pct}%${RST}"
66
-
67
- branch="no-git"; gh_user=""; gh_repo=""; git_dirty=""
68
- if [ -n "$clean_cwd" ]; then
69
- branch=$(git --no-optional-locks -C "$clean_cwd" symbolic-ref --short HEAD 2>/dev/null)
70
- [ -z "$branch" ] && branch=$(git --no-optional-locks -C "$clean_cwd" rev-parse --short HEAD 2>/dev/null)
71
- if [ -n "$branch" ]; then
72
- remote_url=$(git --no-optional-locks -C "$clean_cwd" remote get-url origin 2>/dev/null)
73
- if [ -n "$remote_url" ]; then
74
- gh_user=$(echo "$remote_url" | sed 's|.*github\.com[:/]\([^/]*\)/.*|\1|'); [ "$gh_user" = "$remote_url" ] && gh_user=""
75
- gh_repo=$(echo "$remote_url" | sed 's|.*/\([^/]*\)\.git$|\1|; s|.*/\([^/]*\)$|\1|'); [ "$gh_repo" = "$remote_url" ] && gh_repo=""
76
- fi
77
- git --no-optional-locks -C "$clean_cwd" diff --cached --quiet 2>/dev/null || git_dirty="${GREEN}+${RST}"
78
- git --no-optional-locks -C "$clean_cwd" diff --quiet 2>/dev/null || git_dirty="${git_dirty}${YELLOW}~${RST}"
79
- fi
80
- [ -z "$branch" ] && branch="no-git"
81
- fi
82
- [ -n "$gh_repo" ] && gh_label="${gh_user}/${gh_repo}/${branch}" || gh_label="$branch"
83
-
84
- cost_raw=$(json_num "total_cost_usd")
85
- if [ -z "$cost_raw" ] || [ "$cost_raw" = "0" ]; then cost_label='$0.00'
86
- else cost_label=$(awk -v c="$cost_raw" 'BEGIN { if (c < 0.01) printf "$%.4f", c; else printf "$%.2f", c }'); fi
87
-
88
- total_in=$(json_num "total_input_tokens"); total_out=$(json_num "total_output_tokens")
89
- [ -z "$total_in" ] && total_in="0"; [ -z "$total_out" ] && total_out="0"
90
- fmt_tok() { awk -v t="$1" 'BEGIN { if (t >= 1000000) printf "%.1fM", t/1000000; else if (t >= 1000) printf "%.0fk", t/1000; else printf "%d", t }'; }
91
- tok_in=$(fmt_tok "$total_in"); tok_out=$(fmt_tok "$total_out")
92
- tok_total=$(awk -v i="$total_in" -v o="$total_out" 'BEGIN { printf "%d", i + o }')
93
- token_label="${tok_in} + ${tok_out} = $(fmt_tok "$tok_total")"
94
-
95
- skill_label="Idle"
96
- if [ -n "$clean_cwd" ]; then
97
- search_path="$clean_cwd"
98
- while [ -n "$search_path" ] && [ "$search_path" != "/" ]; do
99
- proj_hash=$(echo "$search_path" | sed 's|^/\([a-zA-Z]\)/|\U\1--|; s|^[A-Z]:/|&|; s|:/|--|; s|/|-|g')
100
- proj_dir="$HOME/.claude/projects/${proj_hash}"
101
- if [ -d "$proj_dir" ]; then tpath=$(ls -t "$proj_dir"/*.jsonl 2>/dev/null | head -1); [ -n "$tpath" ] && break; fi
102
- search_path=$(echo "$search_path" | sed 's|/[^/]*$||')
103
- done
104
- if [ -n "$tpath" ] && [ -f "$tpath" ]; then
105
- recent_block=$(tail -200 "$tpath" 2>/dev/null)
106
- last_tool=$(echo "$recent_block" | grep -o '"type":"tool_use","id":"[^"]*","name":"[^"]*"' | tail -1 | sed 's/.*"name":"\([^"]*\)".*/\1/')
107
- if [ -n "$last_tool" ]; then
108
- case "$last_tool" in
109
- Task) skill_label="Agent" ;; Read) skill_label="Read" ;; Write) skill_label="Write" ;;
110
- Edit) skill_label="Edit" ;; Glob) skill_label="Search(Files)" ;; Grep) skill_label="Search(Content)" ;;
111
- Bash) skill_label="Terminal" ;; WebSearch) skill_label="Web Search" ;; *) skill_label="$last_tool" ;;
112
- esac
113
- fi
114
- fi
115
- fi
116
-
117
- C1=38; S=$(printf '%b' " ${SEP_C}│${RST} ")
118
- printf ' '; rpad "${PINK}Skill:${RST} ${PINK}${skill_label}${RST}" "$C1"; printf '%b' "$S"; printf '%b\n' "${WHITE}GitHub:${RST} ${WHITE}${gh_label}${RST}${git_dirty}"
119
- printf ' '; rpad "${PURPLE}Model:${RST} ${PURPLE}${BOLD}${model_full}${RST}" "$C1"; printf '%b' "$S"; printf '%b\n' "${CYAN}Dir:${RST} ${CYAN}${dir_label}${RST}"
120
- printf ' '; rpad "${YELLOW}Tokens:${RST} ${YELLOW}${token_label}${RST}" "$C1"; printf '%b' "$S"; printf '%b\n' "${GREEN}Cost:${RST} ${GREEN}${cost_label}${RST}"
121
- printf ' '; printf '%b' "${CTX_CLR}Context:${RST} ${ctx_bar}"
1
+ #!/usr/bin/env bash
2
+ # skill-statusline v2.0 — Entry point
3
+ # Delegates to modular v2 engine if installed, falls back to v1 inline
4
+
5
+ STATUSLINE_DIR="${HOME}/.claude/statusline"
6
+
7
+ if [ -f "${STATUSLINE_DIR}/core.sh" ]; then
8
+ # v2: modular engine with themes, layouts, accurate context
9
+ exec bash "${STATUSLINE_DIR}/core.sh"
10
+ fi
11
+
12
+ # ── v1 fallback (inline legacy script) ──
13
+ # This runs if only statusline-command.sh was copied without the statusline/ directory
14
+
15
+ # Read stdin with protection against missing pipe
16
+ if [ -t 0 ]; then
17
+ exit 0
18
+ fi
19
+ input=$(timeout 2 cat 2>/dev/null)
20
+ if [ -z "$input" ]; then
21
+ exit 0
22
+ fi
23
+
24
+ json_val() {
25
+ echo "$input" | grep -o "\"$1\"[[:space:]]*:[[:space:]]*\"[^\"]*\"" | head -1 | sed 's/.*:.*"\(.*\)"/\1/'
26
+ }
27
+ json_num() {
28
+ echo "$input" | grep -o "\"$1\"[[:space:]]*:[[:space:]]*[0-9.]*" | head -1 | sed 's/.*:[[:space:]]*//'
29
+ }
30
+ to_fwd() {
31
+ echo "$1" | tr '\\' '/' | sed 's|//\+|/|g'
32
+ }
33
+ rpad() {
34
+ local str="$1" w="$2"
35
+ local plain
36
+ plain=$(printf '%b' "$str" | sed $'s/\033\\[[0-9;]*m//g')
37
+ local vlen=${#plain}
38
+ local need=$(( w - vlen ))
39
+ printf '%b' "$str"
40
+ [ "$need" -gt 0 ] && printf "%${need}s" ""
41
+ }
42
+
43
+ RST='\033[0m'; BOLD='\033[1m'
44
+ CYAN='\033[38;2;6;182;212m'; PURPLE='\033[38;2;168;85;247m'
45
+ GREEN='\033[38;2;34;197;94m'; YELLOW='\033[38;2;245;158;11m'
46
+ RED='\033[38;2;239;68;68m'; ORANGE='\033[38;2;251;146;60m'
47
+ WHITE='\033[38;2;228;228;231m'; PINK='\033[38;2;236;72;153m'
48
+ SEP_C='\033[38;2;55;55;62m'; DIM_BAR='\033[38;2;40;40;45m'
49
+
50
+ cwd=$(json_val "current_dir")
51
+ [ -z "$cwd" ] && cwd=$(json_val "cwd")
52
+ if [ -z "$cwd" ]; then dir_label="~"; clean_cwd=""
53
+ else
54
+ clean_cwd=$(to_fwd "$cwd")
55
+ dir_label=$(echo "$clean_cwd" | awk -F'/' '{if(NF>3) print $(NF-2)"/"$(NF-1)"/"$NF; else if(NF>2) print $(NF-1)"/"$NF; else print $0}')
56
+ [ -z "$dir_label" ] && dir_label="~"
57
+ fi
58
+
59
+ model_display=$(json_val "display_name"); model_id=$(json_val "id")
60
+ [ -z "$model_display" ] && model_display="unknown"
61
+ model_ver=""; [ -n "$model_id" ] && model_ver=$(echo "$model_id" | sed -n 's/.*-\([0-9]*\)-\([0-9]*\)$/\1.\2/p')
62
+ if [ -n "$model_ver" ] && ! echo "$model_display" | grep -q '[0-9]'; then model_full="${model_display} ${model_ver}"; else model_full="$model_display"; fi
63
+
64
+ pct=$(json_num "used_percentage"); [ -z "$pct" ] && pct="0"; pct=$(echo "$pct" | cut -d. -f1)
65
+ if [ "$pct" -gt 75 ] 2>/dev/null; then CTX_CLR="$RED"
66
+ elif [ "$pct" -gt 40 ] 2>/dev/null; then CTX_CLR="$ORANGE"
67
+ else CTX_CLR="$WHITE"; fi
68
+ BAR_WIDTH=40; filled=$(( pct * BAR_WIDTH / 100 )); [ "$filled" -gt "$BAR_WIDTH" ] && filled=$BAR_WIDTH
69
+ empty=$(( BAR_WIDTH - filled )); bar_filled=""; bar_empty=""
70
+ i=0; while [ $i -lt $filled ]; do bar_filled="${bar_filled}█"; i=$((i+1)); done
71
+ i=0; while [ $i -lt $empty ]; do bar_empty="${bar_empty}░"; i=$((i+1)); done
72
+ ctx_bar="${CTX_CLR}${bar_filled}${RST}${DIM_BAR}${bar_empty}${RST} ${CTX_CLR}${pct}%${RST}"
73
+
74
+ branch="no-git"; gh_user=""; gh_repo=""; git_dirty=""
75
+ if [ -n "$clean_cwd" ]; then
76
+ branch=$(timeout 2 git --no-optional-locks -C "$clean_cwd" symbolic-ref --short HEAD 2>/dev/null)
77
+ [ -z "$branch" ] && branch=$(timeout 2 git --no-optional-locks -C "$clean_cwd" rev-parse --short HEAD 2>/dev/null)
78
+ if [ -n "$branch" ]; then
79
+ remote_url=$(timeout 2 git --no-optional-locks -C "$clean_cwd" remote get-url origin 2>/dev/null)
80
+ if [ -n "$remote_url" ]; then
81
+ gh_user=$(echo "$remote_url" | sed 's|.*github\.com[:/]\([^/]*\)/.*|\1|'); [ "$gh_user" = "$remote_url" ] && gh_user=""
82
+ gh_repo=$(echo "$remote_url" | sed 's|.*/\([^/]*\)\.git$|\1|; s|.*/\([^/]*\)$|\1|'); [ "$gh_repo" = "$remote_url" ] && gh_repo=""
83
+ fi
84
+ timeout 2 git --no-optional-locks -C "$clean_cwd" diff --cached --quiet 2>/dev/null || git_dirty="${GREEN}+${RST}"
85
+ timeout 2 git --no-optional-locks -C "$clean_cwd" diff --quiet 2>/dev/null || git_dirty="${git_dirty}${YELLOW}~${RST}"
86
+ fi
87
+ [ -z "$branch" ] && branch="no-git"
88
+ fi
89
+ [ -n "$gh_repo" ] && gh_label="${gh_user}/${gh_repo}/${branch}" || gh_label="$branch"
90
+
91
+ cost_raw=$(json_num "total_cost_usd")
92
+ if [ -z "$cost_raw" ] || [ "$cost_raw" = "0" ]; then cost_label='$0.00'
93
+ else cost_label=$(awk -v c="$cost_raw" 'BEGIN { if (c < 0.01) printf "$%.4f", c; else printf "$%.2f", c }'); fi
94
+
95
+ total_in=$(json_num "total_input_tokens"); total_out=$(json_num "total_output_tokens")
96
+ [ -z "$total_in" ] && total_in="0"; [ -z "$total_out" ] && total_out="0"
97
+ fmt_tok() { awk -v t="$1" 'BEGIN { if (t >= 1000000) printf "%.1fM", t/1000000; else if (t >= 1000) printf "%.0fk", t/1000; else printf "%d", t }'; }
98
+ tok_in=$(fmt_tok "$total_in"); tok_out=$(fmt_tok "$total_out")
99
+ tok_total=$(awk -v i="$total_in" -v o="$total_out" 'BEGIN { printf "%d", i + o }')
100
+ token_label="${tok_in} + ${tok_out} = $(fmt_tok "$tok_total")"
101
+
102
+ skill_label="Idle"
103
+ if [ -n "$clean_cwd" ]; then
104
+ search_path="$clean_cwd"
105
+ while [ -n "$search_path" ] && [ "$search_path" != "/" ]; do
106
+ proj_hash=$(echo "$search_path" | sed 's|^/\([a-zA-Z]\)/|\U\1--|; s|^[A-Z]:/|&|; s|:/|--|; s|/|-|g')
107
+ proj_dir="$HOME/.claude/projects/${proj_hash}"
108
+ if [ -d "$proj_dir" ]; then tpath=$(ls -t "$proj_dir"/*.jsonl 2>/dev/null | head -1); [ -n "$tpath" ] && break; fi
109
+ search_path=$(echo "$search_path" | sed 's|/[^/]*$||')
110
+ done
111
+ if [ -n "$tpath" ] && [ -f "$tpath" ]; then
112
+ last_tool=$(tail -50 "$tpath" 2>/dev/null | grep -o '"type":"tool_use","id":"[^"]*","name":"[^"]*"' | tail -1 | sed 's/.*"name":"\([^"]*\)".*/\1/')
113
+ if [ -n "$last_tool" ]; then
114
+ case "$last_tool" in
115
+ Task) skill_label="Agent" ;; Read) skill_label="Read" ;; Write) skill_label="Write" ;;
116
+ Edit) skill_label="Edit" ;; Glob) skill_label="Search(Files)" ;; Grep) skill_label="Search(Content)" ;;
117
+ Bash) skill_label="Terminal" ;; WebSearch) skill_label="Web Search" ;; *) skill_label="$last_tool" ;;
118
+ esac
119
+ fi
120
+ fi
121
+ fi
122
+
123
+ C1=38; S=$(printf '%b' " ${SEP_C}│${RST} ")
124
+ printf ' '; rpad "${PINK}Skill:${RST} ${PINK}${skill_label}${RST}" "$C1"; printf '%b' "$S"; printf '%b\n' "${WHITE}GitHub:${RST} ${WHITE}${gh_label}${RST}${git_dirty}"
125
+ printf ' '; rpad "${PURPLE}Model:${RST} ${PURPLE}${BOLD}${model_full}${RST}" "$C1"; printf '%b' "$S"; printf '%b\n' "${CYAN}Dir:${RST} ${CYAN}${dir_label}${RST}"
126
+ printf ' '; rpad "${YELLOW}Tokens:${RST} ${YELLOW}${token_label}${RST}" "$C1"; printf '%b' "$S"; printf '%b\n' "${GREEN}Cost:${RST} ${GREEN}${cost_label}${RST}"
127
+ printf ' '; printf '%b' "${CTX_CLR}Context:${RST} ${ctx_bar}"