skill-statusline 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Thinqmesh Technologies
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # Claude Code Statusline
2
+
3
+ A rich, customizable statusline for Claude Code with colored legends, context progress bar, GitHub info, token tracking, and active skill display.
4
+
5
+ ## Layout
6
+
7
+ ```
8
+ Skill: Edit │ GitHub: User/Repo/master+~
9
+ Model: Opus 4.6 │ Dir: Downloads/Project
10
+ Tokens: 25k + 12k = 37k │ Cost: $1.23
11
+ Context: ████████████████████░░░░░░░░░░░░░░░░░░░░ 50%
12
+ ```
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ npx skill-statusline install
18
+ ```
19
+
20
+ Or install globally:
21
+
22
+ ```bash
23
+ npm install -g skill-statusline
24
+ ccsl install
25
+ ```
26
+
27
+ Then restart Claude Code.
28
+
29
+ ## Uninstall
30
+
31
+ ```bash
32
+ ccsl uninstall
33
+ ```
34
+
35
+ ## Fields
36
+
37
+ | Field | Color | Description |
38
+ |---------|------------------|-------------|
39
+ | Skill | Pink | Last tool used (Read, Write, Edit, Terminal, Agent, etc.) |
40
+ | Model | Purple | Active model name and version |
41
+ | GitHub | White | username/repo/branch with +~ dirty indicators |
42
+ | Dir | Cyan | Last 3 segments of working directory |
43
+ | Tokens | Yellow | Input + Output = Total (e.g., 25k + 12k = 37k) |
44
+ | Cost | Green | Session cost in USD |
45
+ | Context | White/Orange/Red | 40-char progress bar (white ≤40%, orange 41-75%, red >75%) |
46
+
47
+ ## Requirements
48
+
49
+ - Bash shell (Git Bash on Windows, or any Unix shell)
50
+ - Git (for GitHub field)
51
+ - Works on Windows, macOS, Linux
52
+
53
+ ## What it installs
54
+
55
+ - `~/.claude/statusline-command.sh` — the statusline script
56
+ - Updates `~/.claude/settings.json` — adds `statusLine` config
57
+
58
+ ## Part of the Thinqmesh Skills Ecosystem
59
+
60
+ This statusline is also bundled with [codebase-context-skill](https://www.npmjs.com/package/codebase-context-skill) — context engineering middleware for Claude Code with 23 slash commands, 6 agents, and session persistence.
61
+
62
+ ```bash
63
+ npx codebase-context-skill init
64
+ ```
65
+
66
+ ## License
67
+
68
+ MIT — [Thinqmesh Technologies](https://thinqmesh.com)
package/bin/cli.js ADDED
@@ -0,0 +1,187 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const os = require('os');
6
+
7
+ const args = process.argv.slice(2);
8
+ const command = args[0];
9
+ const VERSION = '1.0.0';
10
+
11
+ const PKG_DIR = path.resolve(__dirname, '..');
12
+ const HOME = os.homedir();
13
+
14
+ // Terminal colors
15
+ const R = '\x1b[0m';
16
+ const B = '\x1b[1m';
17
+ const D = '\x1b[2m';
18
+ const GRN = '\x1b[32m';
19
+ const YLW = '\x1b[33m';
20
+ const CYN = '\x1b[36m';
21
+ const WHT = '\x1b[97m';
22
+ const PURPLE = '\x1b[38;2;168;85;247m';
23
+ const PINK = '\x1b[38;2;236;72;153m';
24
+ const TEAL = '\x1b[38;2;6;182;212m';
25
+ const GRAY = '\x1b[38;2;90;90;99m';
26
+
27
+ function log(msg) { console.log(msg); }
28
+ function success(msg) { log(` ${GRAY}\u2502${R} ${GRN}\u2713${R} ${msg}`); }
29
+ function warn(msg) { log(` ${GRAY}\u2502${R} ${YLW}\u26A0${R} ${msg}`); }
30
+ function info(msg) { log(` ${GRAY}\u2502${R} ${CYN}\u2139${R} ${msg}`); }
31
+ function bar(msg) { log(` ${GRAY}\u2502${R} ${D}${msg}${R}`); }
32
+ function blank() { log(` ${GRAY}\u2502${R}`); }
33
+
34
+ function header() {
35
+ log('');
36
+ log(` ${GRAY}\u250C${''.padEnd(58, '\u2500')}\u2510${R}`);
37
+ log(` ${GRAY}\u2502${R} ${GRAY}\u2502${R}`);
38
+ log(` ${GRAY}\u2502${R} ${PURPLE}${B}\u2588\u2588\u2588${R} ${PINK}${B}\u2588\u2588\u2588${R} ${WHT}${B}skill-statusline${R} ${D}v${VERSION}${R} ${GRAY}\u2502${R}`);
39
+ log(` ${GRAY}\u2502${R} ${PURPLE}\u2588${R} ${PINK}\u2588${R} ${PURPLE}\u2588${R} ${D}Rich statusline for Claude Code${R} ${GRAY}\u2502${R}`);
40
+ log(` ${GRAY}\u2502${R} ${PURPLE}${B}\u2588\u2588\u2588${R} ${PINK}${B}\u2588\u2588\u2588${R} ${GRAY}\u2502${R}`);
41
+ log(` ${GRAY}\u2502${R} ${GRAY}\u2502${R}`);
42
+ log(` ${GRAY}\u2502${R} ${TEAL}Thinqmesh Technologies${R} ${GRAY}\u2502${R}`);
43
+ log(` ${GRAY}\u2502${R} ${GRAY}skills.thinqmesh.com${R} ${GRAY}\u2502${R}`);
44
+ log(` ${GRAY}\u2502${R} ${GRAY}\u2502${R}`);
45
+ log(` ${GRAY}\u251C${''.padEnd(58, '\u2500')}\u2524${R}`);
46
+ }
47
+
48
+ function footer() {
49
+ log(` ${GRAY}\u2502${R}`);
50
+ log(` ${GRAY}\u2514${''.padEnd(58, '\u2500')}\u2518${R}`);
51
+ log('');
52
+ }
53
+
54
+ function install() {
55
+ header();
56
+ blank();
57
+ info(`${B}Installing statusline${R} to ~/.claude/`);
58
+ blank();
59
+
60
+ const claudeDir = path.join(HOME, '.claude');
61
+ if (!fs.existsSync(claudeDir)) fs.mkdirSync(claudeDir, { recursive: true });
62
+
63
+ // Copy statusline script
64
+ const slSrc = path.join(PKG_DIR, 'bin', 'statusline.sh');
65
+ const slDest = path.join(claudeDir, 'statusline-command.sh');
66
+ fs.copyFileSync(slSrc, slDest);
67
+ success(`${B}statusline-command.sh${R} copied to ~/.claude/`);
68
+
69
+ // Merge into ~/.claude/settings.json
70
+ const settingsPath = path.join(claudeDir, 'settings.json');
71
+ let settings = {};
72
+ if (fs.existsSync(settingsPath)) {
73
+ try { settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); } catch (e) {}
74
+ }
75
+ if (!settings.statusLine) {
76
+ settings.statusLine = {
77
+ type: 'command',
78
+ command: 'bash ~/.claude/statusline-command.sh'
79
+ };
80
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
81
+ success(`${B}statusLine${R} config added to ~/.claude/settings.json`);
82
+ } else {
83
+ success(`statusLine already configured in settings.json`);
84
+ }
85
+
86
+ blank();
87
+ log(` ${GRAY}\u251C${''.padEnd(58, '\u2500')}\u2524${R}`);
88
+ blank();
89
+ log(` ${GRAY}\u2502${R} ${GRN}${B}Ready.${R} Restart Claude Code to see the statusline.`);
90
+ blank();
91
+ log(` ${GRAY}\u2502${R} ${WHT}${B}Layout:${R}`);
92
+ blank();
93
+ log(` ${GRAY}\u2502${R} ${PINK}Skill:${R} Edit ${GRAY}\u2502${R} ${WHT}GitHub:${R} User/Repo/main`);
94
+ log(` ${GRAY}\u2502${R} ${PURPLE}Model:${R} Opus 4.6 ${GRAY}\u2502${R} ${CYN}Dir:${R} Downloads/Project`);
95
+ log(` ${GRAY}\u2502${R} ${YLW}Tokens:${R} 25k + 12k = 37k ${GRAY}\u2502${R} ${GRN}Cost:${R} $1.23`);
96
+ log(` ${GRAY}\u2502${R} ${WHT}Context:${R} ${GRN}\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588${R}${D}\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591${R} 50%`);
97
+ blank();
98
+ bar(`${R}${D}Script:${R} ${CYN}~/.claude/statusline-command.sh${R}`);
99
+ bar(`${R}${D}Settings:${R} ${CYN}~/.claude/settings.json${R}`);
100
+ blank();
101
+ bar(`Docs ${R}${TEAL}https://skills.thinqmesh.com${R}`);
102
+ bar(`GitHub ${R}${PURPLE}https://github.com/AnitChaudhry/skill-statusline${R}`);
103
+
104
+ footer();
105
+ }
106
+
107
+ function uninstall() {
108
+ header();
109
+ blank();
110
+ info(`${B}Uninstalling statusline${R}`);
111
+ blank();
112
+
113
+ const claudeDir = path.join(HOME, '.claude');
114
+
115
+ // Remove script
116
+ const slDest = path.join(claudeDir, 'statusline-command.sh');
117
+ if (fs.existsSync(slDest)) {
118
+ fs.unlinkSync(slDest);
119
+ success(`Removed ~/.claude/statusline-command.sh`);
120
+ } else {
121
+ warn(`statusline-command.sh not found`);
122
+ }
123
+
124
+ // Remove from settings.json
125
+ const settingsPath = path.join(claudeDir, 'settings.json');
126
+ if (fs.existsSync(settingsPath)) {
127
+ try {
128
+ const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
129
+ if (settings.statusLine) {
130
+ delete settings.statusLine;
131
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
132
+ success(`Removed statusLine from settings.json`);
133
+ }
134
+ } catch (e) {
135
+ warn(`Could not parse settings.json`);
136
+ }
137
+ }
138
+
139
+ blank();
140
+ log(` ${GRAY}\u2502${R} ${GRN}${B}Done.${R} Restart Claude Code to apply.`);
141
+
142
+ footer();
143
+ }
144
+
145
+ function showHelp() {
146
+ header();
147
+ blank();
148
+ log(` ${GRAY}\u2502${R} ${WHT}${B}Usage:${R}`);
149
+ blank();
150
+ log(` ${GRAY}\u2502${R} ${CYN}ccsl install${R} Install statusline to ~/.claude/`);
151
+ log(` ${GRAY}\u2502${R} ${CYN}ccsl uninstall${R} Remove statusline`);
152
+ log(` ${GRAY}\u2502${R} ${CYN}ccsl help${R} Show this help`);
153
+ blank();
154
+ log(` ${GRAY}\u2502${R} ${WHT}${B}What it shows:${R}`);
155
+ blank();
156
+ log(` ${GRAY}\u2502${R} ${PINK}Skill${R} Last tool used (Read, Write, Terminal...)`);
157
+ log(` ${GRAY}\u2502${R} ${PURPLE}Model${R} Active model name and version`);
158
+ log(` ${GRAY}\u2502${R} ${WHT}GitHub${R} username/repo/branch with dirty indicators`);
159
+ log(` ${GRAY}\u2502${R} ${CYN}Dir${R} Last 3 segments of working directory`);
160
+ log(` ${GRAY}\u2502${R} ${YLW}Tokens${R} Input + Output = Total`);
161
+ log(` ${GRAY}\u2502${R} ${GRN}Cost${R} Session cost in USD`);
162
+ log(` ${GRAY}\u2502${R} ${WHT}Context${R} 40-char progress bar with color thresholds`);
163
+ blank();
164
+ log(` ${GRAY}\u2502${R} ${WHT}${B}Quick install:${R}`);
165
+ blank();
166
+ log(` ${GRAY}\u2502${R} ${CYN}npx skill-statusline install${R}`);
167
+ blank();
168
+ bar(`Docs ${R}${TEAL}https://skills.thinqmesh.com${R}`);
169
+ bar(`GitHub ${R}${PURPLE}https://github.com/AnitChaudhry/skill-statusline${R}`);
170
+
171
+ footer();
172
+ }
173
+
174
+ // Main
175
+ if (command === 'install' || command === 'init') {
176
+ install();
177
+ } else if (command === 'uninstall' || command === 'remove') {
178
+ uninstall();
179
+ } else if (command === 'help' || command === '--help' || command === '-h') {
180
+ showHelp();
181
+ } else {
182
+ if (command) {
183
+ log('');
184
+ warn(`Unknown command: ${command}`);
185
+ }
186
+ showHelp();
187
+ }
@@ -0,0 +1,253 @@
1
+ #!/usr/bin/env bash
2
+ # Claude Code status line — 4-row grid, no jq, cross-platform
3
+ # Row 1: Skill │ GitHub
4
+ # Row 2: Model │ Dir
5
+ # Row 3: Tokens │ Cost
6
+ # Row 4: Context (wide progress bar)
7
+
8
+ input=$(cat)
9
+
10
+ # --- Helpers ---
11
+ json_val() {
12
+ echo "$input" | grep -o "\"$1\"[[:space:]]*:[[:space:]]*\"[^\"]*\"" | head -1 | sed 's/.*:.*"\(.*\)"/\1/'
13
+ }
14
+ json_num() {
15
+ echo "$input" | grep -o "\"$1\"[[:space:]]*:[[:space:]]*[0-9.]*" | head -1 | sed 's/.*:[[:space:]]*//'
16
+ }
17
+ # Convert any path to forward slashes (safe on all OS)
18
+ to_fwd() {
19
+ echo "$1" | tr '\\' '/' | sed 's|//\+|/|g'
20
+ }
21
+
22
+ # Right-pad a colored string to a visible width
23
+ rpad() {
24
+ local str="$1" w="$2"
25
+ local plain
26
+ plain=$(printf '%b' "$str" | sed $'s/\033\\[[0-9;]*m//g')
27
+ local vlen=${#plain}
28
+ local need=$(( w - vlen ))
29
+ printf '%b' "$str"
30
+ [ "$need" -gt 0 ] && printf "%${need}s" ""
31
+ }
32
+
33
+ # --- Colors ---
34
+ RST='\033[0m'
35
+ BOLD='\033[1m'
36
+ CYAN='\033[38;2;6;182;212m'
37
+ PURPLE='\033[38;2;168;85;247m'
38
+ GREEN='\033[38;2;34;197;94m'
39
+ YELLOW='\033[38;2;245;158;11m'
40
+ RED='\033[38;2;239;68;68m'
41
+ ORANGE='\033[38;2;251;146;60m'
42
+ WHITE='\033[38;2;228;228;231m'
43
+ PINK='\033[38;2;236;72;153m'
44
+ BLUE='\033[38;2;99;102;241m'
45
+ TEAL='\033[38;2;20;184;166m'
46
+ SEP='\033[38;2;55;55;62m'
47
+ DIM_BAR='\033[38;2;40;40;45m'
48
+
49
+ # ── 1. Directory ──
50
+ cwd=$(json_val "current_dir")
51
+ [ -z "$cwd" ] && cwd=$(json_val "cwd")
52
+ if [ -z "$cwd" ]; then
53
+ dir_label="~"
54
+ clean_cwd=""
55
+ else
56
+ clean_cwd=$(to_fwd "$cwd")
57
+ 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}')
58
+ [ -z "$dir_label" ] && dir_label="~"
59
+ fi
60
+
61
+ # ── 2. Model ──
62
+ model_display=$(json_val "display_name")
63
+ model_id=$(json_val "id")
64
+ [ -z "$model_display" ] && model_display="unknown"
65
+ model_ver=""
66
+ if [ -n "$model_id" ]; then
67
+ model_ver=$(echo "$model_id" | sed -n 's/.*-\([0-9]*\)-\([0-9]*\)$/\1.\2/p')
68
+ fi
69
+ if [ -n "$model_ver" ] && ! echo "$model_display" | grep -q '[0-9]'; then
70
+ model_full="${model_display} ${model_ver}"
71
+ else
72
+ model_full="$model_display"
73
+ fi
74
+
75
+ # ── 3. Context bar (wide: 40 chars) ──
76
+ pct=$(json_num "used_percentage")
77
+ [ -z "$pct" ] && pct="0"
78
+ pct=$(echo "$pct" | cut -d. -f1)
79
+ if [ "$pct" -gt 75 ] 2>/dev/null; then
80
+ CTX_CLR="$RED"
81
+ elif [ "$pct" -gt 40 ] 2>/dev/null; then
82
+ CTX_CLR="$ORANGE"
83
+ else
84
+ CTX_CLR="$WHITE"
85
+ fi
86
+ BAR_WIDTH=40
87
+ filled=$(( pct * BAR_WIDTH / 100 ))
88
+ [ "$filled" -gt "$BAR_WIDTH" ] && filled=$BAR_WIDTH
89
+ empty=$(( BAR_WIDTH - filled ))
90
+ bar_filled=""; bar_empty=""
91
+ i=0; while [ $i -lt $filled ]; do bar_filled="${bar_filled}█"; i=$((i+1)); done
92
+ i=0; while [ $i -lt $empty ]; do bar_empty="${bar_empty}░"; i=$((i+1)); done
93
+ ctx_bar="${CTX_CLR}${bar_filled}${RST}${DIM_BAR}${bar_empty}${RST} ${CTX_CLR}${pct}%${RST}"
94
+
95
+ # ── 4. GitHub: user/repo/branch + dirty ──
96
+ branch="no-git"
97
+ gh_user=""
98
+ gh_repo=""
99
+ git_dirty=""
100
+ if [ -n "$clean_cwd" ]; then
101
+ branch=$(git --no-optional-locks -C "$clean_cwd" symbolic-ref --short HEAD 2>/dev/null)
102
+ [ -z "$branch" ] && branch=$(git --no-optional-locks -C "$clean_cwd" rev-parse --short HEAD 2>/dev/null)
103
+ if [ -n "$branch" ]; then
104
+ remote_url=$(git --no-optional-locks -C "$clean_cwd" remote get-url origin 2>/dev/null)
105
+ if [ -n "$remote_url" ]; then
106
+ gh_user=$(echo "$remote_url" | sed 's|.*github\.com[:/]\([^/]*\)/.*|\1|')
107
+ [ "$gh_user" = "$remote_url" ] && gh_user=""
108
+ gh_repo=$(echo "$remote_url" | sed 's|.*/\([^/]*\)\.git$|\1|; s|.*/\([^/]*\)$|\1|')
109
+ [ "$gh_repo" = "$remote_url" ] && gh_repo=""
110
+ fi
111
+ git --no-optional-locks -C "$clean_cwd" diff --cached --quiet 2>/dev/null || git_dirty="${GREEN}+${RST}"
112
+ git --no-optional-locks -C "$clean_cwd" diff --quiet 2>/dev/null || git_dirty="${git_dirty}${YELLOW}~${RST}"
113
+ fi
114
+ [ -z "$branch" ] && branch="no-git"
115
+ fi
116
+ if [ -n "$gh_repo" ]; then
117
+ gh_label="${gh_user}/${gh_repo}/${branch}"
118
+ else
119
+ gh_label="$branch"
120
+ fi
121
+
122
+ # ── 5. Cost (green, price only) ──
123
+ cost_raw=$(json_num "total_cost_usd")
124
+ if [ -z "$cost_raw" ] || [ "$cost_raw" = "0" ]; then
125
+ cost_label='$0.00'
126
+ else
127
+ cost_label=$(awk -v c="$cost_raw" 'BEGIN { if (c < 0.01) printf "$%.4f", c; else printf "$%.2f", c }')
128
+ fi
129
+
130
+ # ── 5b. Tokens (input + output = total) ──
131
+ total_in=$(json_num "total_input_tokens")
132
+ total_out=$(json_num "total_output_tokens")
133
+ [ -z "$total_in" ] && total_in="0"
134
+ [ -z "$total_out" ] && total_out="0"
135
+ fmt_tok() {
136
+ awk -v t="$1" 'BEGIN {
137
+ if (t >= 1000000) printf "%.1fM", t/1000000
138
+ else if (t >= 1000) printf "%.0fk", t/1000
139
+ else printf "%d", t
140
+ }'
141
+ }
142
+ tok_in=$(fmt_tok "$total_in")
143
+ tok_out=$(fmt_tok "$total_out")
144
+ tok_total=$(awk -v i="$total_in" -v o="$total_out" 'BEGIN { printf "%d", i + o }')
145
+ tok_total_fmt=$(fmt_tok "$tok_total")
146
+ token_label="${tok_in} + ${tok_out} = ${tok_total_fmt}"
147
+
148
+ # ── 6. Skill (live tool detection from transcript) ──
149
+ skill_label="Idle"
150
+ tpath=""
151
+
152
+ # Find the most recent transcript from ~/.claude/projects/<hash>/*.jsonl
153
+ # Walks up parent directories if exact match not found (handles subfolders)
154
+ if [ -n "$clean_cwd" ]; then
155
+ search_path="$clean_cwd"
156
+ while [ -n "$search_path" ] && [ "$search_path" != "/" ]; do
157
+ proj_hash=$(echo "$search_path" | sed 's|^/\([a-zA-Z]\)/|\U\1--|; s|^[A-Z]:/|&|; s|:/|--|; s|/|-|g')
158
+ proj_dir="$HOME/.claude/projects/${proj_hash}"
159
+ if [ -d "$proj_dir" ]; then
160
+ tpath=$(ls -t "$proj_dir"/*.jsonl 2>/dev/null | head -1)
161
+ [ -n "$tpath" ] && break
162
+ fi
163
+ # Go up one directory
164
+ search_path=$(echo "$search_path" | sed 's|/[^/]*$||')
165
+ done
166
+ fi
167
+
168
+ if [ -n "$tpath" ] && [ -f "$tpath" ]; then
169
+ # Read recent transcript lines
170
+ recent_block=$(tail -200 "$tpath" 2>/dev/null)
171
+
172
+ # Get the LAST tool used — this is what the user sees
173
+ last_tool=$(echo "$recent_block" | grep -o '"type":"tool_use","id":"[^"]*","name":"[^"]*"' | tail -1 | sed 's/.*"name":"\([^"]*\)".*/\1/')
174
+
175
+ if [ -n "$last_tool" ]; then
176
+ case "$last_tool" in
177
+ Task)
178
+ # Last tool is Task (agent) — count how many in recent block
179
+ agent_count=$(echo "$recent_block" | grep -c '"type":"tool_use","id":"[^"]*","name":"Task"')
180
+ if [ "$agent_count" -gt 1 ]; then
181
+ skill_label="${agent_count} Agents"
182
+ else
183
+ agent_desc=$(echo "$recent_block" | grep -o '"description":"[^"]*"' | tail -1 | sed 's/"description":"//;s/"$//')
184
+ if [ -n "$agent_desc" ]; then
185
+ skill_label="Agent($(echo "$agent_desc" | cut -c1-20))"
186
+ else
187
+ skill_label="Agent"
188
+ fi
189
+ fi
190
+ ;;
191
+ Read) skill_label="Read" ;;
192
+ Write) skill_label="Write" ;;
193
+ Edit) skill_label="Edit" ;;
194
+ MultiEdit) skill_label="Multi Edit" ;;
195
+ Glob) skill_label="Search(Files)" ;;
196
+ Grep) skill_label="Search(Content)" ;;
197
+ Bash) skill_label="Terminal" ;;
198
+ WebSearch) skill_label="Web Search" ;;
199
+ WebFetch) skill_label="Web Fetch" ;;
200
+ Skill) skill_label="Skill" ;;
201
+ AskUserQuestion) skill_label="Asking..." ;;
202
+ EnterPlanMode) skill_label="Planning" ;;
203
+ ExitPlanMode) skill_label="Plan Ready" ;;
204
+ TaskCreate) skill_label="Task Create" ;;
205
+ TaskUpdate) skill_label="Task Update" ;;
206
+ TaskGet) skill_label="Task Get" ;;
207
+ TaskList) skill_label="Task List" ;;
208
+ TaskStop) skill_label="Task Stop" ;;
209
+ TaskOutput) skill_label="Task Output" ;;
210
+ NotebookEdit) skill_label="Notebook" ;;
211
+ *) skill_label="$last_tool" ;;
212
+ esac
213
+ fi
214
+ fi
215
+
216
+ # Fallback: check .ccs/task.md for last slash command
217
+ if [ "$skill_label" = "Idle" ] && [ -n "$clean_cwd" ]; then
218
+ task_file="${clean_cwd}/.ccs/task.md"
219
+ if [ -f "$task_file" ]; then
220
+ last_skill=$(grep -oE '/ccs-[a-z]+' "$task_file" 2>/dev/null | tail -1)
221
+ [ -n "$last_skill" ] && skill_label="$last_skill"
222
+ fi
223
+ fi
224
+
225
+ # ── Column widths ──
226
+ C1=38 # Col 1
227
+ C2=30 # Col 2
228
+
229
+ # ── Separator ──
230
+ S=$(printf '%b' " ${SEP}│${RST} ")
231
+
232
+ # ── Assemble: 4 rows ──
233
+ # Row 1: Skill │ GitHub
234
+ printf ' '
235
+ rpad "${PINK}Skill:${RST} ${PINK}${skill_label}${RST}" "$C1"
236
+ printf '%b' "$S"
237
+ printf '%b\n' "${WHITE}GitHub:${RST} ${WHITE}${gh_label}${RST}${git_dirty}"
238
+
239
+ # Row 2: Model │ Dir
240
+ printf ' '
241
+ rpad "${PURPLE}Model:${RST} ${PURPLE}${BOLD}${model_full}${RST}" "$C1"
242
+ printf '%b' "$S"
243
+ printf '%b\n' "${CYAN}Dir:${RST} ${CYAN}${dir_label}${RST}"
244
+
245
+ # Row 3: Tokens │ Cost
246
+ printf ' '
247
+ rpad "${YELLOW}Tokens:${RST} ${YELLOW}${token_label}${RST}" "$C1"
248
+ printf '%b' "$S"
249
+ printf '%b\n' "${GREEN}Cost:${RST} ${GREEN}${cost_label}${RST}"
250
+
251
+ # Row 4: Context (wide bar, full width)
252
+ printf ' '
253
+ printf '%b' "${CTX_CLR}Context:${RST} ${ctx_bar}"
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "skill-statusline",
3
+ "version": "1.0.0",
4
+ "description": "Rich, customizable statusline for Claude Code — model, tokens, cost, context bar, GitHub info, and active skill display.",
5
+ "bin": {
6
+ "skill-statusline": "bin/cli.js",
7
+ "ccsl": "bin/cli.js"
8
+ },
9
+ "files": [
10
+ "bin/",
11
+ "README.md",
12
+ "LICENSE"
13
+ ],
14
+ "keywords": [
15
+ "claude-code",
16
+ "statusline",
17
+ "status-bar",
18
+ "cli",
19
+ "claude",
20
+ "anthropic",
21
+ "ai-coding",
22
+ "developer-tools",
23
+ "terminal",
24
+ "prompt"
25
+ ],
26
+ "author": {
27
+ "name": "Anit Chaudhary",
28
+ "url": "https://github.com/AnitChaudhry"
29
+ },
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/AnitChaudhry/claude-code-statusline.git"
34
+ },
35
+ "homepage": "https://skills.thinqmesh.com",
36
+ "bugs": {
37
+ "url": "https://github.com/AnitChaudhry/claude-code-statusline/issues"
38
+ },
39
+ "engines": {
40
+ "node": ">=16"
41
+ }
42
+ }