claude-code-templates 1.10.0 → 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/hook-scanner.js +21 -1
- package/src/index.js +24 -1
- package/src/templates.js +28 -0
- package/src/test-console-bridge.js +67 -0
- package/src/utils.js +46 -0
- package/templates/ruby/.claude/commands/model.md +360 -0
- package/templates/ruby/.claude/commands/test.md +480 -0
- package/templates/ruby/.claude/settings.json +146 -0
- package/templates/ruby/.mcp.json +83 -0
- package/templates/ruby/CLAUDE.md +284 -0
- package/templates/ruby/examples/rails-app/.claude/commands/authentication.md +490 -0
- package/templates/ruby/examples/rails-app/CLAUDE.md +376 -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/hook-scanner.js
CHANGED
|
@@ -123,6 +123,10 @@ function getHookDescription(hook, matcher, type) {
|
|
|
123
123
|
return 'Block print() statements in Python files';
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
if (command.includes('puts\\|p ') && command.includes('rb$')) {
|
|
127
|
+
return 'Block puts/p statements in Ruby files';
|
|
128
|
+
}
|
|
129
|
+
|
|
126
130
|
if (command.includes('fmt.Print') && command.includes('go$')) {
|
|
127
131
|
return 'Block fmt.Print statements in Go files';
|
|
128
132
|
}
|
|
@@ -131,7 +135,7 @@ function getHookDescription(hook, matcher, type) {
|
|
|
131
135
|
return 'Block println! macros in Rust files';
|
|
132
136
|
}
|
|
133
137
|
|
|
134
|
-
if (command.includes('npm audit') || command.includes('pip-audit') || command.includes('cargo audit')) {
|
|
138
|
+
if (command.includes('npm audit') || command.includes('pip-audit') || command.includes('bundle audit') || command.includes('cargo audit')) {
|
|
135
139
|
return 'Security audit for dependencies';
|
|
136
140
|
}
|
|
137
141
|
|
|
@@ -143,6 +147,18 @@ function getHookDescription(hook, matcher, type) {
|
|
|
143
147
|
return 'Auto-format Python files with Black';
|
|
144
148
|
}
|
|
145
149
|
|
|
150
|
+
if (command.includes('rubocop -A') && command.includes('rb$')) {
|
|
151
|
+
return 'Auto-format Ruby files with RuboCop';
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (command.includes('rubocop') && command.includes('rb$') && !command.includes('-A')) {
|
|
155
|
+
return 'Run Ruby linting with RuboCop';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (command.includes('brakeman')) {
|
|
159
|
+
return 'Run Ruby security scan with Brakeman';
|
|
160
|
+
}
|
|
161
|
+
|
|
146
162
|
if (command.includes('isort') && command.includes('py$')) {
|
|
147
163
|
return 'Auto-sort Python imports with isort';
|
|
148
164
|
}
|
|
@@ -195,6 +211,10 @@ function getHookDescription(hook, matcher, type) {
|
|
|
195
211
|
return 'Auto-run Python tests for modified files';
|
|
196
212
|
}
|
|
197
213
|
|
|
214
|
+
if (command.includes('rspec')) {
|
|
215
|
+
return 'Auto-run Ruby tests with RSpec';
|
|
216
|
+
}
|
|
217
|
+
|
|
198
218
|
if (command.includes('go test')) {
|
|
199
219
|
return 'Auto-run Go tests for modified files';
|
|
200
220
|
}
|
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: {
|
|
@@ -76,6 +79,31 @@ const TEMPLATES_CONFIG = {
|
|
|
76
79
|
}
|
|
77
80
|
}
|
|
78
81
|
},
|
|
82
|
+
'ruby': {
|
|
83
|
+
name: 'Ruby',
|
|
84
|
+
description: 'Optimized for Ruby development with modern tools',
|
|
85
|
+
files: [
|
|
86
|
+
{ source: 'ruby/CLAUDE.md', destination: 'CLAUDE.md' },
|
|
87
|
+
{ source: 'ruby/.claude', destination: '.claude' },
|
|
88
|
+
{ source: 'ruby/.claude/settings.json', destination: '.claude/settings.json' },
|
|
89
|
+
{ source: 'ruby/.mcp.json', destination: '.mcp.json' }
|
|
90
|
+
],
|
|
91
|
+
frameworks: {
|
|
92
|
+
'rails': {
|
|
93
|
+
name: 'Ruby on Rails 8',
|
|
94
|
+
additionalFiles: [
|
|
95
|
+
{ source: 'ruby/examples/rails-app/.claude/commands', destination: '.claude/commands' },
|
|
96
|
+
{ source: 'ruby/examples/rails-app/CLAUDE.md', destination: 'CLAUDE.md' }
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
'sinatra': {
|
|
100
|
+
name: 'Sinatra',
|
|
101
|
+
additionalFiles: [
|
|
102
|
+
{ source: 'ruby/examples/sinatra-app/.claude/commands', destination: '.claude/commands' }
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
79
107
|
'rust': {
|
|
80
108
|
name: 'Rust',
|
|
81
109
|
description: 'Optimized for Rust development',
|
|
@@ -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();
|
package/src/utils.js
CHANGED
|
@@ -60,6 +60,50 @@ async function detectProject(targetDir) {
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
// Check for Ruby files
|
|
64
|
+
const rubyFiles = await findFilesByExtension(targetDir, ['.rb']);
|
|
65
|
+
const gemfilePath = path.join(targetDir, 'Gemfile');
|
|
66
|
+
const gemfileLockPath = path.join(targetDir, 'Gemfile.lock');
|
|
67
|
+
|
|
68
|
+
if (rubyFiles.length > 0 || await fs.pathExists(gemfilePath)) {
|
|
69
|
+
detectedLanguages.push('ruby');
|
|
70
|
+
|
|
71
|
+
// Check for Ruby frameworks
|
|
72
|
+
if (await fs.pathExists(gemfilePath)) {
|
|
73
|
+
try {
|
|
74
|
+
const gemfile = await fs.readFile(gemfilePath, 'utf-8');
|
|
75
|
+
if (gemfile.includes('rails')) {
|
|
76
|
+
detectedFrameworks.push('rails');
|
|
77
|
+
}
|
|
78
|
+
if (gemfile.includes('sinatra')) {
|
|
79
|
+
detectedFrameworks.push('sinatra');
|
|
80
|
+
}
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.warn('Could not parse Gemfile');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Check for Rails application structure
|
|
87
|
+
const railsAppPath = path.join(targetDir, 'config', 'application.rb');
|
|
88
|
+
const railsRoutesPath = path.join(targetDir, 'config', 'routes.rb');
|
|
89
|
+
if (await fs.pathExists(railsAppPath) || await fs.pathExists(railsRoutesPath)) {
|
|
90
|
+
detectedFrameworks.push('rails');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Check for Rakefile (common in Rails and Ruby projects)
|
|
94
|
+
const rakefilePath = path.join(targetDir, 'Rakefile');
|
|
95
|
+
if (await fs.pathExists(rakefilePath)) {
|
|
96
|
+
try {
|
|
97
|
+
const rakefile = await fs.readFile(rakefilePath, 'utf-8');
|
|
98
|
+
if (rakefile.includes('Rails.application.load_tasks')) {
|
|
99
|
+
detectedFrameworks.push('rails');
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
// Ignore parsing errors
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
63
107
|
// Check for Rust files
|
|
64
108
|
const rustFiles = await findFilesByExtension(targetDir, ['.rs']);
|
|
65
109
|
const cargoPath = path.join(targetDir, 'Cargo.toml');
|
|
@@ -145,6 +189,7 @@ async function getProjectSummary(targetDir) {
|
|
|
145
189
|
hasGit: await fs.pathExists(path.join(targetDir, '.git')),
|
|
146
190
|
hasNodeModules: await fs.pathExists(path.join(targetDir, 'node_modules')),
|
|
147
191
|
hasVenv: await fs.pathExists(path.join(targetDir, 'venv')) || await fs.pathExists(path.join(targetDir, '.venv')),
|
|
192
|
+
hasBundle: await fs.pathExists(path.join(targetDir, 'vendor', 'bundle')),
|
|
148
193
|
configFiles: []
|
|
149
194
|
};
|
|
150
195
|
|
|
@@ -152,6 +197,7 @@ async function getProjectSummary(targetDir) {
|
|
|
152
197
|
const configFiles = [
|
|
153
198
|
'package.json', 'tsconfig.json', 'webpack.config.js', 'vite.config.js',
|
|
154
199
|
'requirements.txt', 'setup.py', 'pyproject.toml', 'Pipfile',
|
|
200
|
+
'Gemfile', 'Gemfile.lock', 'Rakefile', 'config.ru',
|
|
155
201
|
'Cargo.toml', 'go.mod', '.gitignore', 'README.md'
|
|
156
202
|
];
|
|
157
203
|
|