claude-code-templates 1.3.10 → 1.4.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.
@@ -41,6 +41,7 @@ program
41
41
  .option('-d, --directory <directory>', 'target directory (default: current directory)')
42
42
  .option('-y, --yes', 'skip prompts and use defaults')
43
43
  .option('--dry-run', 'show what would be copied without actually copying')
44
+ .option('--command-stats', 'analyze existing Claude Code commands and offer optimization')
44
45
  .action(async (options) => {
45
46
  try {
46
47
  await createClaudeConfig(options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-templates",
3
- "version": "1.3.10",
3
+ "version": "1.4.1",
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": {
@@ -0,0 +1,283 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+ const { spawn } = require('child_process');
5
+
6
+ /**
7
+ * Estimates token count for text content
8
+ * Based on approximate tokenization rules: ~4 characters per token for English text
9
+ * @param {string} text - Text to analyze
10
+ * @returns {number} Estimated token count
11
+ */
12
+ function estimateTokens(text) {
13
+ // Remove excessive whitespace and normalize
14
+ const cleanText = text.replace(/\s+/g, ' ').trim();
15
+
16
+ // Basic estimation: ~4 characters per token for English text
17
+ // Adjust for code content which typically has more tokens per character
18
+ const baseTokens = Math.ceil(cleanText.length / 4);
19
+
20
+ // Adjust for markdown and code content
21
+ const codeBlocks = (text.match(/```[\s\S]*?```/g) || []).length;
22
+ const inlineCode = (text.match(/`[^`]*`/g) || []).length;
23
+ const markdownElements = (text.match(/[#*_\[\]()]/g) || []).length;
24
+
25
+ // Add extra tokens for code and markdown formatting
26
+ const adjustedTokens = baseTokens + (codeBlocks * 5) + (inlineCode * 2) + Math.ceil(markdownElements / 2);
27
+
28
+ return adjustedTokens;
29
+ }
30
+
31
+ /**
32
+ * Analyzes existing Claude Code commands in the current project
33
+ * @param {string} targetDir - Directory to analyze (default: current directory)
34
+ * @returns {Array} Array of command analysis results
35
+ */
36
+ async function analyzeCommands(targetDir = process.cwd()) {
37
+ const commandsDir = path.join(targetDir, '.claude', 'commands');
38
+
39
+ // Check if .claude/commands directory exists
40
+ if (!fs.existsSync(commandsDir)) {
41
+ return {
42
+ exists: false,
43
+ message: 'No .claude/commands directory found in current project'
44
+ };
45
+ }
46
+
47
+ try {
48
+ const commandFiles = await fs.readdir(commandsDir);
49
+ const markdownFiles = commandFiles.filter(file => file.endsWith('.md'));
50
+
51
+ if (markdownFiles.length === 0) {
52
+ return {
53
+ exists: true,
54
+ commands: [],
55
+ message: 'No command files (.md) found in .claude/commands directory'
56
+ };
57
+ }
58
+
59
+ const commands = [];
60
+
61
+ for (const file of markdownFiles) {
62
+ const filePath = path.join(commandsDir, file);
63
+ const stats = await fs.stat(filePath);
64
+ const content = await fs.readFile(filePath, 'utf8');
65
+
66
+ // Extract first line as title/description
67
+ const lines = content.split('\n').filter(line => line.trim());
68
+ const title = lines.find(line => line.startsWith('#')) || file;
69
+
70
+ // Calculate token estimate
71
+ const tokenCount = estimateTokens(content);
72
+
73
+ commands.push({
74
+ name: file.replace('.md', ''),
75
+ filename: file,
76
+ size: stats.size,
77
+ lastModified: stats.mtime,
78
+ title: title.replace(/^#+\s*/, ''),
79
+ lines: lines.length,
80
+ wordCount: content.split(/\s+/).length,
81
+ tokens: tokenCount
82
+ });
83
+ }
84
+
85
+ // Sort by last modified (newest first)
86
+ commands.sort((a, b) => b.lastModified - a.lastModified);
87
+
88
+ return {
89
+ exists: true,
90
+ commands,
91
+ total: commands.length,
92
+ totalSize: commands.reduce((sum, cmd) => sum + cmd.size, 0),
93
+ totalTokens: commands.reduce((sum, cmd) => sum + cmd.tokens, 0)
94
+ };
95
+ } catch (error) {
96
+ return {
97
+ exists: true,
98
+ error: `Error analyzing commands: ${error.message}`
99
+ };
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Displays command statistics in a formatted table
105
+ * @param {Object} analysis - Result from analyzeCommands()
106
+ */
107
+ function displayCommandStats(analysis) {
108
+ console.log(chalk.cyan('\nšŸ“Š Claude Code Command Analysis'));
109
+ console.log(chalk.gray('═'.repeat(90)));
110
+
111
+ if (!analysis.exists) {
112
+ console.log(chalk.yellow('āš ļø ' + analysis.message));
113
+ console.log(chalk.blue('\nšŸ’” Run the setup first: npx claude-code-templates'));
114
+ return;
115
+ }
116
+
117
+ if (analysis.error) {
118
+ console.log(chalk.red('āŒ ' + analysis.error));
119
+ return;
120
+ }
121
+
122
+ if (analysis.commands.length === 0) {
123
+ console.log(chalk.yellow('āš ļø ' + analysis.message));
124
+ console.log(chalk.blue('\nšŸ’” No commands found to analyze'));
125
+ return;
126
+ }
127
+
128
+ // Summary
129
+ const totalSizeKB = (analysis.totalSize / 1024).toFixed(1);
130
+ const totalTokens = analysis.totalTokens.toLocaleString();
131
+ console.log(chalk.green(`āœ… Found ${analysis.total} command file(s) (${totalSizeKB} KB, ~${totalTokens} tokens total)`));
132
+ console.log('');
133
+
134
+ // Table header
135
+ const header = chalk.bold.blue(
136
+ 'Command'.padEnd(18) +
137
+ 'Size'.padEnd(7) +
138
+ 'Lines'.padEnd(6) +
139
+ 'Words'.padEnd(6) +
140
+ 'Tokens'.padEnd(7) +
141
+ 'Last Modified'
142
+ );
143
+ console.log(header);
144
+ console.log(chalk.gray('─'.repeat(90)));
145
+
146
+ // Table rows
147
+ analysis.commands.forEach(cmd => {
148
+ const sizeFormatted = `${(cmd.size / 1024).toFixed(1)}KB`.padEnd(7);
149
+ const linesFormatted = cmd.lines.toString().padEnd(6);
150
+ const wordsFormatted = cmd.wordCount.toString().padEnd(6);
151
+ const tokensFormatted = cmd.tokens.toString().padEnd(7);
152
+ const dateFormatted = cmd.lastModified.toLocaleDateString();
153
+
154
+ const row = chalk.white(cmd.name.padEnd(18)) +
155
+ chalk.cyan(sizeFormatted) +
156
+ chalk.yellow(linesFormatted) +
157
+ chalk.green(wordsFormatted) +
158
+ chalk.magenta(tokensFormatted) +
159
+ chalk.gray(dateFormatted);
160
+
161
+ console.log(row);
162
+ });
163
+
164
+ console.log(chalk.gray('─'.repeat(90)));
165
+ console.log(chalk.bold(`Total: ${analysis.total} commands, ${totalSizeKB} KB, ~${totalTokens} tokens`));
166
+ }
167
+
168
+ /**
169
+ * Prompts user for command optimization and executes Claude Code if approved
170
+ * @param {Object} analysis - Result from analyzeCommands()
171
+ * @param {string} targetDir - Project directory
172
+ */
173
+ async function promptCommandOptimization(analysis, targetDir) {
174
+ if (!analysis.exists || analysis.commands.length === 0) {
175
+ return;
176
+ }
177
+
178
+ const inquirer = require('inquirer');
179
+
180
+ console.log(chalk.cyan('\nšŸ”§ Command Optimization'));
181
+ console.log(chalk.gray('Claude Code can analyze your commands and suggest improvements based on your project structure.'));
182
+
183
+ try {
184
+ const { optimize } = await inquirer.prompt([{
185
+ type: 'confirm',
186
+ name: 'optimize',
187
+ message: 'Would you like Claude Code to review and optimize your commands?',
188
+ default: true,
189
+ prefix: chalk.blue('šŸ¤–')
190
+ }]);
191
+
192
+ if (!optimize) {
193
+ console.log(chalk.yellow('ā­ļø Skipping optimization. You can run this anytime with --command-stats'));
194
+ return;
195
+ }
196
+
197
+ console.log(chalk.blue('\nšŸš€ Starting Claude Code command optimization...'));
198
+ console.log(chalk.gray('This will analyze your project structure and suggest command improvements.\n'));
199
+
200
+ // Create optimization prompt for Claude
201
+ const optimizationPrompt = createOptimizationPrompt(analysis, targetDir);
202
+
203
+ // Execute Claude Code with optimization prompt
204
+ const claudeCommand = `claude "${optimizationPrompt.replace(/"/g, '\\"')}"`;
205
+
206
+ const claudeProcess = spawn('sh', ['-c', claudeCommand], {
207
+ cwd: targetDir,
208
+ stdio: 'inherit'
209
+ });
210
+
211
+ claudeProcess.on('error', (error) => {
212
+ if (error.code === 'ENOENT') {
213
+ console.log(chalk.yellow('\nāš ļø Claude Code CLI not found in PATH.'));
214
+ console.log(chalk.blue('šŸ’” To run optimization manually later, use: claude "Analyze and optimize .claude/commands/ based on project structure"'));
215
+ } else {
216
+ console.error(chalk.red('Error running Claude Code optimization:'), error.message);
217
+ }
218
+ });
219
+
220
+ claudeProcess.on('close', (code) => {
221
+ if (code === 0) {
222
+ console.log(chalk.green('\nāœ… Claude Code optimization completed successfully!'));
223
+ } else if (code !== null) {
224
+ console.log(chalk.yellow(`\nāš ļø Claude Code optimization exited with code ${code}`));
225
+ }
226
+ });
227
+
228
+ } catch (error) {
229
+ console.error(chalk.red('Error during optimization setup:'), error.message);
230
+ console.log(chalk.blue('šŸ’” You can run optimization manually with: claude "Analyze .claude/commands/ directory"'));
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Creates detailed optimization prompt for Claude Code
236
+ * @param {Object} analysis - Command analysis results
237
+ * @param {string} targetDir - Project directory
238
+ * @returns {string} Formatted prompt for Claude
239
+ */
240
+ function createOptimizationPrompt(analysis, targetDir) {
241
+ const commandList = analysis.commands.map(cmd => `- ${cmd.name}.md (${cmd.lines} lines)`).join('\n');
242
+
243
+ return `Analyze and optimize the Claude Code commands in this project:
244
+
245
+ 1. **Review project structure**: Use LS to examine the current project (package.json, src/, etc.) and identify technologies/frameworks used
246
+
247
+ 2. **Analyze existing commands**: Review all files in .claude/commands/ directory:
248
+ ${commandList}
249
+
250
+ 3. **Command optimization**:
251
+ - Check if commands are relevant for this project's tech stack
252
+ - Suggest improvements to existing commands based on project structure
253
+ - Recommend missing commands that would be useful for this project
254
+ - Update command content to match project's specific setup and dependencies
255
+
256
+ 4. **Make improvements**: If you find commands that need updates or see missing useful commands, implement the changes directly to the .claude/commands/ files
257
+
258
+ Focus on making the commands as useful and specific as possible for this project's actual setup and development workflow.`;
259
+ }
260
+
261
+ /**
262
+ * Main function to run command stats analysis
263
+ * @param {Object} options - CLI options
264
+ */
265
+ async function runCommandStats(options = {}) {
266
+ const targetDir = options.directory || process.cwd();
267
+
268
+ // Analyze existing commands
269
+ const analysis = await analyzeCommands(targetDir);
270
+
271
+ // Display results
272
+ displayCommandStats(analysis);
273
+
274
+ // Prompt for optimization
275
+ await promptCommandOptimization(analysis, targetDir);
276
+ }
277
+
278
+ module.exports = {
279
+ analyzeCommands,
280
+ displayCommandStats,
281
+ promptCommandOptimization,
282
+ runCommandStats
283
+ };
package/src/index.js CHANGED
@@ -8,10 +8,17 @@ 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 { runCommandStats } = require('./command-stats');
11
12
 
12
13
  async function createClaudeConfig(options = {}) {
13
14
  const targetDir = options.directory || process.cwd();
14
15
 
16
+ // Handle command stats analysis
17
+ if (options.commandStats) {
18
+ await runCommandStats(options);
19
+ return;
20
+ }
21
+
15
22
  console.log(chalk.blue('šŸš€ Setting up Claude Code configuration...'));
16
23
  console.log(chalk.gray(`Target directory: ${targetDir}`));
17
24