@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 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 4.5 │ 📊 53% 106K │ ⏱ 26% 2h09 ▼1h32/46% 2d15 ▼1d04 │ 🕌 Asr 15:32 (2h15)
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 (default), ISNA, Muslim World League, and more
80
- - **Location** - Auto-detect or manual lat/lng
81
-
82
- Supported calculation methods:
83
- | Code | Method |
84
- |------|--------|
85
- | 0 | Shia Ithna-Ansari |
86
- | 1 | University of Islamic Sciences, Karachi |
87
- | 2 | Islamic Society of North America (ISNA) |
88
- | 3 | Muslim World League |
89
- | 4 | Umm Al-Qura, Makkah (default) |
90
- | 5 | Egyptian General Authority |
91
- | 7 | Institute of Geophysics, Tehran |
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 {
@@ -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.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": {