claude-code-templates 1.10.1 → 1.11.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.
- package/README.md +6 -0
- package/bin/create-claude-config.js +1 -0
- package/package.json +2 -2
- package/src/analytics/core/ConversationAnalyzer.js +94 -20
- package/src/analytics/core/FileWatcher.js +146 -11
- package/src/analytics/data/DataCache.js +124 -19
- package/src/analytics/notifications/NotificationManager.js +37 -0
- package/src/analytics/notifications/WebSocketServer.js +1 -1
- package/src/analytics-web/FRONT_ARCHITECTURE.md +46 -0
- package/src/analytics-web/assets/js/{main.js → main.js.deprecated} +32 -3
- package/src/analytics-web/components/AgentsPage.js +2535 -0
- package/src/analytics-web/components/App.js +430 -0
- package/src/analytics-web/components/{Dashboard.js → Dashboard.js.deprecated} +23 -7
- package/src/analytics-web/components/DashboardPage.js +1527 -0
- package/src/analytics-web/components/Sidebar.js +197 -0
- package/src/analytics-web/components/ToolDisplay.js +539 -0
- package/src/analytics-web/index.html +3275 -1792
- package/src/analytics-web/services/DataService.js +89 -16
- package/src/analytics-web/services/StateService.js +9 -0
- package/src/analytics-web/services/WebSocketService.js +17 -5
- package/src/analytics.js +323 -35
- package/src/console-bridge.js +610 -0
- package/src/file-operations.js +143 -23
- package/src/index.js +24 -1
- package/src/templates.js +4 -0
- package/src/test-console-bridge.js +67 -0
package/src/file-operations.js
CHANGED
|
@@ -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
|
|
7
|
-
const
|
|
7
|
+
async function checkExistingFiles(targetDir, templateConfig) {
|
|
8
|
+
const existingFiles = [];
|
|
8
9
|
|
|
9
|
-
// Check
|
|
10
|
+
// Check for existing CLAUDE.md
|
|
10
11
|
const claudeFile = path.join(targetDir, 'CLAUDE.md');
|
|
11
12
|
if (await fs.pathExists(claudeFile)) {
|
|
12
|
-
|
|
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
|
|
16
|
+
// Check for existing .claude directory
|
|
19
17
|
const claudeDir = path.join(targetDir, '.claude');
|
|
20
18
|
if (await fs.pathExists(claudeDir)) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
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,
|
|
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:
|
|
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();
|