@daliovic/cc-statusline 1.3.1 → 1.4.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/README.md +19 -32
- package/dist/config.js +7 -0
- package/dist/statusline.js +72 -0
- package/dist/wizard.js +60 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
A minimal, informative statusline for [Claude Code](https://claude.ai/claude-code) CLI.
|
|
4
4
|
|
|
5
5
|
```
|
|
6
|
-
🤖 Opus
|
|
6
|
+
🤖 Opus │ 📊 53% 106K │ ⏱ 26% 2h09 ▼1h32/46% 2d15 ▼1d04 │ 🕌 Asr 15:32 (2h15) │ 🔌 3
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
## Features
|
|
@@ -13,6 +13,7 @@ A minimal, informative statusline for [Claude Code](https://claude.ai/claude-cod
|
|
|
13
13
|
- **Usage limits** - 5-hour session and 7-day limits with reset times
|
|
14
14
|
- **Budget delta** - Shows if you're under (▼ green) or over (▲ red) your expected usage rate
|
|
15
15
|
- **Prayer times** - Next prayer name, time, and countdown (auto-detects location)
|
|
16
|
+
- **MCP servers** - Shows count of enabled MCP servers for the current session
|
|
16
17
|
- **Customizable** - Interactive wizard to configure colors, visibility, and thresholds
|
|
17
18
|
|
|
18
19
|
## Installation
|
|
@@ -66,9 +67,10 @@ cc-statusline --config
|
|
|
66
67
|
```
|
|
67
68
|
|
|
68
69
|
This lets you customize:
|
|
69
|
-
- **Visibility** - Show/hide model, context, 5hr limit, 7day limit, delta arrows
|
|
70
|
+
- **Visibility** - Show/hide model, context, 5hr limit, 7day limit, delta arrows, prayer times, MCP servers
|
|
70
71
|
- **Colors** - ANSI 256 color codes for each element
|
|
71
72
|
- **Thresholds** - Context warning percentage, cache TTL
|
|
73
|
+
- **MCP display** - Show server count or names
|
|
72
74
|
|
|
73
75
|
Config is saved to `~/.claude/cc-statusline.json`.
|
|
74
76
|
|
|
@@ -76,35 +78,19 @@ Config is saved to `~/.claude/cc-statusline.json`.
|
|
|
76
78
|
|
|
77
79
|
Prayer times are enabled by default and auto-detect your location via IP. Configure via `--config`:
|
|
78
80
|
|
|
79
|
-
- **Calculation method** - Umm Al-Qura
|
|
80
|
-
- **Location** - Auto-detect or manual
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
| 8 | Gulf Region |
|
|
93
|
-
| 9 | Kuwait |
|
|
94
|
-
| 10 | Qatar |
|
|
95
|
-
| 11 | Majlis Ugama Islam Singapura |
|
|
96
|
-
| 12 | UOIF France |
|
|
97
|
-
| 13 | Diyanet Turkey |
|
|
98
|
-
| 14 | Spiritual Administration of Muslims of Russia |
|
|
99
|
-
| 15 | Moonsighting Committee Worldwide |
|
|
100
|
-
| 16 | Dubai (experimental) |
|
|
101
|
-
| 17 | JAKIM, Malaysia |
|
|
102
|
-
| 18 | Tunisia |
|
|
103
|
-
| 19 | Algeria |
|
|
104
|
-
| 20 | KEMENAG, Indonesia |
|
|
105
|
-
| 21 | Morocco |
|
|
106
|
-
| 22 | Comunidade Islamica de Lisboa |
|
|
107
|
-
| 23 | Ministry of Awqaf, Jordan |
|
|
81
|
+
- **Calculation method** - 20+ methods including Umm Al-Qura, ISNA, Muslim World League, and regional options
|
|
82
|
+
- **Location** - Auto-detect via IP, city/country search, or manual coordinates
|
|
83
|
+
|
|
84
|
+
See [Aladhan API docs](https://aladhan.com/prayer-times-api#methods) for all available calculation methods.
|
|
85
|
+
|
|
86
|
+
### MCP Servers
|
|
87
|
+
|
|
88
|
+
Shows the count of enabled MCP servers for your current project. Configure via `--config`:
|
|
89
|
+
|
|
90
|
+
- **Display mode** - Show server count (`🔌 3`) or names (`🔌 reddit, chrome, shell`)
|
|
91
|
+
- **Max names** - Limit names shown before truncating (e.g., `🔌 reddit, chrome+1`)
|
|
92
|
+
|
|
93
|
+
Reads from `~/.claude.json` and respects both global and project-level `disabledMcpServers`.
|
|
108
94
|
|
|
109
95
|
### Environment Variables
|
|
110
96
|
|
|
@@ -122,7 +108,7 @@ Supported calculation methods:
|
|
|
122
108
|
## Output Format
|
|
123
109
|
|
|
124
110
|
```
|
|
125
|
-
🤖 Model │ 📊 Context% Tokens │ ⏱ 5hr% Time Delta/7day% Time Delta │ 🕌 Prayer Time (countdown)
|
|
111
|
+
🤖 Model │ 📊 Context% Tokens │ ⏱ 5hr% Time Delta/7day% Time Delta │ 🕌 Prayer Time (countdown) │ 🔌 MCP
|
|
126
112
|
```
|
|
127
113
|
|
|
128
114
|
**Default Colors** (customizable via `--config`):
|
|
@@ -131,6 +117,7 @@ Supported calculation methods:
|
|
|
131
117
|
- Orange: Context at threshold
|
|
132
118
|
- Green ▼: Under budget
|
|
133
119
|
- Red ▲: Over budget
|
|
120
|
+
- Purple: MCP servers
|
|
134
121
|
|
|
135
122
|
## License
|
|
136
123
|
|
package/dist/config.js
CHANGED
|
@@ -36,6 +36,7 @@ export const DEFAULT_CONFIG = {
|
|
|
36
36
|
usage7day: true,
|
|
37
37
|
delta: true,
|
|
38
38
|
prayer: true,
|
|
39
|
+
mcp: true,
|
|
39
40
|
},
|
|
40
41
|
thresholds: {
|
|
41
42
|
contextWarning: 75,
|
|
@@ -52,11 +53,16 @@ export const DEFAULT_CONFIG = {
|
|
|
52
53
|
deltaUnder: 32, // green
|
|
53
54
|
deltaOver: 31, // red
|
|
54
55
|
prayer: 36, // cyan
|
|
56
|
+
mcp: 141, // purple
|
|
55
57
|
},
|
|
56
58
|
prayer: {
|
|
57
59
|
method: 4, // Umm Al-Qura
|
|
58
60
|
location: null, // auto-detect via IP
|
|
59
61
|
},
|
|
62
|
+
mcp: {
|
|
63
|
+
showNames: false, // Show count by default
|
|
64
|
+
maxNames: 3, // Max server names to show
|
|
65
|
+
},
|
|
60
66
|
};
|
|
61
67
|
export function loadConfig() {
|
|
62
68
|
try {
|
|
@@ -72,6 +78,7 @@ export function loadConfig() {
|
|
|
72
78
|
wizard: { ...DEFAULT_CONFIG.wizard, ...loaded.wizard },
|
|
73
79
|
colors: { ...DEFAULT_CONFIG.colors, ...loaded.colors },
|
|
74
80
|
prayer: { ...DEFAULT_CONFIG.prayer, ...loaded.prayer },
|
|
81
|
+
mcp: { ...DEFAULT_CONFIG.mcp, ...loaded.mcp },
|
|
75
82
|
};
|
|
76
83
|
}
|
|
77
84
|
catch {
|
package/dist/statusline.js
CHANGED
|
@@ -29,6 +29,7 @@ const color = {
|
|
|
29
29
|
deltaUnder: `\x1b[38;5;${userConfig.colors.deltaUnder}m`,
|
|
30
30
|
deltaOver: `\x1b[38;5;${userConfig.colors.deltaOver}m`,
|
|
31
31
|
prayer: `\x1b[38;5;${userConfig.colors.prayer}m`,
|
|
32
|
+
mcp: `\x1b[38;5;${userConfig.colors.mcp}m`,
|
|
32
33
|
};
|
|
33
34
|
// === Helpers ===
|
|
34
35
|
function formatTokens(tokens) {
|
|
@@ -141,6 +142,58 @@ function getContextFromTranscript(transcriptPath, modelId) {
|
|
|
141
142
|
return undefined;
|
|
142
143
|
}
|
|
143
144
|
}
|
|
145
|
+
function getMcpServers(projectPath) {
|
|
146
|
+
try {
|
|
147
|
+
const claudeJsonPath = join(homedir(), ".claude.json");
|
|
148
|
+
if (!existsSync(claudeJsonPath))
|
|
149
|
+
return null;
|
|
150
|
+
const content = readFileSync(claudeJsonPath, "utf-8");
|
|
151
|
+
const claudeJson = JSON.parse(content);
|
|
152
|
+
// Collect all MCP servers from global and project-level configs
|
|
153
|
+
const allServers = new Set();
|
|
154
|
+
const disabledServers = new Set();
|
|
155
|
+
// 1. Global MCP servers (root level)
|
|
156
|
+
if (claudeJson.mcpServers) {
|
|
157
|
+
Object.keys(claudeJson.mcpServers).forEach(s => allServers.add(s));
|
|
158
|
+
}
|
|
159
|
+
if (claudeJson.disabledMcpServers) {
|
|
160
|
+
claudeJson.disabledMcpServers.forEach(s => disabledServers.add(s));
|
|
161
|
+
}
|
|
162
|
+
// 2. Project-level MCP servers
|
|
163
|
+
if (claudeJson.projects) {
|
|
164
|
+
const pathsToTry = projectPath ? [projectPath] : [];
|
|
165
|
+
const cwd = process.cwd();
|
|
166
|
+
if (cwd && !pathsToTry.includes(cwd))
|
|
167
|
+
pathsToTry.push(cwd);
|
|
168
|
+
pathsToTry.push(homedir());
|
|
169
|
+
for (const path of pathsToTry) {
|
|
170
|
+
const projectConfig = claudeJson.projects[path];
|
|
171
|
+
if (projectConfig) {
|
|
172
|
+
// Add project-specific servers
|
|
173
|
+
if (projectConfig.mcpServers) {
|
|
174
|
+
Object.keys(projectConfig.mcpServers).forEach(s => allServers.add(s));
|
|
175
|
+
}
|
|
176
|
+
// Project-level disabled servers take precedence
|
|
177
|
+
if (projectConfig.disabledMcpServers) {
|
|
178
|
+
projectConfig.disabledMcpServers.forEach(s => disabledServers.add(s));
|
|
179
|
+
}
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (allServers.size === 0)
|
|
185
|
+
return null;
|
|
186
|
+
const enabledServers = [...allServers].filter(s => !disabledServers.has(s));
|
|
187
|
+
return {
|
|
188
|
+
total: allServers.size,
|
|
189
|
+
enabled: enabledServers.length,
|
|
190
|
+
names: enabledServers,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
144
197
|
// === OAuth Usage API ===
|
|
145
198
|
async function fetchUsageFromApi() {
|
|
146
199
|
try {
|
|
@@ -249,6 +302,25 @@ async function main() {
|
|
|
249
302
|
segments.push(`${color.prayer}\u{1F54C} ${prayer.name} ${prayer.time}${color.dim} (${prayer.timeLeft})${color.reset}`);
|
|
250
303
|
}
|
|
251
304
|
}
|
|
305
|
+
// MCP Servers
|
|
306
|
+
if (userConfig.show.mcp) {
|
|
307
|
+
const projectPath = stdin.workspace?.project_dir || stdin.cwd;
|
|
308
|
+
const mcpInfo = getMcpServers(projectPath);
|
|
309
|
+
if (mcpInfo && mcpInfo.enabled > 0) {
|
|
310
|
+
let mcpDisplay;
|
|
311
|
+
if (userConfig.mcp.showNames && mcpInfo.names.length > 0) {
|
|
312
|
+
const names = mcpInfo.names.slice(0, userConfig.mcp.maxNames);
|
|
313
|
+
const suffix = mcpInfo.names.length > userConfig.mcp.maxNames
|
|
314
|
+
? `${color.dim}+${mcpInfo.names.length - userConfig.mcp.maxNames}${color.reset}`
|
|
315
|
+
: "";
|
|
316
|
+
mcpDisplay = `${color.mcp}\u{1F50C} ${names.join(", ")}${suffix}${color.reset}`;
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
mcpDisplay = `${color.mcp}\u{1F50C} ${mcpInfo.enabled}${color.reset}`;
|
|
320
|
+
}
|
|
321
|
+
segments.push(mcpDisplay);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
252
324
|
// Output
|
|
253
325
|
console.log(segments.join(` ${color.dim}\u{2502}${color.reset} `));
|
|
254
326
|
}
|
package/dist/wizard.js
CHANGED
|
@@ -41,6 +41,14 @@ function showPreview(config) {
|
|
|
41
41
|
if (config.show.prayer) {
|
|
42
42
|
parts.push(colorize(c.prayer, "\u{1F54C} Asr 15:32") + " \x1b[2m(2h15)\x1b[0m");
|
|
43
43
|
}
|
|
44
|
+
if (config.show.mcp) {
|
|
45
|
+
if (config.mcp.showNames) {
|
|
46
|
+
parts.push(colorize(c.mcp, "\u{1F50C} reddit, chrome"));
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
parts.push(colorize(c.mcp, "\u{1F50C} 3"));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
44
52
|
console.log("\n Preview: " + parts.join(" \x1b[2m\u{2502}\x1b[0m ") + "\n");
|
|
45
53
|
}
|
|
46
54
|
function autoSave(config) {
|
|
@@ -58,6 +66,7 @@ async function visibilityMenu(config) {
|
|
|
58
66
|
{ name: "7-day usage limit", value: "usage7day", checked: config.show.usage7day },
|
|
59
67
|
{ name: "Budget delta arrows", value: "delta", checked: config.show.delta },
|
|
60
68
|
{ name: "Prayer times", value: "prayer", checked: config.show.prayer },
|
|
69
|
+
{ name: "MCP servers", value: "mcp", checked: config.show.mcp },
|
|
61
70
|
];
|
|
62
71
|
const selected = await checkbox({
|
|
63
72
|
message: "Toggle items (space=toggle, enter=confirm, ctrl+c=back)",
|
|
@@ -69,6 +78,7 @@ async function visibilityMenu(config) {
|
|
|
69
78
|
config.show.usage7day = selected.includes("usage7day");
|
|
70
79
|
config.show.delta = selected.includes("delta");
|
|
71
80
|
config.show.prayer = selected.includes("prayer");
|
|
81
|
+
config.show.mcp = selected.includes("mcp");
|
|
72
82
|
autoSave(config);
|
|
73
83
|
return false;
|
|
74
84
|
}
|
|
@@ -88,6 +98,7 @@ async function colorsMenu(config) {
|
|
|
88
98
|
{ name: `Delta under budget (current: ${colorize(config.colors.deltaUnder, String(config.colors.deltaUnder))})`, value: "deltaUnder" },
|
|
89
99
|
{ name: `Delta over budget (current: ${colorize(config.colors.deltaOver, String(config.colors.deltaOver))})`, value: "deltaOver" },
|
|
90
100
|
{ name: `Prayer (current: ${colorize(config.colors.prayer, String(config.colors.prayer))})`, value: "prayer" },
|
|
101
|
+
{ name: `MCP servers (current: ${colorize(config.colors.mcp, String(config.colors.mcp))})`, value: "mcp" },
|
|
91
102
|
{ name: "\x1b[2m← Back\x1b[0m", value: "back" },
|
|
92
103
|
];
|
|
93
104
|
while (true) {
|
|
@@ -279,6 +290,50 @@ async function settingsMenu(config) {
|
|
|
279
290
|
throw e;
|
|
280
291
|
}
|
|
281
292
|
}
|
|
293
|
+
async function mcpMenu(config) {
|
|
294
|
+
try {
|
|
295
|
+
while (true) {
|
|
296
|
+
const displayMode = config.mcp.showNames ? "Server names" : "Server count";
|
|
297
|
+
const choice = await select({
|
|
298
|
+
message: "MCP servers display settings",
|
|
299
|
+
choices: [
|
|
300
|
+
{ name: `Display mode: ${displayMode}`, value: "mode" },
|
|
301
|
+
{ name: `Max names to show: ${config.mcp.maxNames}`, value: "maxNames" },
|
|
302
|
+
{ name: "\x1b[2m← Back\x1b[0m", value: "back" },
|
|
303
|
+
],
|
|
304
|
+
});
|
|
305
|
+
if (choice === "back")
|
|
306
|
+
return false;
|
|
307
|
+
if (choice === "mode") {
|
|
308
|
+
const showNames = await confirm({
|
|
309
|
+
message: "Show server names instead of count?",
|
|
310
|
+
default: config.mcp.showNames,
|
|
311
|
+
});
|
|
312
|
+
config.mcp.showNames = showNames;
|
|
313
|
+
autoSave(config);
|
|
314
|
+
}
|
|
315
|
+
else if (choice === "maxNames") {
|
|
316
|
+
const maxNames = await input({
|
|
317
|
+
message: "Max server names to show (extras shown as +N)",
|
|
318
|
+
default: String(config.mcp.maxNames),
|
|
319
|
+
validate: (val) => {
|
|
320
|
+
const num = parseInt(val, 10);
|
|
321
|
+
if (isNaN(num) || num < 1 || num > 10)
|
|
322
|
+
return "Enter 1-10";
|
|
323
|
+
return true;
|
|
324
|
+
},
|
|
325
|
+
});
|
|
326
|
+
config.mcp.maxNames = parseInt(maxNames, 10);
|
|
327
|
+
autoSave(config);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
catch (e) {
|
|
332
|
+
if (isCancelled(e))
|
|
333
|
+
return true;
|
|
334
|
+
throw e;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
282
337
|
function showHeader(config) {
|
|
283
338
|
clearScreen();
|
|
284
339
|
console.log("\x1b[1mcc-statusline Configuration\x1b[0m");
|
|
@@ -298,6 +353,7 @@ export async function runWizard() {
|
|
|
298
353
|
{ name: "Colors", value: "colors" },
|
|
299
354
|
{ name: "Thresholds", value: "thresholds" },
|
|
300
355
|
{ name: "Prayer settings", value: "prayer" },
|
|
356
|
+
{ name: "MCP servers display", value: "mcp" },
|
|
301
357
|
{ name: `Settings ${autosaveLabel} autosave`, value: "settings" },
|
|
302
358
|
{ name: "Reset to defaults", value: "reset" },
|
|
303
359
|
...(config.wizard.autosave ? [] : [{ name: "Save & Exit", value: "save" }]),
|
|
@@ -321,6 +377,10 @@ export async function runWizard() {
|
|
|
321
377
|
await prayerMenu(config);
|
|
322
378
|
showHeader(config);
|
|
323
379
|
break;
|
|
380
|
+
case "mcp":
|
|
381
|
+
await mcpMenu(config);
|
|
382
|
+
showHeader(config);
|
|
383
|
+
break;
|
|
324
384
|
case "settings":
|
|
325
385
|
await settingsMenu(config);
|
|
326
386
|
showHeader(config);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@daliovic/cc-statusline",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Minimal Claude Code statusline with usage limits and budget tracking",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/statusline.js",
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"claude-code",
|
|
25
25
|
"statusline",
|
|
26
26
|
"cli",
|
|
27
|
-
"anthropic"
|
|
27
|
+
"anthropic",
|
|
28
|
+
"mcp"
|
|
28
29
|
],
|
|
29
30
|
"license": "MIT",
|
|
30
31
|
"engines": {
|