claude-code-templates 1.2.1 โ†’ 1.3.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.
Files changed (43) hide show
  1. package/package.json +10 -8
  2. package/src/command-scanner.js +1 -1
  3. package/src/file-operations.js +44 -3
  4. package/src/hook-scanner.js +106 -2
  5. package/src/index.js +23 -3
  6. package/src/prompts.js +68 -2
  7. package/src/templates.js +31 -8
  8. package/scripts/sync-templates.js +0 -182
  9. package/templates/common/.claude/commands/git-workflow.md +0 -239
  10. package/templates/common/.claude/commands/project-setup.md +0 -316
  11. package/templates/common/CLAUDE.md +0 -109
  12. package/templates/common/README.md +0 -96
  13. package/templates/go/README.md +0 -25
  14. package/templates/javascript-typescript/.claude/commands/api-endpoint.md +0 -51
  15. package/templates/javascript-typescript/.claude/commands/debug.md +0 -52
  16. package/templates/javascript-typescript/.claude/commands/lint.md +0 -48
  17. package/templates/javascript-typescript/.claude/commands/npm-scripts.md +0 -48
  18. package/templates/javascript-typescript/.claude/commands/refactor.md +0 -55
  19. package/templates/javascript-typescript/.claude/commands/test.md +0 -61
  20. package/templates/javascript-typescript/.claude/commands/typescript-migrate.md +0 -51
  21. package/templates/javascript-typescript/.claude/settings.json +0 -142
  22. package/templates/javascript-typescript/.mcp.json +0 -13
  23. package/templates/javascript-typescript/CLAUDE.md +0 -185
  24. package/templates/javascript-typescript/README.md +0 -259
  25. package/templates/javascript-typescript/examples/angular-app/.claude/commands/components.md +0 -63
  26. package/templates/javascript-typescript/examples/angular-app/.claude/commands/services.md +0 -62
  27. package/templates/javascript-typescript/examples/node-api/.claude/commands/api-endpoint.md +0 -46
  28. package/templates/javascript-typescript/examples/node-api/.claude/commands/database.md +0 -56
  29. package/templates/javascript-typescript/examples/node-api/.claude/commands/middleware.md +0 -61
  30. package/templates/javascript-typescript/examples/node-api/.claude/commands/route.md +0 -57
  31. package/templates/javascript-typescript/examples/node-api/CLAUDE.md +0 -102
  32. package/templates/javascript-typescript/examples/react-app/.claude/commands/component.md +0 -29
  33. package/templates/javascript-typescript/examples/react-app/.claude/commands/hooks.md +0 -44
  34. package/templates/javascript-typescript/examples/react-app/.claude/commands/state-management.md +0 -45
  35. package/templates/javascript-typescript/examples/react-app/CLAUDE.md +0 -81
  36. package/templates/javascript-typescript/examples/vue-app/.claude/commands/components.md +0 -46
  37. package/templates/javascript-typescript/examples/vue-app/.claude/commands/composables.md +0 -51
  38. package/templates/python/.claude/commands/django-model.md +0 -124
  39. package/templates/python/.claude/commands/flask-route.md +0 -217
  40. package/templates/python/.claude/commands/lint.md +0 -111
  41. package/templates/python/.claude/commands/test.md +0 -73
  42. package/templates/python/CLAUDE.md +0 -276
  43. 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.2.1",
4
- "description": "CLI tool to setup Claude Code configurations with selective automation hooks for different programming languages",
3
+ "version": "1.3.1",
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
- "sync": "node scripts/sync-templates.js",
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
  }
@@ -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, '..', 'templates');
10
+ const templatesDir = path.join(__dirname, '../../');
11
11
  const languageDir = path.join(templatesDir, language);
12
12
 
13
13
  // Check if language directory exists
@@ -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, '../templates');
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
  };
@@ -267,7 +267,7 @@ function getHookDescription(hook, matcher, type) {
267
267
  * @returns {Array} Array of available hooks for the language
268
268
  */
269
269
  function getHooksForLanguage(language) {
270
- const templateDir = path.join(__dirname, '..', 'templates', language);
270
+ const templateDir = path.join(__dirname, '../../', language);
271
271
  const settingsPath = path.join(templateDir, '.claude', 'settings.json');
272
272
 
273
273
  return getHooksFromSettings(settingsPath);
@@ -336,9 +336,113 @@ function filterHooksBySelection(originalSettings, selectedHookIds, availableHook
336
336
  return filteredSettings;
337
337
  }
338
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
+
339
440
  module.exports = {
340
441
  getHooksFromSettings,
341
442
  getHooksForLanguage,
342
443
  filterHooksBySelection,
343
- getHookDescription
444
+ getHookDescription,
445
+ getMCPsFromFile,
446
+ getMCPsForLanguage,
447
+ filterMCPsBySelection
344
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;
package/src/templates.js CHANGED
@@ -7,7 +7,8 @@ const TEMPLATES_CONFIG = {
7
7
  description: 'Universal configuration for any project',
8
8
  files: [
9
9
  { source: 'common/CLAUDE.md', destination: 'CLAUDE.md' },
10
- { source: 'common/.claude', destination: '.claude' }
10
+ { source: 'common/.claude', destination: '.claude' },
11
+ { source: 'common/.mcp.json', destination: '.mcp.json' }
11
12
  ]
12
13
  },
13
14
  'javascript-typescript': {
@@ -50,23 +51,45 @@ const TEMPLATES_CONFIG = {
50
51
  description: 'Optimized for Python development',
51
52
  files: [
52
53
  { source: 'python/CLAUDE.md', destination: 'CLAUDE.md' },
53
- { source: 'python/.claude', destination: '.claude' }
54
- ]
54
+ { source: 'python/.claude', destination: '.claude' },
55
+ { source: 'python/.mcp.json', destination: '.mcp.json' }
56
+ ],
57
+ frameworks: {
58
+ 'django': {
59
+ name: 'Django',
60
+ additionalFiles: [
61
+ { source: 'python/examples/django-app/.claude/commands', destination: '.claude/commands' },
62
+ { source: 'python/examples/django-app/CLAUDE.md', destination: 'CLAUDE.md' }
63
+ ]
64
+ },
65
+ 'flask': {
66
+ name: 'Flask',
67
+ additionalFiles: [
68
+ { source: 'python/examples/flask-app/.claude/commands', destination: '.claude/commands' }
69
+ ]
70
+ },
71
+ 'fastapi': {
72
+ name: 'FastAPI',
73
+ additionalFiles: [
74
+ { source: 'python/examples/fastapi-app/.claude/commands', destination: '.claude/commands' }
75
+ ]
76
+ }
77
+ }
55
78
  },
56
79
  'rust': {
57
80
  name: 'Rust',
58
81
  description: 'Optimized for Rust development',
82
+ comingSoon: true,
59
83
  files: [
60
- { source: 'rust/CLAUDE.md', destination: 'CLAUDE.md' },
61
- { source: 'rust/.claude', destination: '.claude' }
84
+ { source: 'rust/.mcp.json', destination: '.mcp.json' }
62
85
  ]
63
86
  },
64
87
  'go': {
65
88
  name: 'Go',
66
- description: 'Optimized for Go development',
89
+ description: 'Optimized for Go development',
90
+ comingSoon: true,
67
91
  files: [
68
- { source: 'go/CLAUDE.md', destination: 'CLAUDE.md' },
69
- { source: 'go/.claude', destination: '.claude' }
92
+ { source: 'go/.mcp.json', destination: '.mcp.json' }
70
93
  ]
71
94
  }
72
95
  };
@@ -1,182 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const fs = require('fs-extra');
4
- const path = require('path');
5
- const chalk = require('chalk');
6
-
7
- /**
8
- * Script to synchronize templates from root folders
9
- * to cli-tool/templates/
10
- */
11
-
12
- async function syncTemplates() {
13
- console.log(chalk.blue('๐Ÿ”„ Synchronizing templates...'));
14
-
15
- const rootDir = path.join(__dirname, '..', '..');
16
- const templatesDir = path.join(__dirname, '..', 'templates');
17
-
18
- // Languages to synchronize
19
- const languages = ['common', 'javascript-typescript', 'python', 'rust', 'go'];
20
-
21
- let totalCopied = 0;
22
- let totalSkipped = 0;
23
-
24
- for (const language of languages) {
25
- const sourceDir = path.join(rootDir, language);
26
- const targetDir = path.join(templatesDir, language);
27
-
28
- if (!await fs.pathExists(sourceDir)) {
29
- console.log(chalk.yellow(`โš ๏ธ Source folder does not exist: ${language}`));
30
- continue;
31
- }
32
-
33
- console.log(chalk.cyan(`\n๐Ÿ“‚ Synchronizing ${language}...`));
34
-
35
- // Clean destination directory
36
- if (await fs.pathExists(targetDir)) {
37
- await fs.remove(targetDir);
38
- console.log(chalk.gray(` ๐Ÿ—‘๏ธ Previous directory removed`));
39
- }
40
-
41
- // Copy everything from source
42
- try {
43
- await fs.copy(sourceDir, targetDir, {
44
- filter: (src, dest) => {
45
- // Filter files we don't want to copy
46
- const relativePath = path.relative(sourceDir, src);
47
-
48
- // Exclude specific directories and files
49
- if (relativePath.includes('node_modules')) return false;
50
- if (relativePath.includes('.git')) return false;
51
- if (relativePath.includes('package-lock.json')) return false;
52
- if (relativePath.endsWith('.log')) return false;
53
-
54
- return true;
55
- }
56
- });
57
-
58
- // Count copied files
59
- const stats = await getDirectoryStats(targetDir);
60
- totalCopied += stats.files;
61
-
62
- console.log(chalk.green(` โœ… ${stats.files} files copied`));
63
-
64
- // Show copied structure
65
- if (stats.files > 0) {
66
- await showDirectoryStructure(targetDir, ' ');
67
- }
68
-
69
- } catch (error) {
70
- console.error(chalk.red(` โŒ Error copying ${language}:`), error.message);
71
- }
72
- }
73
-
74
- console.log(chalk.green(`\n๐ŸŽ‰ Synchronization completed!`));
75
- console.log(chalk.white(`๐Ÿ“Š Total synchronized files: ${totalCopied}`));
76
-
77
- // Verify that no hook files exist
78
- await cleanupOldReferences();
79
- }
80
-
81
- async function getDirectoryStats(dir) {
82
- let files = 0;
83
- let dirs = 0;
84
-
85
- if (!await fs.pathExists(dir)) {
86
- return { files: 0, dirs: 0 };
87
- }
88
-
89
- const items = await fs.readdir(dir);
90
-
91
- for (const item of items) {
92
- const itemPath = path.join(dir, item);
93
- const stat = await fs.stat(itemPath);
94
-
95
- if (stat.isDirectory()) {
96
- dirs++;
97
- const subStats = await getDirectoryStats(itemPath);
98
- files += subStats.files;
99
- dirs += subStats.dirs;
100
- } else {
101
- files++;
102
- }
103
- }
104
-
105
- return { files, dirs };
106
- }
107
-
108
- async function showDirectoryStructure(dir, prefix = '') {
109
- const items = await fs.readdir(dir);
110
-
111
- for (let i = 0; i < items.length; i++) {
112
- const item = items[i];
113
- const itemPath = path.join(dir, item);
114
- const stat = await fs.stat(itemPath);
115
- const isLast = i === items.length - 1;
116
- const connector = isLast ? 'โ””โ”€โ”€ ' : 'โ”œโ”€โ”€ ';
117
-
118
- if (stat.isDirectory()) {
119
- console.log(chalk.blue(`${prefix}${connector}${item}/`));
120
- if (item === '.claude' || item === 'commands') {
121
- // Show only one more level for .claude and commands
122
- const subItems = await fs.readdir(itemPath);
123
- const newPrefix = prefix + (isLast ? ' ' : 'โ”‚ ');
124
- for (let j = 0; j < Math.min(subItems.length, 3); j++) {
125
- const subItem = subItems[j];
126
- const subConnector = j === Math.min(subItems.length, 3) - 1 ? 'โ””โ”€โ”€ ' : 'โ”œโ”€โ”€ ';
127
- console.log(chalk.gray(`${newPrefix}${subConnector}${subItem}`));
128
- }
129
- if (subItems.length > 3) {
130
- console.log(chalk.gray(`${newPrefix}โ””โ”€โ”€ ... and ${subItems.length - 3} more`));
131
- }
132
- }
133
- } else {
134
- console.log(chalk.gray(`${prefix}${connector}${item}`));
135
- }
136
- }
137
- }
138
-
139
- async function cleanupOldReferences() {
140
- console.log(chalk.yellow('\n๐Ÿงน Cleaning up obsolete references...'));
141
-
142
- const templatesDir = path.join(__dirname, '..', 'templates');
143
-
144
- // Search and remove hooks directories
145
- const languages = ['javascript-typescript', 'python', 'common'];
146
-
147
- for (const language of languages) {
148
- const hooksDir = path.join(templatesDir, language, '.claude', 'hooks');
149
- if (await fs.pathExists(hooksDir)) {
150
- await fs.remove(hooksDir);
151
- console.log(chalk.yellow(` ๐Ÿ—‘๏ธ Removed: ${language}/.claude/hooks/`));
152
- }
153
- }
154
-
155
- // Check for empty files in commands
156
- for (const language of languages) {
157
- const commandsDir = path.join(templatesDir, language, '.claude', 'commands');
158
- if (await fs.pathExists(commandsDir)) {
159
- const files = await fs.readdir(commandsDir);
160
- for (const file of files) {
161
- const filePath = path.join(commandsDir, file);
162
- const stat = await fs.stat(filePath);
163
- if (stat.size < 50) { // Very small files are probably empty
164
- const content = await fs.readFile(filePath, 'utf8');
165
- if (content.trim().length < 10) {
166
- console.log(chalk.yellow(` โš ๏ธ Possibly empty file: ${language}/.claude/commands/${file} (${stat.size} bytes)`));
167
- }
168
- }
169
- }
170
- }
171
- }
172
- }
173
-
174
- // Function to execute synchronization
175
- if (require.main === module) {
176
- syncTemplates().catch(error => {
177
- console.error(chalk.red('โŒ Error during synchronization:'), error);
178
- process.exit(1);
179
- });
180
- }
181
-
182
- module.exports = { syncTemplates };