claude-code-templates 1.4.3 ā 1.5.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/create-claude-config.js +4 -1
- package/package.json +10 -3
- package/src/analytics.js +739 -0
- package/src/hook-stats.js +258 -0
- package/src/index.js +23 -2
- package/src/mcp-stats.js +321 -0
- package/README.md +0 -436
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const inquirer = require('inquirer');
|
|
5
|
+
const { spawn } = require('child_process');
|
|
6
|
+
|
|
7
|
+
function estimateTokens(text) {
|
|
8
|
+
if (!text || typeof text !== 'string') return 0;
|
|
9
|
+
|
|
10
|
+
// Clean the text and estimate tokens
|
|
11
|
+
const cleanText = text.replace(/\s+/g, ' ').trim();
|
|
12
|
+
const baseTokens = Math.ceil(cleanText.length / 4);
|
|
13
|
+
|
|
14
|
+
// Add extra tokens for code blocks and complex structures
|
|
15
|
+
const codeBlocks = (text.match(/```[\s\S]*?```/g) || []).length;
|
|
16
|
+
const inlineCode = (text.match(/`[^`]+`/g) || []).length;
|
|
17
|
+
const jsonBlocks = (text.match(/\{[\s\S]*?\}/g) || []).length;
|
|
18
|
+
|
|
19
|
+
const adjustedTokens = baseTokens + (codeBlocks * 5) + (inlineCode * 2) + (jsonBlocks * 3);
|
|
20
|
+
return Math.max(adjustedTokens, 1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function analyzeHooks(targetDir = process.cwd()) {
|
|
24
|
+
const settingsPath = path.join(targetDir, '.claude', 'settings.json');
|
|
25
|
+
|
|
26
|
+
console.log(chalk.blue('š Analyzing automation hooks...'));
|
|
27
|
+
console.log(chalk.gray(`Scanning: ${settingsPath}`));
|
|
28
|
+
|
|
29
|
+
if (!await fs.pathExists(settingsPath)) {
|
|
30
|
+
console.log(chalk.yellow('ā ļø No .claude/settings.json file found.'));
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const settingsContent = await fs.readFile(settingsPath, 'utf8');
|
|
36
|
+
const settings = JSON.parse(settingsContent);
|
|
37
|
+
|
|
38
|
+
if (!settings.hooks || Object.keys(settings.hooks).length === 0) {
|
|
39
|
+
console.log(chalk.yellow('ā ļø No automation hooks found in settings.json.'));
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const hookAnalysis = {
|
|
44
|
+
totalHooks: 0,
|
|
45
|
+
byType: {},
|
|
46
|
+
hooks: []
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Analyze each hook type
|
|
50
|
+
for (const [hookType, hooks] of Object.entries(settings.hooks)) {
|
|
51
|
+
if (!Array.isArray(hooks)) continue;
|
|
52
|
+
|
|
53
|
+
hookAnalysis.byType[hookType] = {
|
|
54
|
+
count: hooks.length,
|
|
55
|
+
hooks: []
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
for (const hook of hooks) {
|
|
59
|
+
const hookData = {
|
|
60
|
+
type: hookType,
|
|
61
|
+
name: hook.name || 'Unnamed Hook',
|
|
62
|
+
description: hook.description || '',
|
|
63
|
+
scriptContent: hook.script || hook.command || '',
|
|
64
|
+
enabled: hook.enabled !== false, // Default to true if not specified
|
|
65
|
+
conditions: hook.conditions || [],
|
|
66
|
+
size: JSON.stringify(hook).length,
|
|
67
|
+
lines: (hook.script || hook.command || '').split('\n').length,
|
|
68
|
+
words: (hook.script || hook.command || '').split(/\s+/).filter(word => word.length > 0).length,
|
|
69
|
+
tokens: estimateTokens(JSON.stringify(hook))
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
hookAnalysis.byType[hookType].hooks.push(hookData);
|
|
73
|
+
hookAnalysis.hooks.push(hookData);
|
|
74
|
+
hookAnalysis.totalHooks++;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Get file stats
|
|
79
|
+
const stats = await fs.stat(settingsPath);
|
|
80
|
+
hookAnalysis.fileSize = stats.size;
|
|
81
|
+
hookAnalysis.lastModified = stats.mtime;
|
|
82
|
+
|
|
83
|
+
return hookAnalysis;
|
|
84
|
+
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error(chalk.red('ā Error reading settings.json:'), error.message);
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function displayHookStats(analysis) {
|
|
92
|
+
if (!analysis || analysis.totalHooks === 0) {
|
|
93
|
+
console.log(chalk.yellow('\nš Hook Analysis Results'));
|
|
94
|
+
console.log(chalk.gray('No automation hooks found to analyze.'));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
console.log(chalk.blue('\nš Hook Analysis Results'));
|
|
99
|
+
console.log(chalk.gray(`File: .claude/settings.json (${analysis.fileSize} bytes)`));
|
|
100
|
+
console.log(chalk.gray(`Last Modified: ${analysis.lastModified.toLocaleDateString()}`));
|
|
101
|
+
console.log(chalk.gray(`Total Hooks: ${analysis.totalHooks}`));
|
|
102
|
+
|
|
103
|
+
// Calculate column widths
|
|
104
|
+
const maxNameLength = Math.max(
|
|
105
|
+
...analysis.hooks.map(hook => hook.name.length),
|
|
106
|
+
'Hook Name'.length
|
|
107
|
+
);
|
|
108
|
+
const nameWidth = Math.min(maxNameLength, 30);
|
|
109
|
+
|
|
110
|
+
// Header
|
|
111
|
+
const header = chalk.bold.blue(
|
|
112
|
+
'Hook Name'.padEnd(nameWidth) + ' ā ' +
|
|
113
|
+
'Type'.padEnd(12) + ' ā ' +
|
|
114
|
+
'Status'.padEnd(8) + ' ā ' +
|
|
115
|
+
'Description'
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const separator = 'ā'.repeat(nameWidth) + 'āā¼ā' +
|
|
119
|
+
'ā'.repeat(12) + 'āā¼ā' +
|
|
120
|
+
'ā'.repeat(8) + 'āā¼ā' +
|
|
121
|
+
'ā'.repeat(40);
|
|
122
|
+
|
|
123
|
+
console.log('\n' + header);
|
|
124
|
+
console.log(chalk.gray(separator));
|
|
125
|
+
|
|
126
|
+
// Hook rows
|
|
127
|
+
analysis.hooks.forEach(hook => {
|
|
128
|
+
const truncatedName = hook.name.length > nameWidth ?
|
|
129
|
+
hook.name.substring(0, nameWidth - 3) + '...' :
|
|
130
|
+
hook.name;
|
|
131
|
+
|
|
132
|
+
const statusColor = hook.enabled ? chalk.green : chalk.yellow;
|
|
133
|
+
const status = hook.enabled ? 'Enabled' : 'Disabled';
|
|
134
|
+
|
|
135
|
+
const truncatedDesc = hook.description.length > 40 ?
|
|
136
|
+
hook.description.substring(0, 37) + '...' :
|
|
137
|
+
hook.description;
|
|
138
|
+
|
|
139
|
+
const row =
|
|
140
|
+
truncatedName.padEnd(nameWidth) + ' ā ' +
|
|
141
|
+
hook.type.padEnd(12) + ' ā ' +
|
|
142
|
+
statusColor(status.padEnd(8)) + ' ā ' +
|
|
143
|
+
chalk.gray(truncatedDesc);
|
|
144
|
+
|
|
145
|
+
console.log(row);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Summary by type
|
|
149
|
+
console.log(chalk.blue('\nš Hook Summary by Type:'));
|
|
150
|
+
for (const [type, data] of Object.entries(analysis.byType)) {
|
|
151
|
+
const enabledCount = data.hooks.filter(h => h.enabled).length;
|
|
152
|
+
const disabledCount = data.hooks.filter(h => !h.enabled).length;
|
|
153
|
+
|
|
154
|
+
console.log(chalk.gray(` ${type}: ${data.count} hooks`) +
|
|
155
|
+
chalk.green(` (${enabledCount} enabled`) +
|
|
156
|
+
(disabledCount > 0 ? chalk.yellow(`, ${disabledCount} disabled)`) : chalk.green(')')));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
console.log(chalk.blue('\nš§ Hook Types Found:'));
|
|
160
|
+
Object.keys(analysis.byType).forEach(type => {
|
|
161
|
+
console.log(chalk.gray(` ⢠${type}`));
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function runHookStats(options) {
|
|
166
|
+
const targetDir = options.directory || process.cwd();
|
|
167
|
+
|
|
168
|
+
console.log(chalk.blue('š§ Claude Code Hook Analysis'));
|
|
169
|
+
console.log(chalk.gray(`Target directory: ${targetDir}`));
|
|
170
|
+
|
|
171
|
+
const analysis = await analyzeHooks(targetDir);
|
|
172
|
+
|
|
173
|
+
if (!analysis) {
|
|
174
|
+
console.log(chalk.yellow('\nš” No automation hooks found.'));
|
|
175
|
+
console.log(chalk.gray('Would you like to set up Claude Code Templates to add automation hooks?'));
|
|
176
|
+
|
|
177
|
+
const { setupHooks } = await inquirer.prompt([{
|
|
178
|
+
type: 'confirm',
|
|
179
|
+
name: 'setupHooks',
|
|
180
|
+
message: 'Set up automation hooks with Claude Code Templates?',
|
|
181
|
+
default: true
|
|
182
|
+
}]);
|
|
183
|
+
|
|
184
|
+
if (setupHooks) {
|
|
185
|
+
console.log(chalk.blue('\nš Starting Claude Code Templates setup...'));
|
|
186
|
+
|
|
187
|
+
// Import and run the main setup
|
|
188
|
+
const createClaudeConfig = require('./index');
|
|
189
|
+
await createClaudeConfig({ ...options, directory: targetDir });
|
|
190
|
+
}
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
displayHookStats(analysis);
|
|
195
|
+
|
|
196
|
+
// Ask if user wants Claude Code to review and optimize hooks
|
|
197
|
+
console.log(chalk.blue('\nš¤ Optimization Opportunity'));
|
|
198
|
+
console.log(chalk.gray('Claude Code can analyze your automation hooks and suggest optimizations.'));
|
|
199
|
+
|
|
200
|
+
const { optimizeHooks } = await inquirer.prompt([{
|
|
201
|
+
type: 'confirm',
|
|
202
|
+
name: 'optimizeHooks',
|
|
203
|
+
message: 'Would you like Claude Code to review and optimize your automation hooks?',
|
|
204
|
+
default: true
|
|
205
|
+
}]);
|
|
206
|
+
|
|
207
|
+
if (optimizeHooks) {
|
|
208
|
+
console.log(chalk.blue('\nš Launching Claude Code for hook optimization...'));
|
|
209
|
+
|
|
210
|
+
// Prepare the optimization prompt
|
|
211
|
+
const hookSummary = `I have ${analysis.totalHooks} automation hooks configured in my .claude/settings.json file:
|
|
212
|
+
|
|
213
|
+
${Object.entries(analysis.byType).map(([type, data]) =>
|
|
214
|
+
`${type}: ${data.count} hooks (${data.hooks.filter(h => h.enabled).length} enabled, ${data.hooks.filter(h => !h.enabled).length} disabled)`
|
|
215
|
+
).join('\n')}
|
|
216
|
+
|
|
217
|
+
Hook details:
|
|
218
|
+
${analysis.hooks.map(hook =>
|
|
219
|
+
`- ${hook.name} (${hook.type}): ${hook.enabled ? 'Enabled' : 'Disabled'} - ${hook.tokens} tokens - ${hook.description || 'No description'}`
|
|
220
|
+
).join('\n')}
|
|
221
|
+
|
|
222
|
+
Please review my automation hook configuration and suggest optimizations for:
|
|
223
|
+
1. Hook efficiency and performance
|
|
224
|
+
2. Missing hooks that could improve my workflow
|
|
225
|
+
3. Hooks that might be redundant or conflicting
|
|
226
|
+
4. Best practices for hook organization
|
|
227
|
+
5. Security considerations for the hooks
|
|
228
|
+
6. Hook conditions and triggers optimization
|
|
229
|
+
|
|
230
|
+
Consider my project structure and suggest hooks that would be most beneficial for my development workflow.`;
|
|
231
|
+
|
|
232
|
+
const claudeCommand = `claude "${hookSummary}"`;
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
const child = spawn('sh', ['-c', claudeCommand], {
|
|
236
|
+
stdio: 'inherit',
|
|
237
|
+
cwd: targetDir
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
child.on('error', (error) => {
|
|
241
|
+
console.error(chalk.red('ā Error launching Claude Code:'), error.message);
|
|
242
|
+
console.log(chalk.yellow('š” Make sure Claude Code is installed: npm install -g @anthropic-ai/claude-code'));
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
} catch (error) {
|
|
246
|
+
console.error(chalk.red('ā Error launching Claude Code:'), error.message);
|
|
247
|
+
console.log(chalk.yellow('š” Make sure Claude Code is installed and accessible.'));
|
|
248
|
+
}
|
|
249
|
+
} else {
|
|
250
|
+
console.log(chalk.gray('\nā
Hook analysis complete. You can run this command again anytime to re-analyze your hooks.'));
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
module.exports = {
|
|
255
|
+
runHookStats,
|
|
256
|
+
analyzeHooks,
|
|
257
|
+
displayHookStats
|
|
258
|
+
};
|
package/src/index.js
CHANGED
|
@@ -9,16 +9,37 @@ const { createPrompts, interactivePrompts } = require('./prompts');
|
|
|
9
9
|
const { copyTemplateFiles, runPostInstallationValidation } = require('./file-operations');
|
|
10
10
|
const { getHooksForLanguage, getMCPsForLanguage } = require('./hook-scanner');
|
|
11
11
|
const { runCommandStats } = require('./command-stats');
|
|
12
|
+
const { runHookStats } = require('./hook-stats');
|
|
13
|
+
const { runMCPStats } = require('./mcp-stats');
|
|
14
|
+
const { runAnalytics } = require('./analytics');
|
|
12
15
|
|
|
13
16
|
async function createClaudeConfig(options = {}) {
|
|
14
17
|
const targetDir = options.directory || process.cwd();
|
|
15
18
|
|
|
16
|
-
// Handle command stats analysis
|
|
17
|
-
if (options.commandStats) {
|
|
19
|
+
// Handle command stats analysis (both singular and plural)
|
|
20
|
+
if (options.commandStats || options.commandsStats) {
|
|
18
21
|
await runCommandStats(options);
|
|
19
22
|
return;
|
|
20
23
|
}
|
|
21
24
|
|
|
25
|
+
// Handle hook stats analysis (both singular and plural)
|
|
26
|
+
if (options.hookStats || options.hooksStats) {
|
|
27
|
+
await runHookStats(options);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Handle MCP stats analysis (both singular and plural)
|
|
32
|
+
if (options.mcpStats || options.mcpsStats) {
|
|
33
|
+
await runMCPStats(options);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Handle analytics dashboard
|
|
38
|
+
if (options.analytics) {
|
|
39
|
+
await runAnalytics(options);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
22
43
|
console.log(chalk.blue('š Setting up Claude Code configuration...'));
|
|
23
44
|
console.log(chalk.gray(`Target directory: ${targetDir}`));
|
|
24
45
|
|
package/src/mcp-stats.js
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const inquirer = require('inquirer');
|
|
5
|
+
const { spawn } = require('child_process');
|
|
6
|
+
|
|
7
|
+
async function analyzeMCPServers(targetDir = process.cwd()) {
|
|
8
|
+
const mcpConfigPath = path.join(targetDir, '.mcp.json');
|
|
9
|
+
|
|
10
|
+
console.log(chalk.blue('š Analyzing MCP server configurations...'));
|
|
11
|
+
console.log(chalk.gray(`Scanning: ${mcpConfigPath}`));
|
|
12
|
+
|
|
13
|
+
if (!await fs.pathExists(mcpConfigPath)) {
|
|
14
|
+
console.log(chalk.yellow('ā ļø No .mcp.json file found.'));
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const mcpContent = await fs.readFile(mcpConfigPath, 'utf8');
|
|
20
|
+
const mcpConfig = JSON.parse(mcpContent);
|
|
21
|
+
|
|
22
|
+
if (!mcpConfig.mcpServers || Object.keys(mcpConfig.mcpServers).length === 0) {
|
|
23
|
+
console.log(chalk.yellow('ā ļø No MCP servers found in .mcp.json.'));
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const mcpAnalysis = {
|
|
28
|
+
totalServers: 0,
|
|
29
|
+
servers: [],
|
|
30
|
+
byCategory: {}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Analyze each MCP server
|
|
34
|
+
for (const [serverKey, serverConfig] of Object.entries(mcpConfig.mcpServers)) {
|
|
35
|
+
const serverData = {
|
|
36
|
+
key: serverKey,
|
|
37
|
+
name: serverConfig.name || serverKey,
|
|
38
|
+
description: serverConfig.description || 'No description provided',
|
|
39
|
+
command: serverConfig.command || 'Unknown',
|
|
40
|
+
args: serverConfig.args || [],
|
|
41
|
+
env: serverConfig.env || {},
|
|
42
|
+
enabled: serverConfig.disabled !== true, // Default to enabled unless explicitly disabled
|
|
43
|
+
category: categorizeServer(serverConfig),
|
|
44
|
+
configSize: JSON.stringify(serverConfig).length,
|
|
45
|
+
complexity: calculateComplexity(serverConfig)
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
mcpAnalysis.servers.push(serverData);
|
|
49
|
+
mcpAnalysis.totalServers++;
|
|
50
|
+
|
|
51
|
+
// Group by category
|
|
52
|
+
if (!mcpAnalysis.byCategory[serverData.category]) {
|
|
53
|
+
mcpAnalysis.byCategory[serverData.category] = [];
|
|
54
|
+
}
|
|
55
|
+
mcpAnalysis.byCategory[serverData.category].push(serverData);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Get file stats
|
|
59
|
+
const stats = await fs.stat(mcpConfigPath);
|
|
60
|
+
mcpAnalysis.fileSize = stats.size;
|
|
61
|
+
mcpAnalysis.lastModified = stats.mtime;
|
|
62
|
+
|
|
63
|
+
return mcpAnalysis;
|
|
64
|
+
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error(chalk.red('ā Error reading .mcp.json:'), error.message);
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function categorizeServer(serverConfig) {
|
|
72
|
+
const name = (serverConfig.name || '').toLowerCase();
|
|
73
|
+
const command = (serverConfig.command || '').toLowerCase();
|
|
74
|
+
const description = (serverConfig.description || '').toLowerCase();
|
|
75
|
+
|
|
76
|
+
// IDE and Development Tools
|
|
77
|
+
if (name.includes('ide') || name.includes('vscode') || name.includes('jupyter') ||
|
|
78
|
+
command.includes('jupyter') || description.includes('ide')) {
|
|
79
|
+
return 'IDE & Development';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Database Tools
|
|
83
|
+
if (name.includes('postgres') || name.includes('mysql') || name.includes('sqlite') ||
|
|
84
|
+
name.includes('database') || description.includes('database')) {
|
|
85
|
+
return 'Database';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Web and API Tools
|
|
89
|
+
if (name.includes('web') || name.includes('search') || name.includes('api') ||
|
|
90
|
+
name.includes('http') || description.includes('web') || description.includes('search')) {
|
|
91
|
+
return 'Web & API';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// File System Tools
|
|
95
|
+
if (name.includes('file') || name.includes('filesystem') ||
|
|
96
|
+
description.includes('file') || description.includes('filesystem')) {
|
|
97
|
+
return 'Filesystem';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Development Tools
|
|
101
|
+
if (name.includes('git') || name.includes('docker') || name.includes('github') ||
|
|
102
|
+
description.includes('git') || description.includes('docker')) {
|
|
103
|
+
return 'DevOps';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// AI and ML Tools
|
|
107
|
+
if (name.includes('ai') || name.includes('ml') || name.includes('model') ||
|
|
108
|
+
description.includes('ai') || description.includes('machine learning')) {
|
|
109
|
+
return 'AI & ML';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return 'Other';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function calculateComplexity(serverConfig) {
|
|
116
|
+
let complexity = 1; // Base complexity
|
|
117
|
+
|
|
118
|
+
// Add complexity for configuration options
|
|
119
|
+
if (serverConfig.args && serverConfig.args.length > 0) complexity += 1;
|
|
120
|
+
if (serverConfig.env && Object.keys(serverConfig.env).length > 0) complexity += 1;
|
|
121
|
+
if (serverConfig.settings && Object.keys(serverConfig.settings).length > 0) complexity += 1;
|
|
122
|
+
|
|
123
|
+
// Add complexity for environment variables
|
|
124
|
+
const envCount = Object.keys(serverConfig.env || {}).length;
|
|
125
|
+
if (envCount > 3) complexity += 1;
|
|
126
|
+
if (envCount > 6) complexity += 1;
|
|
127
|
+
|
|
128
|
+
return Math.min(complexity, 5); // Cap at 5
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function displayMCPStats(analysis) {
|
|
132
|
+
if (!analysis || analysis.totalServers === 0) {
|
|
133
|
+
console.log(chalk.yellow('\nš MCP Server Analysis Results'));
|
|
134
|
+
console.log(chalk.gray('No MCP servers found to analyze.'));
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
console.log(chalk.blue('\nš MCP Server Analysis Results'));
|
|
139
|
+
console.log(chalk.gray(`File: .mcp.json (${analysis.fileSize} bytes)`));
|
|
140
|
+
console.log(chalk.gray(`Last Modified: ${analysis.lastModified.toLocaleDateString()}`));
|
|
141
|
+
console.log(chalk.gray(`Total MCP Servers: ${analysis.totalServers}`));
|
|
142
|
+
|
|
143
|
+
// Calculate column widths
|
|
144
|
+
const maxNameLength = Math.max(
|
|
145
|
+
...analysis.servers.map(server => server.name.length),
|
|
146
|
+
'Server Name'.length
|
|
147
|
+
);
|
|
148
|
+
const nameWidth = Math.min(maxNameLength, 25);
|
|
149
|
+
|
|
150
|
+
// Header
|
|
151
|
+
const header = chalk.bold.blue(
|
|
152
|
+
'Server Name'.padEnd(nameWidth) + ' ā ' +
|
|
153
|
+
'Category'.padEnd(15) + ' ā ' +
|
|
154
|
+
'Status'.padEnd(8) + ' ā ' +
|
|
155
|
+
'Command'.padEnd(12) + ' ā ' +
|
|
156
|
+
'Complexity'.padEnd(10) + ' ā ' +
|
|
157
|
+
'Description'
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
const separator = 'ā'.repeat(nameWidth) + 'āā¼ā' +
|
|
161
|
+
'ā'.repeat(15) + 'āā¼ā' +
|
|
162
|
+
'ā'.repeat(8) + 'āā¼ā' +
|
|
163
|
+
'ā'.repeat(12) + 'āā¼ā' +
|
|
164
|
+
'ā'.repeat(10) + 'āā¼ā' +
|
|
165
|
+
'ā'.repeat(30);
|
|
166
|
+
|
|
167
|
+
console.log('\n' + header);
|
|
168
|
+
console.log(chalk.gray(separator));
|
|
169
|
+
|
|
170
|
+
// Server rows
|
|
171
|
+
analysis.servers.forEach(server => {
|
|
172
|
+
const truncatedName = server.name.length > nameWidth ?
|
|
173
|
+
server.name.substring(0, nameWidth - 3) + '...' :
|
|
174
|
+
server.name;
|
|
175
|
+
|
|
176
|
+
const statusColor = server.enabled ? chalk.green : chalk.yellow;
|
|
177
|
+
const status = server.enabled ? 'Enabled' : 'Disabled';
|
|
178
|
+
|
|
179
|
+
const truncatedCommand = server.command.length > 12 ?
|
|
180
|
+
server.command.substring(0, 9) + '...' :
|
|
181
|
+
server.command;
|
|
182
|
+
|
|
183
|
+
const complexityStars = 'ā
'.repeat(server.complexity) + 'ā'.repeat(5 - server.complexity);
|
|
184
|
+
|
|
185
|
+
const truncatedDesc = server.description.length > 30 ?
|
|
186
|
+
server.description.substring(0, 27) + '...' :
|
|
187
|
+
server.description;
|
|
188
|
+
|
|
189
|
+
const row =
|
|
190
|
+
truncatedName.padEnd(nameWidth) + ' ā ' +
|
|
191
|
+
server.category.padEnd(15) + ' ā ' +
|
|
192
|
+
statusColor(status.padEnd(8)) + ' ā ' +
|
|
193
|
+
chalk.cyan(truncatedCommand.padEnd(12)) + ' ā ' +
|
|
194
|
+
chalk.yellow(complexityStars.padEnd(10)) + ' ā ' +
|
|
195
|
+
chalk.gray(truncatedDesc);
|
|
196
|
+
|
|
197
|
+
console.log(row);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Summary by category
|
|
201
|
+
console.log(chalk.blue('\nš MCP Server Summary by Category:'));
|
|
202
|
+
for (const [category, servers] of Object.entries(analysis.byCategory)) {
|
|
203
|
+
const enabledCount = servers.filter(s => s.enabled).length;
|
|
204
|
+
const disabledCount = servers.filter(s => !s.enabled).length;
|
|
205
|
+
|
|
206
|
+
console.log(chalk.gray(` ${category}: ${servers.length} servers`) +
|
|
207
|
+
chalk.green(` (${enabledCount} enabled`) +
|
|
208
|
+
(disabledCount > 0 ? chalk.yellow(`, ${disabledCount} disabled)`) : chalk.green(')')));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Complexity analysis
|
|
212
|
+
console.log(chalk.blue('\nš§ Complexity Distribution:'));
|
|
213
|
+
const complexityCount = {};
|
|
214
|
+
analysis.servers.forEach(server => {
|
|
215
|
+
complexityCount[server.complexity] = (complexityCount[server.complexity] || 0) + 1;
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
for (let i = 1; i <= 5; i++) {
|
|
219
|
+
const count = complexityCount[i] || 0;
|
|
220
|
+
if (count > 0) {
|
|
221
|
+
const stars = 'ā
'.repeat(i) + 'ā'.repeat(5 - i);
|
|
222
|
+
console.log(chalk.gray(` ${chalk.yellow(stars)} (${i}/5): ${count} servers`));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async function runMCPStats(options) {
|
|
228
|
+
const targetDir = options.directory || process.cwd();
|
|
229
|
+
|
|
230
|
+
console.log(chalk.blue('š Claude Code MCP Server Analysis'));
|
|
231
|
+
console.log(chalk.gray(`Target directory: ${targetDir}`));
|
|
232
|
+
|
|
233
|
+
const analysis = await analyzeMCPServers(targetDir);
|
|
234
|
+
|
|
235
|
+
if (!analysis) {
|
|
236
|
+
console.log(chalk.yellow('\nš” No MCP servers found.'));
|
|
237
|
+
console.log(chalk.gray('Would you like to set up Claude Code Templates to add MCP servers?'));
|
|
238
|
+
|
|
239
|
+
const { setupMCP } = await inquirer.prompt([{
|
|
240
|
+
type: 'confirm',
|
|
241
|
+
name: 'setupMCP',
|
|
242
|
+
message: 'Set up MCP servers with Claude Code Templates?',
|
|
243
|
+
default: true
|
|
244
|
+
}]);
|
|
245
|
+
|
|
246
|
+
if (setupMCP) {
|
|
247
|
+
console.log(chalk.blue('\nš Starting Claude Code Templates setup...'));
|
|
248
|
+
|
|
249
|
+
// Import and run the main setup
|
|
250
|
+
const createClaudeConfig = require('./index');
|
|
251
|
+
await createClaudeConfig({ ...options, directory: targetDir });
|
|
252
|
+
}
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
displayMCPStats(analysis);
|
|
257
|
+
|
|
258
|
+
// Ask if user wants Claude Code to review and optimize MCP configuration
|
|
259
|
+
console.log(chalk.blue('\nš¤ Optimization Opportunity'));
|
|
260
|
+
console.log(chalk.gray('Claude Code can analyze your MCP server configuration and suggest optimizations.'));
|
|
261
|
+
|
|
262
|
+
const { optimizeMCP } = await inquirer.prompt([{
|
|
263
|
+
type: 'confirm',
|
|
264
|
+
name: 'optimizeMCP',
|
|
265
|
+
message: 'Would you like Claude Code to review and optimize your MCP server configuration?',
|
|
266
|
+
default: true
|
|
267
|
+
}]);
|
|
268
|
+
|
|
269
|
+
if (optimizeMCP) {
|
|
270
|
+
console.log(chalk.blue('\nš Launching Claude Code for MCP optimization...'));
|
|
271
|
+
|
|
272
|
+
// Prepare the optimization prompt
|
|
273
|
+
const mcpSummary = `I have ${analysis.totalServers} MCP servers configured in my .mcp.json file:
|
|
274
|
+
|
|
275
|
+
${Object.entries(analysis.byCategory).map(([category, servers]) =>
|
|
276
|
+
`${category}: ${servers.length} servers (${servers.filter(s => s.enabled).length} enabled, ${servers.filter(s => !s.enabled).length} disabled)`
|
|
277
|
+
).join('\n')}
|
|
278
|
+
|
|
279
|
+
MCP Server details:
|
|
280
|
+
${analysis.servers.map(server =>
|
|
281
|
+
`- ${server.name} (${server.category}): ${server.enabled ? 'Enabled' : 'Disabled'} - Command: ${server.command} - Complexity: ${server.complexity}/5 - ${server.description}`
|
|
282
|
+
).join('\n')}
|
|
283
|
+
|
|
284
|
+
Please review my MCP server configuration and suggest optimizations for:
|
|
285
|
+
1. Server selection and relevance to my development workflow
|
|
286
|
+
2. Missing MCP servers that could improve my productivity
|
|
287
|
+
3. Server configuration optimization (command, args, environment variables)
|
|
288
|
+
4. Performance considerations and resource usage
|
|
289
|
+
5. Security best practices for MCP server configurations
|
|
290
|
+
6. Redundant or conflicting servers
|
|
291
|
+
7. Integration opportunities between different MCP servers
|
|
292
|
+
|
|
293
|
+
Consider my project structure and development needs to suggest the most beneficial MCP server setup.`;
|
|
294
|
+
|
|
295
|
+
const claudeCommand = `claude "${mcpSummary}"`;
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
const child = spawn('sh', ['-c', claudeCommand], {
|
|
299
|
+
stdio: 'inherit',
|
|
300
|
+
cwd: targetDir
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
child.on('error', (error) => {
|
|
304
|
+
console.error(chalk.red('ā Error launching Claude Code:'), error.message);
|
|
305
|
+
console.log(chalk.yellow('š” Make sure Claude Code is installed: npm install -g @anthropic-ai/claude-code'));
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
} catch (error) {
|
|
309
|
+
console.error(chalk.red('ā Error launching Claude Code:'), error.message);
|
|
310
|
+
console.log(chalk.yellow('š” Make sure Claude Code is installed and accessible.'));
|
|
311
|
+
}
|
|
312
|
+
} else {
|
|
313
|
+
console.log(chalk.gray('\nā
MCP analysis complete. You can run this command again anytime to re-analyze your MCP configuration.'));
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
module.exports = {
|
|
318
|
+
runMCPStats,
|
|
319
|
+
analyzeMCPServers,
|
|
320
|
+
displayMCPStats
|
|
321
|
+
};
|