claude-code-templates 1.14.1 → 1.14.2

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.
@@ -43,8 +43,9 @@ program
43
43
  .name('create-claude-config')
44
44
  .description('Setup Claude Code configurations for different programming languages')
45
45
  .version(require('../package.json').version)
46
- .option('-l, --language <language>', 'specify programming language')
47
- .option('-f, --framework <framework>', 'specify framework')
46
+ .option('-l, --language <language>', 'specify programming language (deprecated, use --template)')
47
+ .option('-f, --framework <framework>', 'specify framework (deprecated, use --template)')
48
+ .option('-t, --template <template>', 'specify template (e.g., common, javascript-typescript, python, ruby)')
48
49
  .option('-d, --directory <directory>', 'target directory (default: current directory)')
49
50
  .option('-y, --yes', 'skip prompts and use defaults')
50
51
  .option('--dry-run', 'show what would be copied without actually copying')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-templates",
3
- "version": "1.14.1",
3
+ "version": "1.14.2",
4
4
  "description": "CLI tool to setup Claude Code configurations with framework-specific commands, automation hooks and MCP Servers for your projects",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -86,7 +86,6 @@
86
86
  "bin/",
87
87
  "src/",
88
88
  "templates/",
89
- "components/",
90
89
  "README.md"
91
90
  ],
92
91
  "devDependencies": {
@@ -4,6 +4,152 @@ const chalk = require('chalk');
4
4
  const inquirer = require('inquirer');
5
5
  const { getHooksForLanguage, filterHooksBySelection, getMCPsForLanguage, filterMCPsBySelection } = require('./hook-scanner');
6
6
 
7
+ // GitHub configuration for downloading templates
8
+ const GITHUB_CONFIG = {
9
+ owner: 'davila7',
10
+ repo: 'claude-code-templates',
11
+ branch: 'main',
12
+ templatesPath: 'cli-tool/templates'
13
+ };
14
+
15
+ // Cache for downloaded files to avoid repeated downloads
16
+ const downloadCache = new Map();
17
+
18
+ async function downloadFileFromGitHub(filePath) {
19
+ // Check cache first
20
+ if (downloadCache.has(filePath)) {
21
+ return downloadCache.get(filePath);
22
+ }
23
+
24
+ const githubUrl = `https://raw.githubusercontent.com/${GITHUB_CONFIG.owner}/${GITHUB_CONFIG.repo}/${GITHUB_CONFIG.branch}/${GITHUB_CONFIG.templatesPath}/${filePath}`;
25
+
26
+ try {
27
+ const response = await fetch(githubUrl);
28
+ if (!response.ok) {
29
+ throw new Error(`Failed to download ${filePath}: ${response.status} ${response.statusText}`);
30
+ }
31
+
32
+ const content = await response.text();
33
+ downloadCache.set(filePath, content);
34
+ return content;
35
+ } catch (error) {
36
+ console.error(chalk.red(`❌ Error downloading ${filePath} from GitHub:`), error.message);
37
+ throw error;
38
+ }
39
+ }
40
+
41
+ async function downloadDirectoryFromGitHub(dirPath) {
42
+ // For directories, we need to get the list of files first
43
+ // GitHub API endpoint to get directory contents
44
+ const apiUrl = `https://api.github.com/repos/${GITHUB_CONFIG.owner}/${GITHUB_CONFIG.repo}/contents/${GITHUB_CONFIG.templatesPath}/${dirPath}?ref=${GITHUB_CONFIG.branch}`;
45
+
46
+ try {
47
+ const response = await fetch(apiUrl);
48
+ if (!response.ok) {
49
+ throw new Error(`Failed to get directory listing for ${dirPath}: ${response.status} ${response.statusText}`);
50
+ }
51
+
52
+ const items = await response.json();
53
+ const files = {};
54
+
55
+ for (const item of items) {
56
+ if (item.type === 'file') {
57
+ const relativePath = path.relative(GITHUB_CONFIG.templatesPath, item.path);
58
+ const content = await downloadFileFromGitHub(relativePath);
59
+ files[item.name] = content;
60
+ }
61
+ }
62
+
63
+ return files;
64
+ } catch (error) {
65
+ console.error(chalk.red(`❌ Error downloading directory ${dirPath} from GitHub:`), error.message);
66
+ throw error;
67
+ }
68
+ }
69
+
70
+ // Helper functions for processing downloaded content
71
+ async function processSettingsFileFromContent(settingsContent, destPath, templateConfig) {
72
+ const settings = JSON.parse(settingsContent);
73
+
74
+ // Filter hooks based on selection
75
+ if (templateConfig.selectedHooks && settings.hooks) {
76
+ settings.hooks = filterHooksBySelection(settings.hooks, templateConfig.selectedHooks);
77
+ }
78
+
79
+ const destDir = path.dirname(destPath);
80
+ await fs.ensureDir(destDir);
81
+ await fs.writeJson(destPath, settings, { spaces: 2 });
82
+ }
83
+
84
+ async function mergeSettingsFileFromContent(settingsContent, destPath, templateConfig) {
85
+ const newSettings = JSON.parse(settingsContent);
86
+ let existingSettings = {};
87
+
88
+ if (await fs.pathExists(destPath)) {
89
+ existingSettings = await fs.readJson(destPath);
90
+ }
91
+
92
+ // Filter hooks based on selection
93
+ if (templateConfig.selectedHooks && newSettings.hooks) {
94
+ newSettings.hooks = filterHooksBySelection(newSettings.hooks, templateConfig.selectedHooks);
95
+ }
96
+
97
+ // Merge settings
98
+ const mergedSettings = {
99
+ ...existingSettings,
100
+ ...newSettings,
101
+ hooks: {
102
+ ...existingSettings.hooks,
103
+ ...newSettings.hooks
104
+ }
105
+ };
106
+
107
+ const destDir = path.dirname(destPath);
108
+ await fs.ensureDir(destDir);
109
+ await fs.writeJson(destPath, mergedSettings, { spaces: 2 });
110
+ }
111
+
112
+ async function processMCPFileFromContent(mcpContent, destPath, templateConfig) {
113
+ const mcpConfig = JSON.parse(mcpContent);
114
+
115
+ // Filter MCPs based on selection
116
+ if (templateConfig.selectedMCPs && mcpConfig.mcpServers) {
117
+ mcpConfig.mcpServers = filterMCPsBySelection(mcpConfig.mcpServers, templateConfig.selectedMCPs);
118
+ }
119
+
120
+ const destDir = path.dirname(destPath);
121
+ await fs.ensureDir(destDir);
122
+ await fs.writeJson(destPath, mcpConfig, { spaces: 2 });
123
+ }
124
+
125
+ async function mergeMCPFileFromContent(mcpContent, destPath, templateConfig) {
126
+ const newMcpConfig = JSON.parse(mcpContent);
127
+ let existingMcpConfig = {};
128
+
129
+ if (await fs.pathExists(destPath)) {
130
+ existingMcpConfig = await fs.readJson(destPath);
131
+ }
132
+
133
+ // Filter MCPs based on selection
134
+ if (templateConfig.selectedMCPs && newMcpConfig.mcpServers) {
135
+ newMcpConfig.mcpServers = filterMCPsBySelection(newMcpConfig.mcpServers, templateConfig.selectedMCPs);
136
+ }
137
+
138
+ // Merge MCP configurations
139
+ const mergedMcpConfig = {
140
+ ...existingMcpConfig,
141
+ ...newMcpConfig,
142
+ mcpServers: {
143
+ ...existingMcpConfig.mcpServers,
144
+ ...newMcpConfig.mcpServers
145
+ }
146
+ };
147
+
148
+ const destDir = path.dirname(destPath);
149
+ await fs.ensureDir(destDir);
150
+ await fs.writeJson(destPath, mergedMcpConfig, { spaces: 2 });
151
+ }
152
+
7
153
  async function checkExistingFiles(targetDir, templateConfig) {
8
154
  const existingFiles = [];
9
155
 
@@ -86,7 +232,7 @@ async function createBackups(existingFiles, targetDir) {
86
232
  }
87
233
 
88
234
  async function copyTemplateFiles(templateConfig, targetDir, options = {}) {
89
- const templateDir = path.join(__dirname, '../templates');
235
+ console.log(chalk.gray(`📥 Downloading templates from GitHub (${GITHUB_CONFIG.branch} branch)...`));
90
236
 
91
237
  // Check for existing files and get user preference
92
238
  const existingFiles = await checkExistingFiles(targetDir, templateConfig);
@@ -114,7 +260,6 @@ async function copyTemplateFiles(templateConfig, targetDir, options = {}) {
114
260
 
115
261
  // Copy base files and framework-specific files
116
262
  for (const file of templateConfig.files) {
117
- const sourcePath = path.join(templateDir, file.source);
118
263
  const destPath = path.join(targetDir, file.destination);
119
264
 
120
265
  try {
@@ -123,94 +268,115 @@ async function copyTemplateFiles(templateConfig, targetDir, options = {}) {
123
268
  // This is a framework-specific commands directory - merge with existing commands
124
269
  await fs.ensureDir(destPath);
125
270
 
126
- // Copy framework-specific commands to the commands directory
127
- const frameworkFiles = await fs.readdir(sourcePath);
128
- for (const frameworkFile of frameworkFiles) {
129
- const srcFile = path.join(sourcePath, frameworkFile);
130
- const destFile = path.join(destPath, frameworkFile);
271
+ // Download framework-specific commands from GitHub
272
+ const frameworkFiles = await downloadDirectoryFromGitHub(file.source);
273
+ for (const [frameworkFileName, content] of Object.entries(frameworkFiles)) {
274
+ const destFile = path.join(destPath, frameworkFileName);
131
275
 
132
276
  // In merge mode, skip if file already exists
133
277
  if (userAction === 'merge' && await fs.pathExists(destFile)) {
134
- console.log(chalk.blue(`⏭️ Skipped ${frameworkFile} (already exists)`));
278
+ console.log(chalk.blue(`⏭️ Skipped ${frameworkFileName} (already exists)`));
135
279
  continue;
136
280
  }
137
281
 
138
- await fs.copy(srcFile, destFile, { overwrite: shouldOverwrite });
282
+ await fs.writeFile(destFile, content, 'utf8');
139
283
  }
140
284
 
141
- console.log(chalk.green(`✓ Copied framework commands ${file.source} → ${file.destination}`));
285
+ console.log(chalk.green(`✓ Downloaded framework commands ${file.source} → ${file.destination}`));
142
286
  } else if (file.source.includes('.claude') && !file.source.includes('examples/')) {
143
- // This is base .claude directory - copy it but handle commands specially
144
- await fs.copy(sourcePath, destPath, {
145
- overwrite: shouldOverwrite,
146
- filter: (src) => {
147
- // Skip the commands directory itself - we'll handle it separately
148
- return !src.endsWith('.claude/commands');
149
- }
150
- });
151
-
152
- // Now handle base commands specifically
153
- const baseCommandsPath = path.join(sourcePath, 'commands');
154
- const destCommandsPath = path.join(destPath, 'commands');
287
+ // This is base .claude directory - download it but handle commands specially
288
+ await fs.ensureDir(destPath);
155
289
 
156
- if (await fs.pathExists(baseCommandsPath)) {
157
- await fs.ensureDir(destCommandsPath);
158
-
159
- // Copy base commands, but exclude framework-specific ones that were moved
160
- const baseCommands = await fs.readdir(baseCommandsPath);
161
- const excludeCommands = ['react-component.md', 'route.md', 'api-endpoint.md']; // Commands moved to framework dirs
290
+ // Download base .claude directory structure from GitHub
291
+ try {
292
+ const baseClaudeFiles = await downloadDirectoryFromGitHub(file.source);
162
293
 
163
- for (const baseCommand of baseCommands) {
164
- if (!excludeCommands.includes(baseCommand)) {
165
- const srcFile = path.join(baseCommandsPath, baseCommand);
166
- const destFile = path.join(destCommandsPath, baseCommand);
294
+ // Write non-command files first
295
+ for (const [fileName, content] of Object.entries(baseClaudeFiles)) {
296
+ if (fileName !== 'commands') { // Skip commands directory, handle separately
297
+ const destFile = path.join(destPath, fileName);
167
298
 
168
299
  // In merge mode, skip if file already exists
169
300
  if (userAction === 'merge' && await fs.pathExists(destFile)) {
170
- console.log(chalk.blue(`⏭️ Skipped ${baseCommand} (already exists)`));
301
+ console.log(chalk.blue(`⏭️ Skipped ${fileName} (already exists)`));
171
302
  continue;
172
303
  }
173
304
 
174
- await fs.copy(srcFile, destFile, { overwrite: shouldOverwrite });
305
+ await fs.writeFile(destFile, content, 'utf8');
175
306
  }
176
307
  }
308
+
309
+ // Now handle base commands specifically
310
+ const destCommandsPath = path.join(destPath, 'commands');
311
+ await fs.ensureDir(destCommandsPath);
312
+
313
+ // Download base commands from GitHub
314
+ const baseCommandsDir = `${file.source}/commands`;
315
+ try {
316
+ const baseCommands = await downloadDirectoryFromGitHub(baseCommandsDir);
317
+ const excludeCommands = ['react-component.md', 'route.md', 'api-endpoint.md']; // Commands moved to framework dirs
318
+
319
+ for (const [baseCommandName, commandContent] of Object.entries(baseCommands)) {
320
+ if (!excludeCommands.includes(baseCommandName)) {
321
+ const destFile = path.join(destCommandsPath, baseCommandName);
322
+
323
+ // In merge mode, skip if file already exists
324
+ if (userAction === 'merge' && await fs.pathExists(destFile)) {
325
+ console.log(chalk.blue(`⏭️ Skipped ${baseCommandName} (already exists)`));
326
+ continue;
327
+ }
328
+
329
+ await fs.writeFile(destFile, commandContent, 'utf8');
330
+ }
331
+ }
332
+ } catch (error) {
333
+ // Commands directory might not exist for some templates, that's ok
334
+ console.log(chalk.yellow(`⚠️ No commands directory found for ${baseCommandsDir}`));
335
+ }
336
+
337
+ } catch (error) {
338
+ console.error(chalk.red(`❌ Error downloading .claude directory: ${error.message}`));
339
+ throw error;
177
340
  }
178
341
 
179
- console.log(chalk.green(`✓ Copied base configuration and commands ${file.source} → ${file.destination}`));
342
+ console.log(chalk.green(`✓ Downloaded base configuration and commands ${file.source} → ${file.destination}`));
180
343
  } else if (file.source.includes('settings.json') && templateConfig.selectedHooks) {
344
+ // Download and process settings.json with hooks
345
+ const settingsContent = await downloadFileFromGitHub(file.source);
346
+
181
347
  // In merge mode, merge settings instead of overwriting
182
348
  if (userAction === 'merge') {
183
- await mergeSettingsFile(sourcePath, destPath, templateConfig);
349
+ await mergeSettingsFileFromContent(settingsContent, destPath, templateConfig);
184
350
  console.log(chalk.green(`✓ Merged ${file.source} → ${file.destination} (with selected hooks)`));
185
351
  } else {
186
- await processSettingsFile(sourcePath, destPath, templateConfig);
187
- console.log(chalk.green(`✓ Copied ${file.source} → ${file.destination} (with selected hooks)`));
352
+ await processSettingsFileFromContent(settingsContent, destPath, templateConfig);
353
+ console.log(chalk.green(`✓ Downloaded ${file.source} → ${file.destination} (with selected hooks)`));
188
354
  }
189
355
  } else if (file.source.includes('.mcp.json') && templateConfig.selectedMCPs) {
356
+ // Download and process MCP config with selected MCPs
357
+ const mcpContent = await downloadFileFromGitHub(file.source);
358
+
190
359
  // In merge mode, merge MCP config instead of overwriting
191
360
  if (userAction === 'merge') {
192
- await mergeMCPFile(sourcePath, destPath, templateConfig);
361
+ await mergeMCPFileFromContent(mcpContent, destPath, templateConfig);
193
362
  console.log(chalk.green(`✓ Merged ${file.source} → ${file.destination} (with selected MCPs)`));
194
363
  } else {
195
- await processMCPFile(sourcePath, destPath, templateConfig);
196
- console.log(chalk.green(`✓ Copied ${file.source} → ${file.destination} (with selected MCPs)`));
364
+ await processMCPFileFromContent(mcpContent, destPath, templateConfig);
365
+ console.log(chalk.green(`✓ Downloaded ${file.source} → ${file.destination} (with selected MCPs)`));
197
366
  }
198
367
  } else {
199
- // Copy regular files (CLAUDE.md, etc.)
368
+ // Download regular files (CLAUDE.md, etc.)
200
369
  // In merge mode, skip if file already exists
201
370
  if (userAction === 'merge' && await fs.pathExists(destPath)) {
202
371
  console.log(chalk.blue(`⏭️ Skipped ${file.destination} (already exists)`));
203
372
  continue;
204
373
  }
205
374
 
206
- await fs.copy(sourcePath, destPath, {
207
- overwrite: shouldOverwrite,
208
- filter: (src) => {
209
- // Skip commands directory during regular copy - we handle them above
210
- return !src.includes('.claude/commands');
211
- }
212
- });
213
- console.log(chalk.green(`✓ Copied ${file.source} → ${file.destination}`));
375
+ const fileContent = await downloadFileFromGitHub(file.source);
376
+ const destDir = path.dirname(destPath);
377
+ await fs.ensureDir(destDir);
378
+ await fs.writeFile(destPath, fileContent, 'utf8');
379
+ console.log(chalk.green(`✓ Downloaded ${file.source} → ${file.destination}`));
214
380
  }
215
381
  } catch (error) {
216
382
  console.error(chalk.red(`✗ Failed to copy ${file.source}:`), error.message);
@@ -218,38 +384,8 @@ async function copyTemplateFiles(templateConfig, targetDir, options = {}) {
218
384
  }
219
385
  }
220
386
 
387
+ console.log(chalk.cyan(`📦 All templates downloaded from: https://github.com/${GITHUB_CONFIG.owner}/${GITHUB_CONFIG.repo}/tree/${GITHUB_CONFIG.branch}/${GITHUB_CONFIG.templatesPath}`));
221
388
  return true; // Indicate successful completion
222
-
223
- // Copy selected commands individually
224
- if (templateConfig.selectedCommands && templateConfig.selectedCommands.length > 0) {
225
- const commandsDir = path.join(targetDir, '.claude', 'commands');
226
- await fs.ensureDir(commandsDir);
227
-
228
- for (const command of templateConfig.selectedCommands) {
229
- try {
230
- const commandFileName = `${command.name}.md`;
231
- const destPath = path.join(commandsDir, commandFileName);
232
-
233
- await fs.copy(command.filePath, destPath);
234
- console.log(chalk.green(`✓ Added command: ${command.displayName}`));
235
- } catch (error) {
236
- console.error(chalk.red(`✗ Failed to copy command ${command.name}:`), error.message);
237
- // Don't throw - continue with other commands
238
- }
239
- }
240
-
241
- console.log(chalk.cyan(`📋 Installed ${templateConfig.selectedCommands.length} commands`));
242
- }
243
-
244
- // Report hook selection
245
- if (templateConfig.selectedHooks && templateConfig.selectedHooks.length > 0) {
246
- console.log(chalk.magenta(`🔧 Installed ${templateConfig.selectedHooks.length} automation hooks`));
247
- }
248
-
249
- // Report MCP selection
250
- if (templateConfig.selectedMCPs && templateConfig.selectedMCPs.length > 0) {
251
- console.log(chalk.blue(`🔧 Installed ${templateConfig.selectedMCPs.length} MCP`));
252
- }
253
389
  }
254
390
 
255
391
  async function runPostInstallationValidation(targetDir, templateConfig) {
package/src/index.js CHANGED
@@ -175,8 +175,8 @@ async function createClaudeConfig(options = {}) {
175
175
 
176
176
  let config;
177
177
  if (options.yes) {
178
- // Use defaults
179
- const selectedLanguage = options.language || projectInfo.detectedLanguage || 'common';
178
+ // Use defaults - prioritize --template over --language for backward compatibility
179
+ const selectedLanguage = options.template || options.language || projectInfo.detectedLanguage || 'common';
180
180
 
181
181
  // Check if selected language is coming soon
182
182
  if (selectedLanguage && TEMPLATES_CONFIG[selectedLanguage] && TEMPLATES_CONFIG[selectedLanguage].comingSoon) {
@@ -305,53 +305,39 @@ async function installIndividualAgent(agentName, targetDir, options) {
305
305
  console.log(chalk.blue(`🤖 Installing agent: ${agentName}`));
306
306
 
307
307
  try {
308
- // Check if components directory exists
309
- const componentsPath = path.join(__dirname, '..', 'components', 'agents');
310
- const agentFile = path.join(componentsPath, `${agentName}.md`);
308
+ // Download agent directly from GitHub
309
+ const githubUrl = `https://raw.githubusercontent.com/davila7/claude-code-templates/main/cli-tool/components/agents/${agentName}.md`;
310
+ console.log(chalk.gray(`📥 Downloading from GitHub (main branch)...`));
311
311
 
312
- if (!await fs.pathExists(agentFile)) {
313
- console.log(chalk.red(`❌ Agent "${agentName}" not found`));
314
- console.log(chalk.yellow('Available agents:'));
315
-
316
- // List available agents
317
- if (await fs.pathExists(componentsPath)) {
318
- const agents = await fs.readdir(componentsPath);
319
- agents.filter(f => f.endsWith('.md')).forEach(agent => {
320
- console.log(chalk.cyan(` - ${agent.replace('.md', '')}`));
321
- });
312
+ const response = await fetch(githubUrl);
313
+ if (!response.ok) {
314
+ if (response.status === 404) {
315
+ console.log(chalk.red(`❌ Agent "${agentName}" not found`));
316
+ console.log(chalk.yellow('Available agents: api-security-audit, database-optimization, react-performance-optimization'));
317
+ return;
322
318
  }
323
- return;
319
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
324
320
  }
325
321
 
326
- // For agents, they are typically part of templates, so we need to determine
327
- // the appropriate language/framework and install the complete template
328
- const agentContent = await fs.readFile(agentFile, 'utf8');
329
- const language = extractLanguageFromAgent(agentContent, agentName);
330
- const framework = extractFrameworkFromAgent(agentContent, agentName);
322
+ const agentContent = await response.text();
331
323
 
332
- console.log(chalk.yellow(`📝 Agent "${agentName}" is part of ${language}/${framework} template`));
333
- console.log(chalk.blue('🚀 Installing complete template with this agent...'));
324
+ // Create .claude/agents directory if it doesn't exist
325
+ const agentsDir = path.join(targetDir, '.claude', 'agents');
326
+ await fs.ensureDir(agentsDir);
334
327
 
335
- // Install the template that contains this agent (avoid recursion)
336
- const setupOptions = {
337
- ...options,
338
- language,
339
- framework,
340
- yes: true,
341
- targetDirectory: targetDir,
342
- agent: null // Remove agent to avoid recursion
343
- };
344
- delete setupOptions.agent;
345
- await createClaudeConfig(setupOptions);
328
+ // Write the agent file
329
+ const targetFile = path.join(agentsDir, `${agentName}.md`);
330
+ await fs.writeFile(targetFile, agentContent, 'utf8');
346
331
 
347
332
  console.log(chalk.green(`✅ Agent "${agentName}" installed successfully!`));
333
+ console.log(chalk.cyan(`📁 Installed to: ${path.relative(targetDir, targetFile)}`));
334
+ console.log(chalk.cyan(`📦 Downloaded from: ${githubUrl}`));
348
335
 
349
336
  // Track successful agent installation
350
337
  trackingService.trackDownload('agent', agentName, {
351
- language: language,
352
- framework: framework,
353
- installation_type: 'individual_agent',
354
- template_installed: true
338
+ installation_type: 'individual_component',
339
+ target_directory: path.relative(process.cwd(), targetDir),
340
+ source: 'github_main'
355
341
  });
356
342
 
357
343
  } catch (error) {
@@ -363,39 +349,39 @@ async function installIndividualCommand(commandName, targetDir, options) {
363
349
  console.log(chalk.blue(`⚡ Installing command: ${commandName}`));
364
350
 
365
351
  try {
366
- // Check if components directory exists
367
- const componentsPath = path.join(__dirname, '..', 'components', 'commands');
368
- const commandFile = path.join(componentsPath, `${commandName}.md`);
352
+ // Download command directly from GitHub
353
+ const githubUrl = `https://raw.githubusercontent.com/davila7/claude-code-templates/main/cli-tool/components/commands/${commandName}.md`;
354
+ console.log(chalk.gray(`📥 Downloading from GitHub (main branch)...`));
369
355
 
370
- if (!await fs.pathExists(commandFile)) {
371
- console.log(chalk.red(`❌ Command "${commandName}" not found`));
372
- console.log(chalk.yellow('Available commands:'));
373
-
374
- // List available commands
375
- if (await fs.pathExists(componentsPath)) {
376
- const commands = await fs.readdir(componentsPath);
377
- commands.filter(f => f.endsWith('.md')).forEach(command => {
378
- console.log(chalk.cyan(` - ${command.replace('.md', '')}`));
379
- });
356
+ const response = await fetch(githubUrl);
357
+ if (!response.ok) {
358
+ if (response.status === 404) {
359
+ console.log(chalk.red(`❌ Command "${commandName}" not found`));
360
+ console.log(chalk.yellow('Available commands: check-file, generate-tests'));
361
+ return;
380
362
  }
381
- return;
363
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
382
364
  }
383
365
 
366
+ const commandContent = await response.text();
367
+
384
368
  // Create .claude/commands directory if it doesn't exist
385
369
  const commandsDir = path.join(targetDir, '.claude', 'commands');
386
370
  await fs.ensureDir(commandsDir);
387
371
 
388
- // Copy the command file
372
+ // Write the command file
389
373
  const targetFile = path.join(commandsDir, `${commandName}.md`);
390
- await fs.copy(commandFile, targetFile);
374
+ await fs.writeFile(targetFile, commandContent, 'utf8');
391
375
 
392
376
  console.log(chalk.green(`✅ Command "${commandName}" installed successfully!`));
393
377
  console.log(chalk.cyan(`📁 Installed to: ${path.relative(targetDir, targetFile)}`));
378
+ console.log(chalk.cyan(`📦 Downloaded from: ${githubUrl}`));
394
379
 
395
380
  // Track successful command installation
396
381
  trackingService.trackDownload('command', commandName, {
397
382
  installation_type: 'individual_command',
398
- target_directory: path.relative(process.cwd(), targetDir)
383
+ target_directory: path.relative(process.cwd(), targetDir),
384
+ source: 'github_main'
399
385
  });
400
386
 
401
387
  } catch (error) {
@@ -407,26 +393,22 @@ async function installIndividualMCP(mcpName, targetDir, options) {
407
393
  console.log(chalk.blue(`🔌 Installing MCP: ${mcpName}`));
408
394
 
409
395
  try {
410
- // Check if components directory exists
411
- const componentsPath = path.join(__dirname, '..', 'components', 'mcps');
412
- const mcpFile = path.join(componentsPath, `${mcpName}.json`);
396
+ // Download MCP directly from GitHub
397
+ const githubUrl = `https://raw.githubusercontent.com/davila7/claude-code-templates/main/cli-tool/components/mcps/${mcpName}.json`;
398
+ console.log(chalk.gray(`📥 Downloading from GitHub (main branch)...`));
413
399
 
414
- if (!await fs.pathExists(mcpFile)) {
415
- console.log(chalk.red(`❌ MCP "${mcpName}" not found`));
416
- console.log(chalk.yellow('Available MCPs:'));
417
-
418
- // List available MCPs
419
- if (await fs.pathExists(componentsPath)) {
420
- const mcps = await fs.readdir(componentsPath);
421
- mcps.filter(f => f.endsWith('.json')).forEach(mcp => {
422
- console.log(chalk.cyan(` - ${mcp.replace('.json', '')}`));
423
- });
400
+ const response = await fetch(githubUrl);
401
+ if (!response.ok) {
402
+ if (response.status === 404) {
403
+ console.log(chalk.red(`❌ MCP "${mcpName}" not found`));
404
+ console.log(chalk.yellow('Available MCPs: web-fetch, filesystem-access, github-integration, memory-integration, mysql-integration, postgresql-integration, deepgraph-react, deepgraph-nextjs, deepgraph-typescript, deepgraph-vue'));
405
+ return;
424
406
  }
425
- return;
407
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
426
408
  }
427
409
 
428
- // Read the MCP configuration
429
- const mcpConfig = await fs.readJson(mcpFile);
410
+ const mcpConfigText = await response.text();
411
+ const mcpConfig = JSON.parse(mcpConfigText);
430
412
 
431
413
  // Check if .mcp.json exists in target directory
432
414
  const targetMcpFile = path.join(targetDir, '.mcp.json');
@@ -448,12 +430,14 @@ async function installIndividualMCP(mcpName, targetDir, options) {
448
430
 
449
431
  console.log(chalk.green(`✅ MCP "${mcpName}" installed successfully!`));
450
432
  console.log(chalk.cyan(`📁 Configuration merged into: ${path.relative(targetDir, targetMcpFile)}`));
433
+ console.log(chalk.cyan(`📦 Downloaded from: ${githubUrl}`));
451
434
 
452
435
  // Track successful MCP installation
453
436
  trackingService.trackDownload('mcp', mcpName, {
454
437
  installation_type: 'individual_mcp',
455
438
  merged_with_existing: existingConfig !== null,
456
- servers_count: Object.keys(mergedConfig.mcpServers || {}).length
439
+ servers_count: Object.keys(mergedConfig.mcpServers || {}).length,
440
+ source: 'github_main'
457
441
  });
458
442
 
459
443
  } catch (error) {
@@ -1,92 +0,0 @@
1
- ---
2
- name: api-security-audit
3
- description: Use this agent when conducting security audits for REST APIs. Specializes in authentication vulnerabilities, authorization flaws, injection attacks, data exposure, and API security best practices. Examples: <example>Context: User needs to audit API security. user: 'I need to review my API endpoints for security vulnerabilities' assistant: 'I'll use the api-security-audit agent to perform a comprehensive security audit of your API endpoints' <commentary>Since the user needs API security assessment, use the api-security-audit agent for vulnerability analysis.</commentary></example> <example>Context: User has authentication issues. user: 'My API authentication seems vulnerable to attacks' assistant: 'Let me use the api-security-audit agent to analyze your authentication implementation and identify security weaknesses' <commentary>The user has specific authentication security concerns, so use the api-security-audit agent.</commentary></example>
4
- color: red
5
- ---
6
-
7
- You are an API Security Audit specialist focusing on identifying, analyzing, and resolving security vulnerabilities in REST APIs. Your expertise covers authentication, authorization, data protection, and compliance with security standards.
8
-
9
- Your core expertise areas:
10
- - **Authentication Security**: JWT vulnerabilities, token management, session security
11
- - **Authorization Flaws**: RBAC issues, privilege escalation, access control bypasses
12
- - **Injection Attacks**: SQL injection, NoSQL injection, command injection prevention
13
- - **Data Protection**: Sensitive data exposure, encryption, secure transmission
14
- - **API Security Standards**: OWASP API Top 10, security headers, rate limiting
15
- - **Compliance**: GDPR, HIPAA, PCI DSS requirements for APIs
16
-
17
- ## When to Use This Agent
18
-
19
- Use this agent for:
20
- - Comprehensive API security audits
21
- - Authentication and authorization reviews
22
- - Vulnerability assessments and penetration testing
23
- - Security compliance validation
24
- - Incident response and remediation
25
- - Security architecture reviews
26
-
27
- ## Security Audit Checklist
28
-
29
- ### Authentication & Authorization
30
- ```javascript
31
- // Secure JWT implementation
32
- const jwt = require('jsonwebtoken');
33
- const bcrypt = require('bcrypt');
34
-
35
- class AuthService {
36
- generateToken(user) {
37
- return jwt.sign(
38
- {
39
- userId: user.id,
40
- role: user.role,
41
- permissions: user.permissions
42
- },
43
- process.env.JWT_SECRET,
44
- {
45
- expiresIn: '15m',
46
- issuer: 'your-api',
47
- audience: 'your-app'
48
- }
49
- );
50
- }
51
-
52
- verifyToken(token) {
53
- try {
54
- return jwt.verify(token, process.env.JWT_SECRET, {
55
- issuer: 'your-api',
56
- audience: 'your-app'
57
- });
58
- } catch (error) {
59
- throw new Error('Invalid token');
60
- }
61
- }
62
-
63
- async hashPassword(password) {
64
- const saltRounds = 12;
65
- return await bcrypt.hash(password, saltRounds);
66
- }
67
- }
68
- ```
69
-
70
- ### Input Validation & Sanitization
71
- ```javascript
72
- const { body, validationResult } = require('express-validator');
73
-
74
- const validateUserInput = [
75
- body('email').isEmail().normalizeEmail(),
76
- body('password').isLength({ min: 8 }).matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])/),
77
- body('name').trim().escape().isLength({ min: 1, max: 100 }),
78
-
79
- (req, res, next) => {
80
- const errors = validationResult(req);
81
- if (!errors.isEmpty()) {
82
- return res.status(400).json({
83
- error: 'Validation failed',
84
- details: errors.array()
85
- });
86
- }
87
- next();
88
- }
89
- ];
90
- ```
91
-
92
- Always provide specific, actionable security recommendations with code examples and remediation steps when conducting API security audits.
@@ -1,94 +0,0 @@
1
- ---
2
- name: database-optimization
3
- description: Use this agent when dealing with database performance issues. Specializes in query optimization, indexing strategies, schema design, connection pooling, and database monitoring. Examples: <example>Context: User has slow database queries. user: 'My database queries are taking too long to execute' assistant: 'I'll use the database-optimization agent to analyze and optimize your slow database queries' <commentary>Since the user has database performance issues, use the database-optimization agent for query analysis and optimization.</commentary></example> <example>Context: User needs indexing strategy. user: 'I need help designing indexes for better database performance' assistant: 'Let me use the database-optimization agent to design an optimal indexing strategy for your database schema' <commentary>The user needs indexing help, so use the database-optimization agent.</commentary></example>
4
- color: blue
5
- ---
6
-
7
- You are a Database Optimization specialist focusing on improving database performance, query efficiency, and overall data access patterns. Your expertise covers SQL optimization, NoSQL performance tuning, and database architecture best practices.
8
-
9
- Your core expertise areas:
10
- - **Query Optimization**: SQL query tuning, execution plan analysis, join optimization
11
- - **Indexing Strategies**: B-tree, hash, composite indexes, covering indexes
12
- - **Schema Design**: Normalization, denormalization, partitioning strategies
13
- - **Connection Management**: Connection pooling, transaction optimization
14
- - **Performance Monitoring**: Query profiling, slow query analysis, metrics tracking
15
- - **Database Architecture**: Replication, sharding, caching strategies
16
-
17
- ## When to Use This Agent
18
-
19
- Use this agent for:
20
- - Slow query identification and optimization
21
- - Database schema design and review
22
- - Index strategy development
23
- - Performance bottleneck analysis
24
- - Connection pool configuration
25
- - Database monitoring setup
26
-
27
- ## Optimization Strategies
28
-
29
- ### Query Optimization Examples
30
- ```sql
31
- -- Before: Inefficient query with N+1 problem
32
- SELECT * FROM users WHERE id IN (
33
- SELECT user_id FROM orders WHERE status = 'pending'
34
- );
35
-
36
- -- After: Optimized with proper JOIN
37
- SELECT DISTINCT u.*
38
- FROM users u
39
- INNER JOIN orders o ON u.id = o.user_id
40
- WHERE o.status = 'pending'
41
- AND o.created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY);
42
-
43
- -- Add covering index for this query
44
- CREATE INDEX idx_orders_status_created_userid
45
- ON orders (status, created_at, user_id);
46
- ```
47
-
48
- ### Connection Pool Configuration
49
- ```javascript
50
- // Optimized connection pool setup
51
- const mysql = require('mysql2/promise');
52
-
53
- const pool = mysql.createPool({
54
- host: process.env.DB_HOST,
55
- user: process.env.DB_USER,
56
- password: process.env.DB_PASSWORD,
57
- database: process.env.DB_NAME,
58
- waitForConnections: true,
59
- connectionLimit: 10, // Adjust based on server capacity
60
- queueLimit: 0,
61
- acquireTimeout: 60000,
62
- timeout: 60000,
63
- reconnect: true,
64
- // Enable prepared statements for better performance
65
- namedPlaceholders: true
66
- });
67
-
68
- // Proper transaction handling
69
- async function transferFunds(fromAccount, toAccount, amount) {
70
- const connection = await pool.getConnection();
71
- try {
72
- await connection.beginTransaction();
73
-
74
- await connection.execute(
75
- 'UPDATE accounts SET balance = balance - ? WHERE id = ? AND balance >= ?',
76
- [amount, fromAccount, amount]
77
- );
78
-
79
- await connection.execute(
80
- 'UPDATE accounts SET balance = balance + ? WHERE id = ?',
81
- [amount, toAccount]
82
- );
83
-
84
- await connection.commit();
85
- } catch (error) {
86
- await connection.rollback();
87
- throw error;
88
- } finally {
89
- connection.release();
90
- }
91
- }
92
- ```
93
-
94
- Always provide specific performance improvements with measurable metrics and explain the reasoning behind optimization recommendations.
@@ -1,64 +0,0 @@
1
- ---
2
- name: react-performance-optimization
3
- description: Use this agent when dealing with React performance issues. Specializes in identifying and fixing performance bottlenecks, bundle optimization, rendering optimization, and memory leaks. Examples: <example>Context: User has slow React application. user: 'My React app is loading slowly and feels sluggish during interactions' assistant: 'I'll use the react-performance-optimization agent to help identify and fix the performance bottlenecks in your React application' <commentary>Since the user has React performance issues, use the react-performance-optimization agent for performance analysis and optimization.</commentary></example> <example>Context: User needs help with bundle size optimization. user: 'My React app bundle is too large and taking too long to load' assistant: 'Let me use the react-performance-optimization agent to help optimize your bundle size and improve loading performance' <commentary>The user needs bundle optimization help, so use the react-performance-optimization agent.</commentary></example>
4
- color: red
5
- ---
6
-
7
- You are a React Performance Optimization specialist focusing on identifying, analyzing, and resolving performance bottlenecks in React applications. Your expertise covers rendering optimization, bundle analysis, memory management, and Core Web Vitals.
8
-
9
- Your core expertise areas:
10
- - **Rendering Performance**: Component re-renders, reconciliation optimization
11
- - **Bundle Optimization**: Code splitting, tree shaking, dynamic imports
12
- - **Memory Management**: Memory leaks, cleanup patterns, resource management
13
- - **Network Performance**: Lazy loading, prefetching, caching strategies
14
- - **Core Web Vitals**: LCP, FID, CLS optimization for React apps
15
- - **Profiling Tools**: React DevTools Profiler, Chrome DevTools, Lighthouse
16
-
17
- ## When to Use This Agent
18
-
19
- Use this agent for:
20
- - Slow loading React applications
21
- - Janky or unresponsive user interactions
22
- - Large bundle sizes affecting load times
23
- - Memory leaks or excessive memory usage
24
- - Poor Core Web Vitals scores
25
- - Performance regression analysis
26
-
27
- ## Performance Optimization Strategies
28
-
29
- ### React.memo for Component Memoization
30
- ```javascript
31
- const ExpensiveComponent = React.memo(({ data, onUpdate }) => {
32
- const processedData = useMemo(() => {
33
- return data.map(item => ({
34
- ...item,
35
- computed: heavyComputation(item)
36
- }));
37
- }, [data]);
38
-
39
- return (
40
- <div>
41
- {processedData.map(item => (
42
- <Item key={item.id} item={item} onUpdate={onUpdate} />
43
- ))}
44
- </div>
45
- );
46
- });
47
- ```
48
-
49
- ### Code Splitting with React.lazy
50
- ```javascript
51
- const Dashboard = lazy(() => import('./pages/Dashboard'));
52
-
53
- const App = () => (
54
- <Router>
55
- <Suspense fallback={<LoadingSpinner />}>
56
- <Routes>
57
- <Route path="/dashboard" element={<Dashboard />} />
58
- </Routes>
59
- </Suspense>
60
- </Router>
61
- );
62
- ```
63
-
64
- Always provide specific, measurable solutions with before/after performance comparisons when helping with React performance optimization.
@@ -1,53 +0,0 @@
1
- # File Analysis Tool
2
-
3
- Perform comprehensive analysis of $ARGUMENTS to identify code quality issues, security vulnerabilities, and optimization opportunities.
4
-
5
- ## Task
6
-
7
- I'll analyze the specified file and provide detailed insights on:
8
-
9
- 1. Code quality metrics and maintainability
10
- 2. Security vulnerabilities and best practices
11
- 3. Performance bottlenecks and optimization opportunities
12
- 4. Dependency usage and potential issues
13
- 5. TypeScript/JavaScript specific patterns and improvements
14
- 6. Test coverage and missing tests
15
-
16
- ## Process
17
-
18
- I'll follow these steps:
19
-
20
- 1. Read and parse the target file
21
- 2. Analyze code structure and complexity
22
- 3. Check for security vulnerabilities and anti-patterns
23
- 4. Evaluate performance implications
24
- 5. Review dependency usage and imports
25
- 6. Provide actionable recommendations for improvement
26
-
27
- ## Analysis Areas
28
-
29
- ### Code Quality
30
- - Cyclomatic complexity and maintainability metrics
31
- - Code duplication and refactoring opportunities
32
- - Naming conventions and code organization
33
- - TypeScript type safety and best practices
34
-
35
- ### Security Assessment
36
- - Input validation and sanitization
37
- - Authentication and authorization patterns
38
- - Sensitive data exposure risks
39
- - Common vulnerability patterns (XSS, injection, etc.)
40
-
41
- ### Performance Review
42
- - Bundle size impact and optimization opportunities
43
- - Runtime performance bottlenecks
44
- - Memory usage patterns
45
- - Lazy loading and code splitting opportunities
46
-
47
- ### Best Practices
48
- - Framework-specific patterns (React, Vue, Angular)
49
- - Modern JavaScript/TypeScript features usage
50
- - Error handling and logging practices
51
- - Testing patterns and coverage gaps
52
-
53
- I'll provide specific, actionable recommendations tailored to your project's technology stack and architecture.
@@ -1,68 +0,0 @@
1
- # Test Generator
2
-
3
- Generate comprehensive test suite for $ARGUMENTS following project testing conventions and best practices.
4
-
5
- ## Task
6
-
7
- I'll analyze the target code and create complete test coverage including:
8
-
9
- 1. Unit tests for individual functions and methods
10
- 2. Integration tests for component interactions
11
- 3. Edge case and error handling tests
12
- 4. Mock implementations for external dependencies
13
- 5. Test utilities and helpers as needed
14
- 6. Performance and snapshot tests where appropriate
15
-
16
- ## Process
17
-
18
- I'll follow these steps:
19
-
20
- 1. Analyze the target file/component structure
21
- 2. Identify all testable functions, methods, and behaviors
22
- 3. Examine existing test patterns in the project
23
- 4. Create test files following project naming conventions
24
- 5. Implement comprehensive test cases with proper setup/teardown
25
- 6. Add necessary mocks and test utilities
26
- 7. Verify test coverage and add missing test cases
27
-
28
- ## Test Types
29
-
30
- ### Unit Tests
31
- - Individual function testing with various inputs
32
- - Component rendering and prop handling
33
- - State management and lifecycle methods
34
- - Utility function edge cases and error conditions
35
-
36
- ### Integration Tests
37
- - Component interaction testing
38
- - API integration with mocked responses
39
- - Service layer integration
40
- - End-to-end user workflows
41
-
42
- ### Framework-Specific Tests
43
- - **React**: Component testing with React Testing Library
44
- - **Vue**: Component testing with Vue Test Utils
45
- - **Angular**: Component and service testing with TestBed
46
- - **Node.js**: API endpoint and middleware testing
47
-
48
- ## Testing Best Practices
49
-
50
- ### Test Structure
51
- - Use descriptive test names that explain the behavior
52
- - Follow AAA pattern (Arrange, Act, Assert)
53
- - Group related tests with describe blocks
54
- - Use proper setup and teardown for test isolation
55
-
56
- ### Mock Strategy
57
- - Mock external dependencies and API calls
58
- - Use factories for test data generation
59
- - Implement proper cleanup for async operations
60
- - Mock timers and dates for deterministic tests
61
-
62
- ### Coverage Goals
63
- - Aim for 80%+ code coverage
64
- - Focus on critical business logic paths
65
- - Test both happy path and error scenarios
66
- - Include boundary value testing
67
-
68
- I'll adapt to your project's testing framework (Jest, Vitest, Cypress, etc.) and follow established patterns.
@@ -1,12 +0,0 @@
1
- {
2
- "mcpServers": {
3
- "DeepGraph Next.js MCP": {
4
- "command": "npx",
5
- "args": [
6
- "-y",
7
- "mcp-code-graph@latest",
8
- "vercel/next.js"
9
- ]
10
- }
11
- }
12
- }
@@ -1,12 +0,0 @@
1
- {
2
- "mcpServers": {
3
- "DeepGraph React MCP": {
4
- "command": "npx",
5
- "args": [
6
- "-y",
7
- "mcp-code-graph@latest",
8
- "facebook/react"
9
- ]
10
- }
11
- }
12
- }
@@ -1,12 +0,0 @@
1
- {
2
- "mcpServers": {
3
- "DeepGraph TypeScript MCP": {
4
- "command": "npx",
5
- "args": [
6
- "-y",
7
- "mcp-code-graph@latest",
8
- "microsoft/TypeScript"
9
- ]
10
- }
11
- }
12
- }
@@ -1,12 +0,0 @@
1
- {
2
- "mcpServers": {
3
- "DeepGraph Vue MCP": {
4
- "command": "npx",
5
- "args": [
6
- "-y",
7
- "mcp-code-graph@latest",
8
- "vuejs/core"
9
- ]
10
- }
11
- }
12
- }
@@ -1,12 +0,0 @@
1
- {
2
- "mcpServers": {
3
- "filesystem": {
4
- "command": "npx",
5
- "args": [
6
- "-y",
7
- "@modelcontextprotocol/server-filesystem",
8
- "/path/to/allowed/files"
9
- ]
10
- }
11
- }
12
- }
@@ -1,11 +0,0 @@
1
- {
2
- "mcpServers": {
3
- "github": {
4
- "command": "npx",
5
- "args": ["-y", "@modelcontextprotocol/server-github"],
6
- "env": {
7
- "GITHUB_PERSONAL_ACCESS_TOKEN": "<YOUR_TOKEN>"
8
- }
9
- }
10
- }
11
- }
@@ -1,8 +0,0 @@
1
- {
2
- "mcpServers": {
3
- "memory": {
4
- "command": "npx",
5
- "args": ["-y", "@modelcontextprotocol/server-memory"]
6
- }
7
- }
8
- }
@@ -1,11 +0,0 @@
1
- {
2
- "mcpServers": {
3
- "mysql": {
4
- "command": "uvx",
5
- "args": ["mcp-server-mysql"],
6
- "env": {
7
- "MYSQL_CONNECTION_STRING": "mysql://user:password@localhost:3306/dbname"
8
- }
9
- }
10
- }
11
- }
@@ -1,11 +0,0 @@
1
- {
2
- "mcpServers": {
3
- "postgresql": {
4
- "command": "npx",
5
- "args": ["-y", "@modelcontextprotocol/server-postgres"],
6
- "env": {
7
- "POSTGRES_CONNECTION_STRING": "postgresql://user:password@localhost:5432/dbname"
8
- }
9
- }
10
- }
11
- }
@@ -1,8 +0,0 @@
1
- {
2
- "mcpServers": {
3
- "fetch": {
4
- "command": "npx",
5
- "args": ["-y", "@modelcontextprotocol/server-fetch"]
6
- }
7
- }
8
- }