claude-code-templates 1.12.0 → 1.12.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.
@@ -5,32 +5,39 @@ const chalk = require('chalk');
5
5
  const boxen = require('boxen');
6
6
  const { createClaudeConfig } = require('../src/index');
7
7
 
8
- // ASCII Art for Claude Code Templates
9
- const banner = chalk.hex('#D97706')(`
10
- ██████╗██╗ █████╗ ██╗ ██╗██████╗ ███████╗
11
- ██╔════╝██║ ██╔══██╗██║ ██║██╔══██╗██╔════╝
12
- ██║ ██║ ███████║██║ ██║██║ ██║█████╗
13
- ██║ ██║ ██╔══██║██║ ██║██║ ██║██╔══╝
14
- ╚██████╗███████╗██║ ██║╚██████╔╝██████╔╝███████╗
15
- ╚═════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝
16
-
17
- ██████╗ ██████╗ ██████╗ ███████╗
18
- ██╔════╝██╔═══██╗██╔══██╗██╔════╝
19
- ██║ ██║ ██║██║ ██║█████╗
20
- ██║ ██║ ██║██║ ██║██╔══╝
21
- ╚██████╗╚██████╔╝██████╔╝███████╗
22
- ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝
23
-
24
- ████████╗███████╗███╗ ███╗██████╗ ██╗ █████╗ ████████╗███████╗███████╗
25
- ╚══██╔══╝██╔════╝████╗ ████║██╔══██╗██║ ██╔══██╗╚══██╔══╝██╔════╝██╔════╝
26
- ██║ █████╗ ██╔████╔██║██████╔╝██║ ███████║ ██║ █████╗ ███████╗
27
- ██║ ██╔══╝ ██║╚██╔╝██║██╔═══╝ ██║ ██╔══██║ ██║ ██╔══╝ ╚════██║
28
- ██║ ███████╗██║ ╚═╝ ██║██║ ███████╗██║ ██║ ██║ ███████╗███████║
29
- ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝
30
- `) +
31
- chalk.yellow('\n 🚀 Setup Claude Code for any project language 🚀');
32
-
33
- console.log(banner);
8
+ const pkg = require('../package.json');
9
+
10
+ const title = 'Claude Code Templates';
11
+ const subtitle = 'Your starting point for Claude Code projects';
12
+
13
+ const colorGradient = ['#EA580C', '#F97316', '#FB923C', '#FDBA74', '#FED7AA', '#FFEBD6'];
14
+
15
+ function colorizeTitle(text) {
16
+ const chars = text.split('');
17
+ const steps = colorGradient.length;
18
+ return chars
19
+ .map((char, i) => {
20
+ const color = colorGradient[i % steps];
21
+ return chalk.hex(color)(char);
22
+ })
23
+ .join('');
24
+ }
25
+
26
+ console.clear();
27
+ console.log(chalk.hex('#F97316')('════════════════════════════════════════════════════════════════'));
28
+ console.log('\n');
29
+ console.log(' 🔮 ' + colorizeTitle(title));
30
+ console.log('\n');
31
+ console.log(' ' + chalk.hex('#FDBA74')(subtitle));
32
+ console.log('\n');
33
+ console.log(chalk.hex('#F97316')('════════════════════════════════════════════════════════════════\n'));
34
+
35
+ console.log(
36
+ chalk.hex('#D97706')('🚀 Setup Claude Code for any project language 🚀') +
37
+ chalk.gray(`\n v${pkg.version}\n\n`) +
38
+ chalk.blue('🌐 Templates: ') + chalk.underline('https://davila7.github.io/claude-code-templates/') + '\n' +
39
+ chalk.blue('📖 Documentation: ') + chalk.underline('https://davila7.github.io/claude-code-templates/docu/')
40
+ );
34
41
 
35
42
  program
36
43
  .name('create-claude-config')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-templates",
3
- "version": "1.12.0",
3
+ "version": "1.12.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": {
package/src/agents.js ADDED
@@ -0,0 +1,262 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+
5
+ const TEMPLATES_DIR = path.join(__dirname, '..', 'templates');
6
+
7
+ /**
8
+ * Get all available agents from the templates directory structure
9
+ * @returns {Array} Array of agent objects with name, description, and color
10
+ */
11
+ function getAvailableAgents() {
12
+ const agents = [];
13
+
14
+ try {
15
+ // Scan all language directories
16
+ const languageDirs = fs.readdirSync(TEMPLATES_DIR)
17
+ .filter(dir => {
18
+ const dirPath = path.join(TEMPLATES_DIR, dir);
19
+ return fs.statSync(dirPath).isDirectory() && dir !== 'common';
20
+ });
21
+
22
+ for (const langDir of languageDirs) {
23
+ const frameworksPath = path.join(TEMPLATES_DIR, langDir, 'examples');
24
+
25
+ if (fs.existsSync(frameworksPath)) {
26
+ const frameworks = fs.readdirSync(frameworksPath)
27
+ .filter(dir => {
28
+ const dirPath = path.join(frameworksPath, dir);
29
+ return fs.statSync(dirPath).isDirectory();
30
+ });
31
+
32
+ for (const framework of frameworks) {
33
+ const agentsPath = path.join(frameworksPath, framework, 'agents');
34
+
35
+ if (fs.existsSync(agentsPath)) {
36
+ const agentFiles = fs.readdirSync(agentsPath)
37
+ .filter(file => file.endsWith('.md'));
38
+
39
+ for (const file of agentFiles) {
40
+ const filePath = path.join(agentsPath, file);
41
+ const content = fs.readFileSync(filePath, 'utf8');
42
+ const agent = parseAgentFile(content, file);
43
+
44
+ if (agent) {
45
+ agent.language = langDir;
46
+ agent.framework = framework;
47
+ agent.filePath = filePath;
48
+ agents.push(agent);
49
+ }
50
+ }
51
+ }
52
+ }
53
+ }
54
+ }
55
+
56
+ return agents;
57
+ } catch (error) {
58
+ console.log(chalk.yellow('⚠️ No agents templates found'));
59
+ return [];
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Parse agent markdown file to extract frontmatter
65
+ * @param {string} content - File content
66
+ * @param {string} filename - File name
67
+ * @returns {Object|null} Agent object or null if parsing fails
68
+ */
69
+ function parseAgentFile(content, filename) {
70
+ try {
71
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
72
+ if (!frontmatterMatch) {
73
+ return null;
74
+ }
75
+
76
+ const frontmatter = frontmatterMatch[1];
77
+ const lines = frontmatter.split('\n');
78
+ const agent = { filename };
79
+
80
+ for (const line of lines) {
81
+ const [key, ...valueParts] = line.split(':');
82
+ if (key && valueParts.length > 0) {
83
+ const value = valueParts.join(':').trim();
84
+ agent[key.trim()] = value;
85
+ }
86
+ }
87
+
88
+ // Extract description without examples for display
89
+ if (agent.description) {
90
+ const shortDesc = agent.description.split('Examples:')[0].trim();
91
+ agent.shortDescription = shortDesc;
92
+ }
93
+
94
+ return agent;
95
+ } catch (error) {
96
+ console.log(chalk.yellow(`⚠️ Failed to parse agent file: ${filename}`));
97
+ return null;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Get agents that are relevant for a specific language/framework
103
+ * @param {string} language - Programming language
104
+ * @param {string} framework - Framework (optional)
105
+ * @returns {Array} Array of relevant agents
106
+ */
107
+ function getAgentsForLanguageAndFramework(language, framework) {
108
+ const allAgents = getAvailableAgents();
109
+
110
+ // Filter agents based on language and framework
111
+ return allAgents.filter(agent => {
112
+ // First check exact language match
113
+ if (agent.language !== language) {
114
+ return false;
115
+ }
116
+
117
+ // If framework is specified and not 'none', check framework match
118
+ if (framework && framework !== 'none') {
119
+ // Extract framework name from framework path (e.g., 'react-app' -> 'react')
120
+ const frameworkName = framework.includes('-') ? framework.split('-')[0] : framework;
121
+ const agentFrameworkName = agent.framework.includes('-') ? agent.framework.split('-')[0] : agent.framework;
122
+
123
+ return agentFrameworkName.toLowerCase().includes(frameworkName.toLowerCase());
124
+ }
125
+
126
+ // If no specific framework, return all agents for this language
127
+ return true;
128
+ });
129
+ }
130
+
131
+ /**
132
+ * Install selected agents to the project's .claude/agents directory
133
+ * @param {Array} selectedAgents - Array of agent names to install
134
+ * @param {string} projectPath - Project directory path
135
+ * @returns {Promise<boolean>} Success status
136
+ */
137
+ async function installAgents(selectedAgents, projectPath = process.cwd()) {
138
+ try {
139
+ const claudeDir = path.join(projectPath, '.claude');
140
+ const agentsDir = path.join(claudeDir, 'agents');
141
+
142
+ // Create .claude/agents directory if it doesn't exist
143
+ await fs.ensureDir(agentsDir);
144
+
145
+ let installedCount = 0;
146
+ const allAgents = getAvailableAgents();
147
+
148
+ for (const agentName of selectedAgents) {
149
+ // Find the agent by name in the available agents
150
+ const agent = allAgents.find(a => a.name === agentName);
151
+
152
+ if (agent && agent.filePath) {
153
+ const targetFile = path.join(agentsDir, `${agentName}.md`);
154
+
155
+ if (await fs.pathExists(agent.filePath)) {
156
+ await fs.copy(agent.filePath, targetFile);
157
+ installedCount++;
158
+ console.log(chalk.green(`✓ Installed agent: ${agentName} (${agent.language}/${agent.framework})`));
159
+ } else {
160
+ console.log(chalk.yellow(`⚠️ Agent source file not found: ${agent.filePath}`));
161
+ }
162
+ } else {
163
+ console.log(chalk.yellow(`⚠️ Agent not found: ${agentName}`));
164
+ }
165
+ }
166
+
167
+ if (installedCount > 0) {
168
+ console.log(chalk.green(`\n🎉 Successfully installed ${installedCount} agent(s) to .claude/agents/`));
169
+ console.log(chalk.blue(' You can now use these agents in your Claude Code conversations!'));
170
+ return true;
171
+ } else {
172
+ console.log(chalk.yellow('⚠️ No agents were installed'));
173
+ return false;
174
+ }
175
+
176
+ } catch (error) {
177
+ console.error(chalk.red('❌ Failed to install agents:'), error.message);
178
+ return false;
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Check if project already has agents installed
184
+ * @param {string} projectPath - Project directory path
185
+ * @returns {Promise<Array>} Array of installed agent names
186
+ */
187
+ async function getInstalledAgents(projectPath = process.cwd()) {
188
+ try {
189
+ const agentsDir = path.join(projectPath, '.claude', 'agents');
190
+
191
+ if (!(await fs.pathExists(agentsDir))) {
192
+ return [];
193
+ }
194
+
195
+ const agentFiles = await fs.readdir(agentsDir);
196
+ return agentFiles
197
+ .filter(file => file.endsWith('.md'))
198
+ .map(file => path.basename(file, '.md'));
199
+
200
+ } catch (error) {
201
+ return [];
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Format agent choices for inquirer prompt
207
+ * @param {Array} agents - Array of agent objects
208
+ * @param {Array} installedAgents - Array of already installed agent names
209
+ * @returns {Array} Formatted choices for inquirer
210
+ */
211
+ function formatAgentChoices(agents, installedAgents = []) {
212
+ return agents.map(agent => {
213
+ const isInstalled = installedAgents.includes(agent.name);
214
+ const colorFn = getColorFunction(agent.color);
215
+
216
+ const name = isInstalled
217
+ ? `${colorFn(agent.name)} ${chalk.dim('(already installed)')}`
218
+ : colorFn(agent.name);
219
+
220
+ const description = agent.shortDescription || agent.description || 'No description available';
221
+ // Truncate description if too long
222
+ const truncatedDesc = description.length > 80
223
+ ? description.substring(0, 80) + '...'
224
+ : description;
225
+
226
+ return {
227
+ name: `${name}\n ${chalk.dim(truncatedDesc)}`,
228
+ value: agent.name,
229
+ short: agent.name,
230
+ disabled: isInstalled ? 'Already installed' : false
231
+ };
232
+ });
233
+ }
234
+
235
+ /**
236
+ * Get chalk color function based on color name
237
+ * @param {string} colorName - Color name
238
+ * @returns {Function} Chalk color function
239
+ */
240
+ function getColorFunction(colorName) {
241
+ const colorMap = {
242
+ red: chalk.red,
243
+ green: chalk.green,
244
+ yellow: chalk.yellow,
245
+ blue: chalk.blue,
246
+ magenta: chalk.magenta,
247
+ cyan: chalk.cyan,
248
+ white: chalk.white,
249
+ gray: chalk.gray,
250
+ grey: chalk.gray
251
+ };
252
+
253
+ return colorMap[colorName] || chalk.white;
254
+ }
255
+
256
+ module.exports = {
257
+ getAvailableAgents,
258
+ getAgentsForLanguageAndFramework,
259
+ installAgents,
260
+ getInstalledAgents,
261
+ formatAgentChoices
262
+ };
@@ -25,14 +25,14 @@ function getAvailableCommands(language) {
25
25
  }
26
26
 
27
27
  // Scan framework-specific commands in examples
28
- const examplesDir = path.join(languageDir, 'examples');
29
- if (fs.existsSync(examplesDir)) {
30
- const frameworkDirs = fs.readdirSync(examplesDir).filter(dir => {
31
- return fs.statSync(path.join(examplesDir, dir)).isDirectory();
28
+ const frameworksDir = path.join(languageDir, 'examples');
29
+ if (fs.existsSync(frameworksDir)) {
30
+ const frameworkDirs = fs.readdirSync(frameworksDir).filter(dir => {
31
+ return fs.statSync(path.join(frameworksDir, dir)).isDirectory();
32
32
  });
33
33
 
34
34
  frameworkDirs.forEach(framework => {
35
- const frameworkCommandsDir = path.join(examplesDir, framework, '.claude', 'commands');
35
+ const frameworkCommandsDir = path.join(frameworksDir, framework, '.claude', 'commands');
36
36
  if (fs.existsSync(frameworkCommandsDir)) {
37
37
  const frameworkCommands = scanCommandsInDirectory(frameworkCommandsDir, framework);
38
38
  commands.push(...frameworkCommands);
package/src/index.js CHANGED
@@ -8,6 +8,7 @@ const { getTemplateConfig, TEMPLATES_CONFIG } = require('./templates');
8
8
  const { createPrompts, interactivePrompts } = require('./prompts');
9
9
  const { copyTemplateFiles, runPostInstallationValidation } = require('./file-operations');
10
10
  const { getHooksForLanguage, getMCPsForLanguage } = require('./hook-scanner');
11
+ const { installAgents } = require('./agents');
11
12
  const { runCommandStats } = require('./command-stats');
12
13
  const { runHookStats } = require('./hook-stats');
13
14
  const { runMCPStats } = require('./mcp-stats');
@@ -15,7 +16,6 @@ const { runAnalytics } = require('./analytics');
15
16
  const { runHealthCheck } = require('./health-check');
16
17
 
17
18
  async function showMainMenu() {
18
- console.log(chalk.blue('🚀 Welcome to Claude Code Templates!'));
19
19
  console.log('');
20
20
 
21
21
  const initialChoice = await inquirer.prompt([{
@@ -33,15 +33,15 @@ async function showMainMenu() {
33
33
  value: 'chats',
34
34
  short: 'Chats Dashboard'
35
35
  },
36
- {
37
- name: '🔍 Health Check - Verify your Claude Code setup and configuration',
38
- value: 'health',
39
- short: 'Health Check'
40
- },
41
36
  {
42
37
  name: '⚙️ Project Setup - Configure Claude Code for your project',
43
38
  value: 'setup',
44
39
  short: 'Project Setup'
40
+ },
41
+ {
42
+ name: '🔍 Health Check - Verify your Claude Code setup and configuration',
43
+ value: 'health',
44
+ short: 'Health Check'
45
45
  }
46
46
  ],
47
47
  default: 'analytics'
@@ -193,6 +193,12 @@ async function createClaudeConfig(options = {}) {
193
193
  templateConfig.language = config.language; // Ensure language is available for MCP filtering
194
194
  }
195
195
 
196
+ // Install selected agents
197
+ if (config.agents && config.agents.length > 0) {
198
+ console.log(chalk.blue('🤖 Installing Claude Code agents...'));
199
+ await installAgents(config.agents, targetDir);
200
+ }
201
+
196
202
  if (options.dryRun) {
197
203
  console.log(chalk.yellow('🔍 Dry run - showing what would be copied:'));
198
204
  templateConfig.files.forEach(file => {
@@ -223,6 +229,7 @@ async function createClaudeConfig(options = {}) {
223
229
  console.log(chalk.white(' 3. Start using Claude Code with: claude'));
224
230
  console.log('');
225
231
  console.log(chalk.blue('🌐 View all available templates at: https://davila7.github.io/claude-code-templates/'));
232
+ console.log(chalk.blue('📖 Read the complete documentation at: https://davila7.github.io/claude-code-templates/docu/'));
226
233
 
227
234
  if (config.language !== 'common') {
228
235
  console.log(chalk.yellow(`💡 Language-specific features for ${config.language} have been configured`));
package/src/prompts.js CHANGED
@@ -6,6 +6,7 @@ const inquirer = require('inquirer');
6
6
  const { getAvailableLanguages, getFrameworksForLanguage } = require('./templates');
7
7
  const { getCommandsForLanguageAndFramework } = require('./command-scanner');
8
8
  const { getHooksForLanguage, getMCPsForLanguage } = require('./hook-scanner');
9
+ const { getAgentsForLanguageAndFramework, getInstalledAgents, formatAgentChoices } = require('./agents');
9
10
 
10
11
  async function interactivePrompts(projectInfo, options = {}) {
11
12
  const state = {
@@ -17,7 +18,7 @@ async function interactivePrompts(projectInfo, options = {}) {
17
18
  // Build steps array based on options
18
19
  if (!options.language) state.steps.push('language');
19
20
  if (!options.framework) state.steps.push('framework');
20
- state.steps.push('commands', 'hooks', 'mcps', 'analytics', 'confirm');
21
+ state.steps.push('commands', 'hooks', 'mcps', 'agents', 'analytics', 'confirm');
21
22
 
22
23
  while (state.currentStep < state.steps.length) {
23
24
  const stepName = state.steps[state.currentStep];
@@ -41,7 +42,13 @@ async function interactivePrompts(projectInfo, options = {}) {
41
42
  }
42
43
 
43
44
  async function showStep(stepName, currentAnswers, projectInfo, options) {
44
- const stepConfig = getStepConfig(stepName, currentAnswers, projectInfo, options);
45
+ // Handle async data fetching for agents step
46
+ let additionalData = {};
47
+ if (stepName === 'agents') {
48
+ additionalData.installedAgents = await getInstalledAgents();
49
+ }
50
+
51
+ const stepConfig = getStepConfig(stepName, currentAnswers, projectInfo, options, additionalData);
45
52
 
46
53
  if (!stepConfig) {
47
54
  return { action: 'next', value: null };
@@ -69,7 +76,7 @@ async function showStep(stepName, currentAnswers, projectInfo, options) {
69
76
  return { action: 'next', value };
70
77
  }
71
78
 
72
- function getStepConfig(stepName, currentAnswers, projectInfo, options) {
79
+ function getStepConfig(stepName, currentAnswers, projectInfo, options, additionalData = {}) {
73
80
  switch (stepName) {
74
81
  case 'language':
75
82
  return {
@@ -188,6 +195,32 @@ function getStepConfig(stepName, currentAnswers, projectInfo, options) {
188
195
  pageSize: 15
189
196
  };
190
197
 
198
+ case 'agents':
199
+ const agentLanguage = currentAnswers.language || options.language;
200
+ const agentFramework = currentAnswers.framework || options.framework;
201
+
202
+ if (!agentLanguage) {
203
+ return null; // Skip if no language selected
204
+ }
205
+
206
+ const availableAgents = getAgentsForLanguageAndFramework(agentLanguage, agentFramework);
207
+
208
+ if (availableAgents.length === 0) {
209
+ return null; // Skip if no agents available
210
+ }
211
+
212
+ const installedAgents = additionalData.installedAgents || [];
213
+ const agentChoices = formatAgentChoices(availableAgents, installedAgents);
214
+
215
+ return {
216
+ type: 'checkbox',
217
+ name: 'agents',
218
+ message: 'Select Claude Code agents to install (use space to select):',
219
+ choices: agentChoices,
220
+ prefix: chalk.magenta('🤖'),
221
+ pageSize: 10
222
+ };
223
+
191
224
  case 'analytics':
192
225
  return {
193
226
  type: 'confirm',
@@ -203,6 +236,7 @@ function getStepConfig(stepName, currentAnswers, projectInfo, options) {
203
236
  const commandCount = currentAnswers.commands ? currentAnswers.commands.length : 0;
204
237
  const hookCount = currentAnswers.hooks ? currentAnswers.hooks.length : 0;
205
238
  const mcpCount = currentAnswers.mcps ? currentAnswers.mcps.length : 0;
239
+ const agentCount = currentAnswers.agents ? currentAnswers.agents.length : 0;
206
240
 
207
241
  let message = `Setup Claude Code for ${chalk.cyan(confirmLanguage)}`;
208
242
  if (confirmFramework !== 'none') {
@@ -217,6 +251,9 @@ function getStepConfig(stepName, currentAnswers, projectInfo, options) {
217
251
  if (mcpCount > 0) {
218
252
  message += ` (${chalk.blue(mcpCount)} MCP)`;
219
253
  }
254
+ if (agentCount > 0) {
255
+ message += ` (${chalk.magenta(agentCount)} agents)`;
256
+ }
220
257
  message += '?';
221
258
 
222
259
  return {
package/src/templates.js CHANGED
@@ -6,10 +6,7 @@ const TEMPLATES_CONFIG = {
6
6
  name: 'Common (Language-agnostic)',
7
7
  description: 'Universal configuration for any project',
8
8
  files: [
9
- { source: 'common/CLAUDE.md', destination: 'CLAUDE.md' },
10
- { source: 'common/.claude', destination: '.claude' },
11
- { source: 'common/.claude/settings.json', destination: '.claude/settings.json' },
12
- { source: 'common/.mcp.json', destination: '.mcp.json' }
9
+ { source: 'common/CLAUDE.md', destination: 'CLAUDE.md' }
13
10
  ]
14
11
  },
15
12
  'javascript-typescript': {