claude-code-templates 1.2.0 → 1.3.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/package.json +10 -8
- package/src/command-scanner.js +1 -1
- package/src/file-operations.js +44 -3
- package/src/hook-scanner.js +229 -52
- package/src/index.js +23 -3
- package/src/prompts.js +68 -2
- package/src/templates.js +31 -8
- package/scripts/sync-templates.js +0 -182
- package/templates/common/.claude/commands/git-workflow.md +0 -239
- package/templates/common/.claude/commands/project-setup.md +0 -316
- package/templates/common/CLAUDE.md +0 -109
- package/templates/common/README.md +0 -96
- package/templates/go/README.md +0 -25
- package/templates/javascript-typescript/.claude/commands/api-endpoint.md +0 -51
- package/templates/javascript-typescript/.claude/commands/debug.md +0 -52
- package/templates/javascript-typescript/.claude/commands/lint.md +0 -48
- package/templates/javascript-typescript/.claude/commands/npm-scripts.md +0 -48
- package/templates/javascript-typescript/.claude/commands/refactor.md +0 -55
- package/templates/javascript-typescript/.claude/commands/test.md +0 -61
- package/templates/javascript-typescript/.claude/commands/typescript-migrate.md +0 -51
- package/templates/javascript-typescript/.claude/settings.json +0 -142
- package/templates/javascript-typescript/.mcp.json +0 -13
- package/templates/javascript-typescript/CLAUDE.md +0 -185
- package/templates/javascript-typescript/README.md +0 -259
- package/templates/javascript-typescript/examples/angular-app/.claude/commands/components.md +0 -63
- package/templates/javascript-typescript/examples/angular-app/.claude/commands/services.md +0 -62
- package/templates/javascript-typescript/examples/node-api/.claude/commands/api-endpoint.md +0 -46
- package/templates/javascript-typescript/examples/node-api/.claude/commands/database.md +0 -56
- package/templates/javascript-typescript/examples/node-api/.claude/commands/middleware.md +0 -61
- package/templates/javascript-typescript/examples/node-api/.claude/commands/route.md +0 -57
- package/templates/javascript-typescript/examples/node-api/CLAUDE.md +0 -102
- package/templates/javascript-typescript/examples/react-app/.claude/commands/component.md +0 -29
- package/templates/javascript-typescript/examples/react-app/.claude/commands/hooks.md +0 -44
- package/templates/javascript-typescript/examples/react-app/.claude/commands/state-management.md +0 -45
- package/templates/javascript-typescript/examples/react-app/CLAUDE.md +0 -81
- package/templates/javascript-typescript/examples/vue-app/.claude/commands/components.md +0 -46
- package/templates/javascript-typescript/examples/vue-app/.claude/commands/composables.md +0 -51
- package/templates/python/.claude/commands/django-model.md +0 -124
- package/templates/python/.claude/commands/flask-route.md +0 -217
- package/templates/python/.claude/commands/lint.md +0 -111
- package/templates/python/.claude/commands/test.md +0 -73
- package/templates/python/CLAUDE.md +0 -276
- package/templates/rust/README.md +0 -26
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-templates",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "CLI tool to setup Claude Code configurations with
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "CLI tool to setup Claude Code configurations with framework-specific commands and automation hooks for JavaScript/TypeScript and Python projects",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"create-claude-config": "bin/create-claude-config.js",
|
|
@@ -20,10 +20,7 @@
|
|
|
20
20
|
"dev:link": "npm link",
|
|
21
21
|
"dev:unlink": "npm unlink -g claude-code-templates",
|
|
22
22
|
"pretest": "npm run dev:link",
|
|
23
|
-
"
|
|
24
|
-
"presync": "echo \"🔄 Starting template synchronization...\"",
|
|
25
|
-
"postsync": "echo \"✅ Synchronization completed. Ready to publish!\"",
|
|
26
|
-
"prepublishOnly": "npm run sync && npm run test"
|
|
23
|
+
"prepublishOnly": "npm run test"
|
|
27
24
|
},
|
|
28
25
|
"keywords": [
|
|
29
26
|
"claude",
|
|
@@ -37,7 +34,14 @@
|
|
|
37
34
|
"automation",
|
|
38
35
|
"javascript",
|
|
39
36
|
"typescript",
|
|
37
|
+
"react",
|
|
38
|
+
"vue",
|
|
39
|
+
"angular",
|
|
40
|
+
"nodejs",
|
|
40
41
|
"python",
|
|
42
|
+
"django",
|
|
43
|
+
"flask",
|
|
44
|
+
"fastapi",
|
|
41
45
|
"rust",
|
|
42
46
|
"go"
|
|
43
47
|
],
|
|
@@ -65,8 +69,6 @@
|
|
|
65
69
|
"files": [
|
|
66
70
|
"bin/",
|
|
67
71
|
"src/",
|
|
68
|
-
"scripts/",
|
|
69
|
-
"templates/",
|
|
70
72
|
"README.md"
|
|
71
73
|
]
|
|
72
74
|
}
|
package/src/command-scanner.js
CHANGED
|
@@ -7,7 +7,7 @@ const path = require('path');
|
|
|
7
7
|
* @returns {Array} Array of available commands with metadata
|
|
8
8
|
*/
|
|
9
9
|
function getAvailableCommands(language) {
|
|
10
|
-
const templatesDir = path.join(__dirname, '
|
|
10
|
+
const templatesDir = path.join(__dirname, '../../');
|
|
11
11
|
const languageDir = path.join(templatesDir, language);
|
|
12
12
|
|
|
13
13
|
// Check if language directory exists
|
package/src/file-operations.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const fs = require('fs-extra');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
|
-
const { getHooksForLanguage, filterHooksBySelection } = require('./hook-scanner');
|
|
4
|
+
const { getHooksForLanguage, filterHooksBySelection, getMCPsForLanguage, filterMCPsBySelection } = require('./hook-scanner');
|
|
5
5
|
|
|
6
6
|
async function copyTemplateFiles(templateConfig, targetDir) {
|
|
7
|
-
const templateDir = path.join(__dirname, '
|
|
7
|
+
const templateDir = path.join(__dirname, '../../');
|
|
8
8
|
|
|
9
9
|
// Check if CLAUDE.md already exists
|
|
10
10
|
const claudeFile = path.join(targetDir, 'CLAUDE.md');
|
|
@@ -79,6 +79,10 @@ async function copyTemplateFiles(templateConfig, targetDir) {
|
|
|
79
79
|
// Handle settings.json with hook filtering
|
|
80
80
|
await processSettingsFile(sourcePath, destPath, templateConfig);
|
|
81
81
|
console.log(chalk.green(`✓ Copied ${file.source} → ${file.destination} (with selected hooks)`));
|
|
82
|
+
} else if (file.source.includes('.mcp.json') && templateConfig.selectedMCPs) {
|
|
83
|
+
// Handle .mcp.json with MCP filtering
|
|
84
|
+
await processMCPFile(sourcePath, destPath, templateConfig);
|
|
85
|
+
console.log(chalk.green(`✓ Copied ${file.source} → ${file.destination} (with selected MCPs)`));
|
|
82
86
|
} else {
|
|
83
87
|
// Copy regular files (CLAUDE.md, settings.json, etc.)
|
|
84
88
|
await fs.copy(sourcePath, destPath, {
|
|
@@ -121,6 +125,11 @@ async function copyTemplateFiles(templateConfig, targetDir) {
|
|
|
121
125
|
if (templateConfig.selectedHooks && templateConfig.selectedHooks.length > 0) {
|
|
122
126
|
console.log(chalk.magenta(`🔧 Installed ${templateConfig.selectedHooks.length} automation hooks`));
|
|
123
127
|
}
|
|
128
|
+
|
|
129
|
+
// Report MCP selection
|
|
130
|
+
if (templateConfig.selectedMCPs && templateConfig.selectedMCPs.length > 0) {
|
|
131
|
+
console.log(chalk.blue(`🔧 Installed ${templateConfig.selectedMCPs.length} MCP`));
|
|
132
|
+
}
|
|
124
133
|
}
|
|
125
134
|
|
|
126
135
|
async function processSettingsFile(sourcePath, destPath, templateConfig) {
|
|
@@ -155,6 +164,37 @@ async function processSettingsFile(sourcePath, destPath, templateConfig) {
|
|
|
155
164
|
}
|
|
156
165
|
}
|
|
157
166
|
|
|
167
|
+
async function processMCPFile(sourcePath, destPath, templateConfig) {
|
|
168
|
+
try {
|
|
169
|
+
// Read the original MCP file
|
|
170
|
+
const originalMCPData = JSON.parse(await fs.readFile(sourcePath, 'utf8'));
|
|
171
|
+
|
|
172
|
+
// If MCPs are selected, filter them
|
|
173
|
+
if (templateConfig.selectedMCPs && templateConfig.selectedMCPs.length > 0) {
|
|
174
|
+
const availableMCPs = getMCPsForLanguage(templateConfig.language);
|
|
175
|
+
const filteredMCPData = filterMCPsBySelection(
|
|
176
|
+
originalMCPData,
|
|
177
|
+
templateConfig.selectedMCPs,
|
|
178
|
+
availableMCPs
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
// Write the filtered MCP data
|
|
182
|
+
await fs.ensureDir(path.dirname(destPath));
|
|
183
|
+
await fs.writeFile(destPath, JSON.stringify(filteredMCPData, null, 2));
|
|
184
|
+
} else {
|
|
185
|
+
// No MCPs selected, create empty MCP file
|
|
186
|
+
const emptyMCPData = { mcpServers: {} };
|
|
187
|
+
|
|
188
|
+
await fs.ensureDir(path.dirname(destPath));
|
|
189
|
+
await fs.writeFile(destPath, JSON.stringify(emptyMCPData, null, 2));
|
|
190
|
+
}
|
|
191
|
+
} catch (error) {
|
|
192
|
+
console.error(chalk.red(`Failed to process MCP file: ${error.message}`));
|
|
193
|
+
// Fallback to copying original file
|
|
194
|
+
await fs.copy(sourcePath, destPath);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
158
198
|
async function ensureDirectoryExists(dirPath) {
|
|
159
199
|
try {
|
|
160
200
|
await fs.ensureDir(dirPath);
|
|
@@ -180,5 +220,6 @@ module.exports = {
|
|
|
180
220
|
copyTemplateFiles,
|
|
181
221
|
ensureDirectoryExists,
|
|
182
222
|
checkWritePermissions,
|
|
183
|
-
processSettingsFile
|
|
223
|
+
processSettingsFile,
|
|
224
|
+
processMCPFile
|
|
184
225
|
};
|
package/src/hook-scanner.js
CHANGED
|
@@ -110,56 +110,129 @@ function getHooksFromSettings(settingsPath) {
|
|
|
110
110
|
function getHookDescription(hook, matcher, type) {
|
|
111
111
|
const command = hook.command || '';
|
|
112
112
|
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
113
|
+
// Extract key patterns for more specific descriptions
|
|
114
|
+
if (command.includes('jq -r') && command.includes('bash-command-log')) {
|
|
115
|
+
return 'Log all Bash commands for debugging';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (command.includes('console\\.log')) {
|
|
119
|
+
return 'Block console.log statements in JS/TS files';
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (command.includes('print(') && command.includes('py$')) {
|
|
123
|
+
return 'Block print() statements in Python files';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (command.includes('fmt.Print') && command.includes('go$')) {
|
|
127
|
+
return 'Block fmt.Print statements in Go files';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (command.includes('println!') && command.includes('rs$')) {
|
|
131
|
+
return 'Block println! macros in Rust files';
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (command.includes('npm audit') || command.includes('pip-audit') || command.includes('cargo audit')) {
|
|
135
|
+
return 'Security audit for dependencies';
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (command.includes('prettier --write')) {
|
|
139
|
+
return 'Auto-format JS/TS files with Prettier';
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (command.includes('black') && command.includes('py$')) {
|
|
143
|
+
return 'Auto-format Python files with Black';
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (command.includes('isort') && command.includes('py$')) {
|
|
147
|
+
return 'Auto-sort Python imports with isort';
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (command.includes('gofmt') && command.includes('go$')) {
|
|
151
|
+
return 'Auto-format Go files with gofmt';
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (command.includes('goimports')) {
|
|
155
|
+
return 'Auto-format Go imports with goimports';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (command.includes('rustfmt') && command.includes('rs$')) {
|
|
159
|
+
return 'Auto-format Rust files with rustfmt';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (command.includes('tsc --noEmit')) {
|
|
163
|
+
return 'Run TypeScript type checking';
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (command.includes('flake8') && !command.includes('git diff')) {
|
|
167
|
+
return 'Run Python linting with flake8';
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (command.includes('mypy')) {
|
|
171
|
+
return 'Run Python type checking with mypy';
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (command.includes('go vet') && !command.includes('git diff')) {
|
|
175
|
+
return 'Run Go static analysis with go vet';
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (command.includes('cargo check')) {
|
|
179
|
+
return 'Run Rust compilation checks';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (command.includes('cargo clippy') && !command.includes('git diff')) {
|
|
183
|
+
return 'Run Rust linting with clippy';
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (command.includes('import \\* from')) {
|
|
187
|
+
return 'Warn about wildcard imports';
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (command.includes('jest') || command.includes('vitest')) {
|
|
191
|
+
return 'Auto-run tests for modified files';
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (command.includes('pytest')) {
|
|
195
|
+
return 'Auto-run Python tests for modified files';
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (command.includes('go test')) {
|
|
199
|
+
return 'Auto-run Go tests for modified files';
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (command.includes('cargo test')) {
|
|
203
|
+
return 'Auto-run Rust tests for modified files';
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (command.includes('eslint') && command.includes('git diff')) {
|
|
207
|
+
return 'Run ESLint on changed files';
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (command.includes('flake8') && command.includes('git diff')) {
|
|
211
|
+
return 'Run Python linting on changed files';
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (command.includes('bandit')) {
|
|
215
|
+
return 'Run Python security analysis';
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (command.includes('go vet') && command.includes('git diff')) {
|
|
219
|
+
return 'Run Go analysis on changed files';
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (command.includes('staticcheck')) {
|
|
223
|
+
return 'Run Go static analysis on changed files';
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (command.includes('cargo clippy') && command.includes('git diff')) {
|
|
227
|
+
return 'Run Rust linting on changed files';
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (command.includes('bundlesize') || command.includes('webpack-bundle-analyzer')) {
|
|
231
|
+
return 'Analyze bundle size impact';
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (command.includes('notifications.log')) {
|
|
235
|
+
return 'Log Claude Code notifications';
|
|
163
236
|
}
|
|
164
237
|
|
|
165
238
|
// Generate description based on command analysis
|
|
@@ -194,7 +267,7 @@ function getHookDescription(hook, matcher, type) {
|
|
|
194
267
|
* @returns {Array} Array of available hooks for the language
|
|
195
268
|
*/
|
|
196
269
|
function getHooksForLanguage(language) {
|
|
197
|
-
const templateDir = path.join(__dirname, '
|
|
270
|
+
const templateDir = path.join(__dirname, '../../', language);
|
|
198
271
|
const settingsPath = path.join(templateDir, '.claude', 'settings.json');
|
|
199
272
|
|
|
200
273
|
return getHooksFromSettings(settingsPath);
|
|
@@ -263,9 +336,113 @@ function filterHooksBySelection(originalSettings, selectedHookIds, availableHook
|
|
|
263
336
|
return filteredSettings;
|
|
264
337
|
}
|
|
265
338
|
|
|
339
|
+
/**
|
|
340
|
+
* Extracts and describes MCPs from a .mcp.json file
|
|
341
|
+
* @param {string} mcpPath - Path to the .mcp.json file
|
|
342
|
+
* @returns {Array} Array of MCP descriptions
|
|
343
|
+
*/
|
|
344
|
+
function getMCPsFromFile(mcpPath) {
|
|
345
|
+
if (!fs.existsSync(mcpPath)) {
|
|
346
|
+
return [];
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
try {
|
|
350
|
+
const mcpData = JSON.parse(fs.readFileSync(mcpPath, 'utf8'));
|
|
351
|
+
const mcps = [];
|
|
352
|
+
|
|
353
|
+
if (mcpData.mcpServers) {
|
|
354
|
+
Object.keys(mcpData.mcpServers).forEach((serverId) => {
|
|
355
|
+
const server = mcpData.mcpServers[serverId];
|
|
356
|
+
mcps.push({
|
|
357
|
+
id: serverId,
|
|
358
|
+
name: server.name || serverId,
|
|
359
|
+
description: server.description || 'No description available',
|
|
360
|
+
command: server.command,
|
|
361
|
+
args: server.args || [],
|
|
362
|
+
env: server.env || {},
|
|
363
|
+
originalServer: server,
|
|
364
|
+
checked: getDefaultMCPSelection(serverId) // Default selection logic
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return mcps;
|
|
370
|
+
} catch (error) {
|
|
371
|
+
console.error(`Error parsing MCP file ${mcpPath}:`, error.message);
|
|
372
|
+
return [];
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Determines default selection for MCP servers
|
|
378
|
+
* @param {string} serverId - The MCP server ID
|
|
379
|
+
* @returns {boolean} Whether the MCP should be selected by default
|
|
380
|
+
*/
|
|
381
|
+
function getDefaultMCPSelection(serverId) {
|
|
382
|
+
// Default to checked for commonly useful MCPs
|
|
383
|
+
const defaultSelected = [
|
|
384
|
+
'filesystem',
|
|
385
|
+
'memory-bank',
|
|
386
|
+
'sequential-thinking',
|
|
387
|
+
'typescript-sdk',
|
|
388
|
+
'python-sdk',
|
|
389
|
+
'rust-sdk',
|
|
390
|
+
'go-sdk'
|
|
391
|
+
];
|
|
392
|
+
|
|
393
|
+
return defaultSelected.includes(serverId);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Gets MCPs for a specific language
|
|
398
|
+
* @param {string} language - The programming language
|
|
399
|
+
* @returns {Array} Array of available MCPs for the language
|
|
400
|
+
*/
|
|
401
|
+
function getMCPsForLanguage(language) {
|
|
402
|
+
const templateDir = path.join(__dirname, '../../', language);
|
|
403
|
+
const mcpPath = path.join(templateDir, '.mcp.json');
|
|
404
|
+
|
|
405
|
+
return getMCPsFromFile(mcpPath);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Filters MCPs based on user selection
|
|
410
|
+
* @param {Object} originalMCPData - Original MCP data object
|
|
411
|
+
* @param {Array} selectedMCPIds - Array of selected MCP IDs
|
|
412
|
+
* @param {Array} availableMCPs - Array of available MCPs
|
|
413
|
+
* @returns {Object} Filtered MCP data object
|
|
414
|
+
*/
|
|
415
|
+
function filterMCPsBySelection(originalMCPData, selectedMCPIds, availableMCPs) {
|
|
416
|
+
if (!originalMCPData.mcpServers) {
|
|
417
|
+
return originalMCPData;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const filteredMCPData = {
|
|
421
|
+
mcpServers: {}
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
// Create a map of selected MCPs for quick lookup
|
|
425
|
+
const selectedMCPs = new Map();
|
|
426
|
+
availableMCPs.forEach(mcp => {
|
|
427
|
+
if (selectedMCPIds.includes(mcp.id)) {
|
|
428
|
+
selectedMCPs.set(mcp.id, mcp);
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// Add selected MCPs to filtered data
|
|
433
|
+
selectedMCPs.forEach((mcp, mcpId) => {
|
|
434
|
+
filteredMCPData.mcpServers[mcpId] = mcp.originalServer;
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
return filteredMCPData;
|
|
438
|
+
}
|
|
439
|
+
|
|
266
440
|
module.exports = {
|
|
267
441
|
getHooksFromSettings,
|
|
268
442
|
getHooksForLanguage,
|
|
269
443
|
filterHooksBySelection,
|
|
270
|
-
getHookDescription
|
|
444
|
+
getHookDescription,
|
|
445
|
+
getMCPsFromFile,
|
|
446
|
+
getMCPsForLanguage,
|
|
447
|
+
filterMCPsBySelection
|
|
271
448
|
};
|
package/src/index.js
CHANGED
|
@@ -4,10 +4,10 @@ const fs = require('fs-extra');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const ora = require('ora');
|
|
6
6
|
const { detectProject } = require('./utils');
|
|
7
|
-
const { getTemplateConfig } = require('./templates');
|
|
7
|
+
const { getTemplateConfig, TEMPLATES_CONFIG } = require('./templates');
|
|
8
8
|
const { createPrompts, interactivePrompts } = require('./prompts');
|
|
9
9
|
const { copyTemplateFiles } = require('./file-operations');
|
|
10
|
-
const { getHooksForLanguage } = require('./hook-scanner');
|
|
10
|
+
const { getHooksForLanguage, getMCPsForLanguage } = require('./hook-scanner');
|
|
11
11
|
|
|
12
12
|
async function createClaudeConfig(options = {}) {
|
|
13
13
|
const targetDir = options.directory || process.cwd();
|
|
@@ -24,14 +24,24 @@ async function createClaudeConfig(options = {}) {
|
|
|
24
24
|
if (options.yes) {
|
|
25
25
|
// Use defaults
|
|
26
26
|
const selectedLanguage = options.language || projectInfo.detectedLanguage || 'common';
|
|
27
|
+
|
|
28
|
+
// Check if selected language is coming soon
|
|
29
|
+
if (selectedLanguage && TEMPLATES_CONFIG[selectedLanguage] && TEMPLATES_CONFIG[selectedLanguage].comingSoon) {
|
|
30
|
+
console.log(chalk.red(`❌ ${selectedLanguage} is not available yet. Coming soon!`));
|
|
31
|
+
console.log(chalk.yellow('Available languages: common, javascript-typescript, python'));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
27
34
|
const availableHooks = getHooksForLanguage(selectedLanguage);
|
|
28
35
|
const defaultHooks = availableHooks.filter(hook => hook.checked).map(hook => hook.id);
|
|
36
|
+
const availableMCPs = getMCPsForLanguage(selectedLanguage);
|
|
37
|
+
const defaultMCPs = availableMCPs.filter(mcp => mcp.checked).map(mcp => mcp.id);
|
|
29
38
|
|
|
30
39
|
config = {
|
|
31
40
|
language: selectedLanguage,
|
|
32
41
|
framework: options.framework || projectInfo.detectedFramework || 'none',
|
|
33
42
|
features: [],
|
|
34
|
-
hooks: defaultHooks
|
|
43
|
+
hooks: defaultHooks,
|
|
44
|
+
mcps: defaultMCPs
|
|
35
45
|
};
|
|
36
46
|
} else {
|
|
37
47
|
// Interactive prompts with back navigation
|
|
@@ -53,6 +63,12 @@ async function createClaudeConfig(options = {}) {
|
|
|
53
63
|
templateConfig.language = config.language; // Ensure language is available for hook filtering
|
|
54
64
|
}
|
|
55
65
|
|
|
66
|
+
// Add selected MCPs to template config
|
|
67
|
+
if (config.mcps) {
|
|
68
|
+
templateConfig.selectedMCPs = config.mcps;
|
|
69
|
+
templateConfig.language = config.language; // Ensure language is available for MCP filtering
|
|
70
|
+
}
|
|
71
|
+
|
|
56
72
|
if (options.dryRun) {
|
|
57
73
|
console.log(chalk.yellow('🔍 Dry run - showing what would be copied:'));
|
|
58
74
|
templateConfig.files.forEach(file => {
|
|
@@ -89,6 +105,10 @@ async function createClaudeConfig(options = {}) {
|
|
|
89
105
|
if (config.hooks && config.hooks.length > 0) {
|
|
90
106
|
console.log(chalk.magenta(`🔧 ${config.hooks.length} automation hooks have been configured`));
|
|
91
107
|
}
|
|
108
|
+
|
|
109
|
+
if (config.mcps && config.mcps.length > 0) {
|
|
110
|
+
console.log(chalk.blue(`🔧 ${config.mcps.length} MCP servers have been configured`));
|
|
111
|
+
}
|
|
92
112
|
}
|
|
93
113
|
|
|
94
114
|
module.exports = createClaudeConfig;
|
package/src/prompts.js
CHANGED
|
@@ -13,7 +13,7 @@ class CustomCheckboxPrompt extends inquirer.prompt.prompts.checkbox {
|
|
|
13
13
|
inquirer.registerPrompt('checkbox', CustomCheckboxPrompt);
|
|
14
14
|
const { getAvailableLanguages, getFrameworksForLanguage } = require('./templates');
|
|
15
15
|
const { getCommandsForLanguageAndFramework } = require('./command-scanner');
|
|
16
|
-
const { getHooksForLanguage } = require('./hook-scanner');
|
|
16
|
+
const { getHooksForLanguage, getMCPsForLanguage } = require('./hook-scanner');
|
|
17
17
|
|
|
18
18
|
async function interactivePrompts(projectInfo, options = {}) {
|
|
19
19
|
const state = {
|
|
@@ -25,7 +25,7 @@ async function interactivePrompts(projectInfo, options = {}) {
|
|
|
25
25
|
// Build steps array based on options
|
|
26
26
|
if (!options.language) state.steps.push('language');
|
|
27
27
|
if (!options.framework) state.steps.push('framework');
|
|
28
|
-
state.steps.push('commands', 'hooks', 'confirm');
|
|
28
|
+
state.steps.push('commands', 'hooks', 'mcps', 'confirm');
|
|
29
29
|
|
|
30
30
|
while (state.currentStep < state.steps.length) {
|
|
31
31
|
const stepName = state.steps[state.currentStep];
|
|
@@ -170,11 +170,38 @@ function getStepConfig(stepName, currentAnswers, projectInfo, options) {
|
|
|
170
170
|
pageSize: 15
|
|
171
171
|
};
|
|
172
172
|
|
|
173
|
+
case 'mcps':
|
|
174
|
+
const mcpLanguage = currentAnswers.language || options.language;
|
|
175
|
+
|
|
176
|
+
if (!mcpLanguage) {
|
|
177
|
+
return null; // Skip if no language selected
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const availableMCPs = getMCPsForLanguage(mcpLanguage);
|
|
181
|
+
|
|
182
|
+
if (availableMCPs.length === 0) {
|
|
183
|
+
return null; // Skip if no MCPs available
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
type: 'checkbox',
|
|
188
|
+
name: 'mcps',
|
|
189
|
+
message: 'Select MCP servers to include (use space to select):',
|
|
190
|
+
choices: availableMCPs.map(mcp => ({
|
|
191
|
+
value: mcp.id,
|
|
192
|
+
name: `${mcp.name} - ${mcp.description}`,
|
|
193
|
+
checked: mcp.checked
|
|
194
|
+
})),
|
|
195
|
+
prefix: chalk.blue('🔧'),
|
|
196
|
+
pageSize: 15
|
|
197
|
+
};
|
|
198
|
+
|
|
173
199
|
case 'confirm':
|
|
174
200
|
const confirmLanguage = currentAnswers.language || options.language || 'common';
|
|
175
201
|
const confirmFramework = currentAnswers.framework || options.framework || 'none';
|
|
176
202
|
const commandCount = currentAnswers.commands ? currentAnswers.commands.length : 0;
|
|
177
203
|
const hookCount = currentAnswers.hooks ? currentAnswers.hooks.length : 0;
|
|
204
|
+
const mcpCount = currentAnswers.mcps ? currentAnswers.mcps.length : 0;
|
|
178
205
|
|
|
179
206
|
let message = `Setup Claude Code for ${chalk.cyan(confirmLanguage)}`;
|
|
180
207
|
if (confirmFramework !== 'none') {
|
|
@@ -186,6 +213,9 @@ function getStepConfig(stepName, currentAnswers, projectInfo, options) {
|
|
|
186
213
|
if (hookCount > 0) {
|
|
187
214
|
message += ` (${chalk.magenta(hookCount)} hooks)`;
|
|
188
215
|
}
|
|
216
|
+
if (mcpCount > 0) {
|
|
217
|
+
message += ` (${chalk.blue(mcpCount)} MCP)`;
|
|
218
|
+
}
|
|
189
219
|
message += '?';
|
|
190
220
|
|
|
191
221
|
return {
|
|
@@ -312,6 +342,38 @@ function createPrompts(projectInfo, options = {}) {
|
|
|
312
342
|
return availableHooks.length > 0;
|
|
313
343
|
}
|
|
314
344
|
});
|
|
345
|
+
|
|
346
|
+
// MCP selection
|
|
347
|
+
prompts.push({
|
|
348
|
+
type: 'checkbox',
|
|
349
|
+
name: 'mcps',
|
|
350
|
+
message: 'Select MCP servers to include (use space to select):',
|
|
351
|
+
choices: (answers) => {
|
|
352
|
+
const selectedLanguage = answers.language || options.language;
|
|
353
|
+
|
|
354
|
+
if (!selectedLanguage) {
|
|
355
|
+
return [];
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const availableMCPs = getMCPsForLanguage(selectedLanguage);
|
|
359
|
+
|
|
360
|
+
return availableMCPs.map(mcp => ({
|
|
361
|
+
value: mcp.id,
|
|
362
|
+
name: `${mcp.name} - ${mcp.description}`,
|
|
363
|
+
checked: mcp.checked
|
|
364
|
+
}));
|
|
365
|
+
},
|
|
366
|
+
prefix: chalk.blue('🔧'),
|
|
367
|
+
pageSize: 15,
|
|
368
|
+
when: (answers) => {
|
|
369
|
+
const selectedLanguage = answers.language || options.language;
|
|
370
|
+
if (!selectedLanguage) {
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
const availableMCPs = getMCPsForLanguage(selectedLanguage);
|
|
374
|
+
return availableMCPs.length > 0;
|
|
375
|
+
}
|
|
376
|
+
});
|
|
315
377
|
|
|
316
378
|
// Confirmation
|
|
317
379
|
prompts.push({
|
|
@@ -322,6 +384,7 @@ function createPrompts(projectInfo, options = {}) {
|
|
|
322
384
|
const framework = answers.framework || options.framework || 'none';
|
|
323
385
|
const commandCount = answers.commands ? answers.commands.length : 0;
|
|
324
386
|
const hookCount = answers.hooks ? answers.hooks.length : 0;
|
|
387
|
+
const mcpCount = answers.mcps ? answers.mcps.length : 0;
|
|
325
388
|
|
|
326
389
|
let message = `Setup Claude Code for ${chalk.cyan(language)}`;
|
|
327
390
|
if (framework !== 'none') {
|
|
@@ -333,6 +396,9 @@ function createPrompts(projectInfo, options = {}) {
|
|
|
333
396
|
if (hookCount > 0) {
|
|
334
397
|
message += ` (${chalk.magenta(hookCount)} hooks)`;
|
|
335
398
|
}
|
|
399
|
+
if (mcpCount > 0) {
|
|
400
|
+
message += ` (${chalk.blue(mcpCount)} MCP)`;
|
|
401
|
+
}
|
|
336
402
|
message += '?';
|
|
337
403
|
|
|
338
404
|
return message;
|