claude-code-templates 1.10.1 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,29 +1,117 @@
1
1
  const fs = require('fs-extra');
2
2
  const path = require('path');
3
3
  const chalk = require('chalk');
4
+ const inquirer = require('inquirer');
4
5
  const { getHooksForLanguage, filterHooksBySelection, getMCPsForLanguage, filterMCPsBySelection } = require('./hook-scanner');
5
6
 
6
- async function copyTemplateFiles(templateConfig, targetDir) {
7
- const templateDir = path.join(__dirname, '../templates');
7
+ async function checkExistingFiles(targetDir, templateConfig) {
8
+ const existingFiles = [];
8
9
 
9
- // Check if CLAUDE.md already exists
10
+ // Check for existing CLAUDE.md
10
11
  const claudeFile = path.join(targetDir, 'CLAUDE.md');
11
12
  if (await fs.pathExists(claudeFile)) {
12
- // Create backup
13
- const backupFile = path.join(targetDir, 'CLAUDE.md.backup');
14
- await fs.copy(claudeFile, backupFile);
15
- console.log(chalk.yellow(`📋 Existing CLAUDE.md backed up to CLAUDE.md.backup`));
13
+ existingFiles.push('CLAUDE.md');
16
14
  }
17
15
 
18
- // Check if .claude directory already exists
16
+ // Check for existing .claude directory
19
17
  const claudeDir = path.join(targetDir, '.claude');
20
18
  if (await fs.pathExists(claudeDir)) {
21
- // Create backup
22
- const backupDir = path.join(targetDir, '.claude.backup');
23
- await fs.copy(claudeDir, backupDir);
24
- console.log(chalk.yellow(`📁 Existing .claude directory backed up to .claude.backup`));
19
+ existingFiles.push('.claude/');
20
+ }
21
+
22
+ // Check for existing .mcp.json
23
+ const mcpFile = path.join(targetDir, '.mcp.json');
24
+ if (await fs.pathExists(mcpFile)) {
25
+ existingFiles.push('.mcp.json');
26
+ }
27
+
28
+ return existingFiles;
29
+ }
30
+
31
+ async function promptUserForOverwrite(existingFiles, targetDir) {
32
+ if (existingFiles.length === 0) {
33
+ return 'proceed'; // No existing files, safe to proceed
34
+ }
35
+
36
+ console.log(chalk.yellow('\n⚠️ Existing Claude Code configuration detected!'));
37
+ console.log(chalk.yellow('The following files/directories already exist:'));
38
+ existingFiles.forEach(file => {
39
+ console.log(chalk.yellow(` • ${file}`));
40
+ });
41
+
42
+ const choices = [
43
+ {
44
+ name: '🔄 Backup and overwrite - Create backups and install new configuration',
45
+ value: 'backup',
46
+ short: 'Backup and overwrite'
47
+ },
48
+ {
49
+ name: '🔀 Merge configurations - Combine existing with new templates',
50
+ value: 'merge',
51
+ short: 'Merge'
52
+ },
53
+ {
54
+ name: '❌ Cancel setup - Keep existing configuration unchanged',
55
+ value: 'cancel',
56
+ short: 'Cancel'
57
+ }
58
+ ];
59
+
60
+ const answer = await inquirer.prompt([{
61
+ type: 'list',
62
+ name: 'action',
63
+ message: 'How would you like to proceed?',
64
+ choices,
65
+ default: 'backup'
66
+ }]);
67
+
68
+ return answer.action;
69
+ }
70
+
71
+ async function createBackups(existingFiles, targetDir) {
72
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
73
+
74
+ for (const file of existingFiles) {
75
+ const sourcePath = path.join(targetDir, file);
76
+ const backupPath = path.join(targetDir, `${file.replace('/', '')}.backup-${timestamp}`);
77
+
78
+ try {
79
+ await fs.copy(sourcePath, backupPath);
80
+ console.log(chalk.green(`📋 Backed up ${file} → ${path.basename(backupPath)}`));
81
+ } catch (error) {
82
+ console.error(chalk.red(`✗ Failed to backup ${file}:`), error.message);
83
+ throw error;
84
+ }
85
+ }
86
+ }
87
+
88
+ async function copyTemplateFiles(templateConfig, targetDir, options = {}) {
89
+ const templateDir = path.join(__dirname, '../templates');
90
+
91
+ // Check for existing files and get user preference
92
+ const existingFiles = await checkExistingFiles(targetDir, templateConfig);
93
+ let userAction = 'proceed';
94
+
95
+ if (!options.yes && !options.dryRun) {
96
+ userAction = await promptUserForOverwrite(existingFiles, targetDir);
97
+
98
+ if (userAction === 'cancel') {
99
+ console.log(chalk.blue('✓ Setup cancelled. Your existing configuration remains unchanged.'));
100
+ return false; // Indicate cancellation
101
+ }
102
+ } else if (existingFiles.length > 0) {
103
+ // In --yes mode, default to backup behavior
104
+ userAction = 'backup';
105
+ }
106
+
107
+ // Create backups if requested
108
+ if (userAction === 'backup' && existingFiles.length > 0) {
109
+ await createBackups(existingFiles, targetDir);
25
110
  }
26
111
 
112
+ // Determine overwrite behavior based on user choice
113
+ const shouldOverwrite = userAction !== 'merge';
114
+
27
115
  // Copy base files and framework-specific files
28
116
  for (const file of templateConfig.files) {
29
117
  const sourcePath = path.join(templateDir, file.source);
@@ -40,14 +128,21 @@ async function copyTemplateFiles(templateConfig, targetDir) {
40
128
  for (const frameworkFile of frameworkFiles) {
41
129
  const srcFile = path.join(sourcePath, frameworkFile);
42
130
  const destFile = path.join(destPath, frameworkFile);
43
- await fs.copy(srcFile, destFile, { overwrite: true });
131
+
132
+ // In merge mode, skip if file already exists
133
+ if (userAction === 'merge' && await fs.pathExists(destFile)) {
134
+ console.log(chalk.blue(`⏭️ Skipped ${frameworkFile} (already exists)`));
135
+ continue;
136
+ }
137
+
138
+ await fs.copy(srcFile, destFile, { overwrite: shouldOverwrite });
44
139
  }
45
140
 
46
141
  console.log(chalk.green(`✓ Copied framework commands ${file.source} → ${file.destination}`));
47
142
  } else if (file.source.includes('.claude') && !file.source.includes('examples/')) {
48
143
  // This is base .claude directory - copy it but handle commands specially
49
144
  await fs.copy(sourcePath, destPath, {
50
- overwrite: true,
145
+ overwrite: shouldOverwrite,
51
146
  filter: (src) => {
52
147
  // Skip the commands directory itself - we'll handle it separately
53
148
  return !src.endsWith('.claude/commands');
@@ -69,24 +164,47 @@ async function copyTemplateFiles(templateConfig, targetDir) {
69
164
  if (!excludeCommands.includes(baseCommand)) {
70
165
  const srcFile = path.join(baseCommandsPath, baseCommand);
71
166
  const destFile = path.join(destCommandsPath, baseCommand);
72
- await fs.copy(srcFile, destFile, { overwrite: true });
167
+
168
+ // In merge mode, skip if file already exists
169
+ if (userAction === 'merge' && await fs.pathExists(destFile)) {
170
+ console.log(chalk.blue(`⏭️ Skipped ${baseCommand} (already exists)`));
171
+ continue;
172
+ }
173
+
174
+ await fs.copy(srcFile, destFile, { overwrite: shouldOverwrite });
73
175
  }
74
176
  }
75
177
  }
76
178
 
77
179
  console.log(chalk.green(`✓ Copied base configuration and commands ${file.source} → ${file.destination}`));
78
180
  } else if (file.source.includes('settings.json') && templateConfig.selectedHooks) {
79
- // Handle settings.json with hook filtering
80
- await processSettingsFile(sourcePath, destPath, templateConfig);
81
- console.log(chalk.green(`✓ Copied ${file.source} → ${file.destination} (with selected hooks)`));
181
+ // In merge mode, merge settings instead of overwriting
182
+ if (userAction === 'merge') {
183
+ await mergeSettingsFile(sourcePath, destPath, templateConfig);
184
+ console.log(chalk.green(`✓ Merged ${file.source} → ${file.destination} (with selected hooks)`));
185
+ } else {
186
+ await processSettingsFile(sourcePath, destPath, templateConfig);
187
+ console.log(chalk.green(`✓ Copied ${file.source} → ${file.destination} (with selected hooks)`));
188
+ }
82
189
  } 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)`));
190
+ // In merge mode, merge MCP config instead of overwriting
191
+ if (userAction === 'merge') {
192
+ await mergeMCPFile(sourcePath, destPath, templateConfig);
193
+ console.log(chalk.green(`✓ Merged ${file.source} → ${file.destination} (with selected MCPs)`));
194
+ } else {
195
+ await processMCPFile(sourcePath, destPath, templateConfig);
196
+ console.log(chalk.green(`✓ Copied ${file.source} → ${file.destination} (with selected MCPs)`));
197
+ }
86
198
  } else {
87
- // Copy regular files (CLAUDE.md, settings.json, etc.)
199
+ // Copy regular files (CLAUDE.md, etc.)
200
+ // In merge mode, skip if file already exists
201
+ if (userAction === 'merge' && await fs.pathExists(destPath)) {
202
+ console.log(chalk.blue(`⏭️ Skipped ${file.destination} (already exists)`));
203
+ continue;
204
+ }
205
+
88
206
  await fs.copy(sourcePath, destPath, {
89
- overwrite: true,
207
+ overwrite: shouldOverwrite,
90
208
  filter: (src) => {
91
209
  // Skip commands directory during regular copy - we handle them above
92
210
  return !src.includes('.claude/commands');
@@ -100,6 +218,8 @@ async function copyTemplateFiles(templateConfig, targetDir) {
100
218
  }
101
219
  }
102
220
 
221
+ return true; // Indicate successful completion
222
+
103
223
  // Copy selected commands individually
104
224
  if (templateConfig.selectedCommands && templateConfig.selectedCommands.length > 0) {
105
225
  const commandsDir = path.join(targetDir, '.claude', 'commands');
package/src/index.js CHANGED
@@ -28,6 +28,11 @@ async function showMainMenu() {
28
28
  value: 'analytics',
29
29
  short: 'Analytics Dashboard'
30
30
  },
31
+ {
32
+ name: '💬 Chats Dashboard - View and analyze your Claude conversations',
33
+ value: 'chats',
34
+ short: 'Chats Dashboard'
35
+ },
31
36
  {
32
37
  name: '🔍 Health Check - Verify your Claude Code setup and configuration',
33
38
  value: 'health',
@@ -48,6 +53,12 @@ async function showMainMenu() {
48
53
  return;
49
54
  }
50
55
 
56
+ if (initialChoice.action === 'chats') {
57
+ console.log(chalk.blue('💬 Launching Claude Code Chats Dashboard...'));
58
+ await runAnalytics({ openTo: 'agents' });
59
+ return;
60
+ }
61
+
51
62
  if (initialChoice.action === 'health') {
52
63
  console.log(chalk.blue('🔍 Running Health Check...'));
53
64
  const healthResult = await runHealthCheck();
@@ -93,6 +104,12 @@ async function createClaudeConfig(options = {}) {
93
104
  return;
94
105
  }
95
106
 
107
+ // Handle chats/agents dashboard
108
+ if (options.chats || options.agents) {
109
+ await runAnalytics({ ...options, openTo: 'agents' });
110
+ return;
111
+ }
112
+
96
113
  // Handle health check
97
114
  let shouldRunSetup = false;
98
115
  if (options.healthCheck || options.health || options.check || options.verify) {
@@ -187,7 +204,11 @@ async function createClaudeConfig(options = {}) {
187
204
  // Copy template files
188
205
  const copySpinner = ora('Copying template files...').start();
189
206
  try {
190
- await copyTemplateFiles(templateConfig, targetDir);
207
+ const result = await copyTemplateFiles(templateConfig, targetDir, options);
208
+ if (result === false) {
209
+ copySpinner.info('Setup cancelled by user');
210
+ return; // Exit early if user cancelled
211
+ }
191
212
  copySpinner.succeed('Template files copied successfully');
192
213
  } catch (error) {
193
214
  copySpinner.fail('Failed to copy template files');
@@ -200,6 +221,8 @@ async function createClaudeConfig(options = {}) {
200
221
  console.log(chalk.white(' 1. Review the generated CLAUDE.md file'));
201
222
  console.log(chalk.white(' 2. Customize the configuration for your project'));
202
223
  console.log(chalk.white(' 3. Start using Claude Code with: claude'));
224
+ console.log('');
225
+ console.log(chalk.blue('🌐 View all available templates at: https://davila7.github.io/claude-code-templates/'));
203
226
 
204
227
  if (config.language !== 'common') {
205
228
  console.log(chalk.yellow(`💡 Language-specific features for ${config.language} have been configured`));
package/src/templates.js CHANGED
@@ -8,6 +8,7 @@ const TEMPLATES_CONFIG = {
8
8
  files: [
9
9
  { source: 'common/CLAUDE.md', destination: 'CLAUDE.md' },
10
10
  { source: 'common/.claude', destination: '.claude' },
11
+ { source: 'common/.claude/settings.json', destination: '.claude/settings.json' },
11
12
  { source: 'common/.mcp.json', destination: '.mcp.json' }
12
13
  ]
13
14
  },
@@ -17,6 +18,7 @@ const TEMPLATES_CONFIG = {
17
18
  files: [
18
19
  { source: 'javascript-typescript/CLAUDE.md', destination: 'CLAUDE.md' },
19
20
  { source: 'javascript-typescript/.claude', destination: '.claude' },
21
+ { source: 'javascript-typescript/.claude/settings.json', destination: '.claude/settings.json' },
20
22
  { source: 'javascript-typescript/.mcp.json', destination: '.mcp.json' }
21
23
  ],
22
24
  frameworks: {
@@ -52,6 +54,7 @@ const TEMPLATES_CONFIG = {
52
54
  files: [
53
55
  { source: 'python/CLAUDE.md', destination: 'CLAUDE.md' },
54
56
  { source: 'python/.claude', destination: '.claude' },
57
+ { source: 'python/.claude/settings.json', destination: '.claude/settings.json' },
55
58
  { source: 'python/.mcp.json', destination: '.mcp.json' }
56
59
  ],
57
60
  frameworks: {
@@ -82,6 +85,7 @@ const TEMPLATES_CONFIG = {
82
85
  files: [
83
86
  { source: 'ruby/CLAUDE.md', destination: 'CLAUDE.md' },
84
87
  { source: 'ruby/.claude', destination: '.claude' },
88
+ { source: 'ruby/.claude/settings.json', destination: '.claude/settings.json' },
85
89
  { source: 'ruby/.mcp.json', destination: '.mcp.json' }
86
90
  ],
87
91
  frameworks: {
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env node
2
+
3
+ const ConsoleBridge = require('./console-bridge');
4
+ const chalk = require('chalk');
5
+
6
+ /**
7
+ * Test script for the Console Bridge
8
+ */
9
+ async function testConsoleBridge() {
10
+ console.log(chalk.blue('🧪 Testing Console Bridge Integration'));
11
+ console.log(chalk.gray('This will monitor running Claude Code processes and enable web-based interaction'));
12
+ console.log('');
13
+
14
+ // Create console bridge instance
15
+ const bridge = new ConsoleBridge({
16
+ port: 3334,
17
+ debug: true
18
+ });
19
+
20
+ try {
21
+ // Initialize the bridge
22
+ const success = await bridge.initialize();
23
+
24
+ if (!success) {
25
+ console.error(chalk.red('❌ Failed to initialize Console Bridge'));
26
+ process.exit(1);
27
+ }
28
+
29
+ console.log('');
30
+ console.log(chalk.green('✅ Console Bridge is running!'));
31
+ console.log(chalk.cyan('📡 WebSocket server: ws://localhost:3334'));
32
+ console.log(chalk.cyan('🌐 Web interface should connect to this WebSocket'));
33
+ console.log('');
34
+ console.log(chalk.yellow('💡 Instructions:'));
35
+ console.log(' 1. Open your analytics dashboard (npm run analytics:start)');
36
+ console.log(' 2. Navigate to the Agents page');
37
+ console.log(' 3. The console interaction panel should appear when Claude Code prompts are detected');
38
+ console.log(' 4. Run Claude Code in another terminal to test interaction');
39
+ console.log('');
40
+ console.log(chalk.gray('Press Ctrl+C to stop the bridge'));
41
+
42
+ // Handle graceful shutdown
43
+ process.on('SIGINT', async () => {
44
+ console.log('');
45
+ console.log(chalk.yellow('🛑 Shutting down Console Bridge...'));
46
+ await bridge.shutdown();
47
+ process.exit(0);
48
+ });
49
+
50
+ process.on('SIGTERM', async () => {
51
+ await bridge.shutdown();
52
+ process.exit(0);
53
+ });
54
+
55
+ // Keep the process running
56
+ setInterval(() => {
57
+ // Heartbeat to keep process alive
58
+ }, 1000);
59
+
60
+ } catch (error) {
61
+ console.error(chalk.red('❌ Error running Console Bridge:'), error);
62
+ process.exit(1);
63
+ }
64
+ }
65
+
66
+ // Run the test
67
+ testConsoleBridge();