claude-code-templates 1.13.2 → 1.14.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.
- package/README.md +44 -239
- package/components/agents/api-security-audit.md +92 -0
- package/components/agents/database-optimization.md +94 -0
- package/components/agents/react-performance-optimization.md +64 -0
- package/components/commands/check-file.md +53 -0
- package/components/commands/generate-tests.md +68 -0
- package/components/mcps/deepgraph-nextjs.json +12 -0
- package/components/mcps/deepgraph-react.json +12 -0
- package/components/mcps/deepgraph-typescript.json +12 -0
- package/components/mcps/deepgraph-vue.json +12 -0
- package/components/mcps/filesystem-access.json +12 -0
- package/components/mcps/github-integration.json +11 -0
- package/components/mcps/memory-integration.json +8 -0
- package/components/mcps/mysql-integration.json +11 -0
- package/components/mcps/postgresql-integration.json +11 -0
- package/components/mcps/web-fetch.json +8 -0
- package/package.json +2 -1
- package/src/analytics-web/components/AgentsPage.js +33 -16
- package/src/analytics-web/components/DashboardPage.js +35 -105
- package/src/analytics-web/components/HeaderComponent.js +307 -0
- package/src/analytics-web/index.html +10 -27
- package/src/analytics-web/services/DataService.js +13 -0
- package/src/health-check.js +48 -44
- package/src/index.js +54 -2
- package/src/tracking-service.js +188 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HeaderComponent - Unified header for all analytics pages
|
|
3
|
+
* Provides consistent branding, navigation, and controls across the application
|
|
4
|
+
*/
|
|
5
|
+
class HeaderComponent {
|
|
6
|
+
constructor(container, options = {}) {
|
|
7
|
+
this.container = container;
|
|
8
|
+
this.options = {
|
|
9
|
+
title: options.title || 'Claude Code Analytics Dashboard',
|
|
10
|
+
subtitle: options.subtitle || 'Real-time monitoring and analytics for Claude Code sessions',
|
|
11
|
+
showVersionBadge: options.showVersionBadge !== false,
|
|
12
|
+
showLastUpdate: options.showLastUpdate !== false,
|
|
13
|
+
showThemeSwitch: options.showThemeSwitch !== false,
|
|
14
|
+
showGitHubLink: options.showGitHubLink !== false,
|
|
15
|
+
version: options.version || 'v1.13.2', // Default fallback
|
|
16
|
+
customActions: options.customActions || [],
|
|
17
|
+
dataService: options.dataService || null, // DataService for dynamic version loading
|
|
18
|
+
...options
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
this.lastUpdateTime = null;
|
|
22
|
+
this.updateInterval = null;
|
|
23
|
+
this.actualVersion = this.options.version; // Will be updated dynamically
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Render the header component
|
|
28
|
+
*/
|
|
29
|
+
render() {
|
|
30
|
+
const headerHTML = `
|
|
31
|
+
<div class="page-header">
|
|
32
|
+
<div class="header-content">
|
|
33
|
+
<div class="header-left">
|
|
34
|
+
<div class="status-header">
|
|
35
|
+
<span class="session-timer-status-dot active" id="session-status-dot"></span>
|
|
36
|
+
<h1 class="page-title">
|
|
37
|
+
${this.options.title}
|
|
38
|
+
${this.options.showVersionBadge ? `<span class="version-badge" id="version-badge">${this.actualVersion}</span>` : ''}
|
|
39
|
+
</h1>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="page-subtitle">
|
|
42
|
+
${this.options.subtitle}
|
|
43
|
+
</div>
|
|
44
|
+
${this.options.showLastUpdate ? `
|
|
45
|
+
<div class="last-update-header">
|
|
46
|
+
<span class="last-update-label">last update:</span>
|
|
47
|
+
<span id="last-update-header-text">Never</span>
|
|
48
|
+
</div>
|
|
49
|
+
` : ''}
|
|
50
|
+
</div>
|
|
51
|
+
<div class="header-right">
|
|
52
|
+
${this.options.showThemeSwitch ? `
|
|
53
|
+
<div class="theme-switch-container" title="Toggle light/dark theme">
|
|
54
|
+
<div class="theme-switch" id="header-theme-switch">
|
|
55
|
+
<div class="theme-switch-track">
|
|
56
|
+
<div class="theme-switch-thumb" id="header-theme-switch-thumb">
|
|
57
|
+
<span class="theme-switch-icon">🌙</span>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
` : ''}
|
|
63
|
+
${this.options.showGitHubLink ? `
|
|
64
|
+
<a href="https://github.com/davila7/claude-code-templates" target="_blank" class="github-link" title="Star on GitHub">
|
|
65
|
+
<span class="github-icon">⭐</span>
|
|
66
|
+
Star on GitHub
|
|
67
|
+
</a>
|
|
68
|
+
` : ''}
|
|
69
|
+
${this.renderCustomActions()}
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
`;
|
|
74
|
+
|
|
75
|
+
this.container.innerHTML = headerHTML;
|
|
76
|
+
this.bindEvents();
|
|
77
|
+
this.initializeTheme();
|
|
78
|
+
|
|
79
|
+
if (this.options.showLastUpdate) {
|
|
80
|
+
this.updateLastUpdateTime();
|
|
81
|
+
this.startUpdateInterval();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Load version dynamically if DataService is available
|
|
85
|
+
if (this.options.dataService) {
|
|
86
|
+
this.loadVersion();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Render custom actions in header
|
|
92
|
+
*/
|
|
93
|
+
renderCustomActions() {
|
|
94
|
+
if (!this.options.customActions || this.options.customActions.length === 0) {
|
|
95
|
+
return '';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return this.options.customActions.map(action => `
|
|
99
|
+
<button class="header-action-btn" id="${action.id}" title="${action.title || action.label}">
|
|
100
|
+
${action.icon ? `<span class="btn-icon">${action.icon}</span>` : ''}
|
|
101
|
+
${action.label}
|
|
102
|
+
</button>
|
|
103
|
+
`).join('');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Bind event listeners
|
|
108
|
+
*/
|
|
109
|
+
bindEvents() {
|
|
110
|
+
// Theme toggle
|
|
111
|
+
if (this.options.showThemeSwitch) {
|
|
112
|
+
const themeSwitch = this.container.querySelector('#header-theme-switch');
|
|
113
|
+
if (themeSwitch) {
|
|
114
|
+
themeSwitch.addEventListener('click', () => this.toggleTheme());
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Custom action handlers
|
|
119
|
+
this.options.customActions.forEach(action => {
|
|
120
|
+
const btn = this.container.querySelector(`#${action.id}`);
|
|
121
|
+
if (btn && action.handler) {
|
|
122
|
+
btn.addEventListener('click', action.handler);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Initialize theme from localStorage
|
|
129
|
+
*/
|
|
130
|
+
initializeTheme() {
|
|
131
|
+
if (!this.options.showThemeSwitch) return;
|
|
132
|
+
|
|
133
|
+
const savedTheme = localStorage.getItem('claude-analytics-theme') || 'dark';
|
|
134
|
+
const body = document.body;
|
|
135
|
+
const headerThumb = this.container.querySelector('#header-theme-switch-thumb');
|
|
136
|
+
const headerIcon = headerThumb?.querySelector('.theme-switch-icon');
|
|
137
|
+
|
|
138
|
+
body.setAttribute('data-theme', savedTheme);
|
|
139
|
+
if (headerThumb && headerIcon) {
|
|
140
|
+
if (savedTheme === 'light') {
|
|
141
|
+
headerThumb.classList.add('light');
|
|
142
|
+
headerIcon.textContent = '☀️';
|
|
143
|
+
} else {
|
|
144
|
+
headerThumb.classList.remove('light');
|
|
145
|
+
headerIcon.textContent = '🌙';
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Toggle theme between light and dark
|
|
152
|
+
*/
|
|
153
|
+
toggleTheme() {
|
|
154
|
+
const body = document.body;
|
|
155
|
+
const headerThumb = this.container.querySelector('#header-theme-switch-thumb');
|
|
156
|
+
const headerIcon = headerThumb?.querySelector('.theme-switch-icon');
|
|
157
|
+
|
|
158
|
+
// Also sync with global theme switch if exists
|
|
159
|
+
const globalThumb = document.getElementById('themeSwitchThumb');
|
|
160
|
+
const globalIcon = globalThumb?.querySelector('.theme-switch-icon');
|
|
161
|
+
|
|
162
|
+
const isLight = body.getAttribute('data-theme') === 'light';
|
|
163
|
+
const newTheme = isLight ? 'dark' : 'light';
|
|
164
|
+
|
|
165
|
+
body.setAttribute('data-theme', newTheme);
|
|
166
|
+
|
|
167
|
+
// Update header theme switch
|
|
168
|
+
if (headerThumb && headerIcon) {
|
|
169
|
+
headerThumb.classList.toggle('light', newTheme === 'light');
|
|
170
|
+
headerIcon.textContent = newTheme === 'light' ? '☀️' : '🌙';
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Sync with global theme switch
|
|
174
|
+
if (globalThumb && globalIcon) {
|
|
175
|
+
globalThumb.classList.toggle('light', newTheme === 'light');
|
|
176
|
+
globalIcon.textContent = newTheme === 'light' ? '☀️' : '🌙';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
localStorage.setItem('claude-analytics-theme', newTheme);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Update last update time
|
|
184
|
+
*/
|
|
185
|
+
updateLastUpdateTime() {
|
|
186
|
+
if (!this.options.showLastUpdate) return;
|
|
187
|
+
|
|
188
|
+
const currentTime = new Date().toLocaleTimeString();
|
|
189
|
+
const lastUpdateText = this.container.querySelector('#last-update-header-text');
|
|
190
|
+
|
|
191
|
+
if (lastUpdateText) {
|
|
192
|
+
lastUpdateText.textContent = currentTime;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
this.lastUpdateTime = currentTime;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Start periodic update of timestamp
|
|
200
|
+
*/
|
|
201
|
+
startUpdateInterval() {
|
|
202
|
+
if (this.updateInterval) {
|
|
203
|
+
clearInterval(this.updateInterval);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Update every 30 seconds
|
|
207
|
+
this.updateInterval = setInterval(() => {
|
|
208
|
+
this.updateLastUpdateTime();
|
|
209
|
+
}, 30000);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Stop update interval
|
|
214
|
+
*/
|
|
215
|
+
stopUpdateInterval() {
|
|
216
|
+
if (this.updateInterval) {
|
|
217
|
+
clearInterval(this.updateInterval);
|
|
218
|
+
this.updateInterval = null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Load version dynamically from backend
|
|
224
|
+
*/
|
|
225
|
+
async loadVersion() {
|
|
226
|
+
if (!this.options.dataService) return;
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
const versionData = await this.options.dataService.getVersion();
|
|
230
|
+
if (versionData && versionData.version) {
|
|
231
|
+
this.actualVersion = `v${versionData.version}`;
|
|
232
|
+
this.updateVersionBadge();
|
|
233
|
+
}
|
|
234
|
+
} catch (error) {
|
|
235
|
+
console.error('Error loading version:', error);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Update version badge in the DOM
|
|
241
|
+
*/
|
|
242
|
+
updateVersionBadge() {
|
|
243
|
+
const versionBadge = this.container.querySelector('#version-badge');
|
|
244
|
+
if (versionBadge) {
|
|
245
|
+
versionBadge.textContent = this.actualVersion;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Update header title
|
|
251
|
+
* @param {string} title - New title
|
|
252
|
+
*/
|
|
253
|
+
updateTitle(title) {
|
|
254
|
+
this.options.title = title;
|
|
255
|
+
const titleElement = this.container.querySelector('.page-title');
|
|
256
|
+
if (titleElement) {
|
|
257
|
+
titleElement.innerHTML = `
|
|
258
|
+
${title}
|
|
259
|
+
${this.options.showVersionBadge ? `<span class="version-badge" id="version-badge">${this.actualVersion}</span>` : ''}
|
|
260
|
+
`;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Update header subtitle
|
|
266
|
+
* @param {string} subtitle - New subtitle
|
|
267
|
+
*/
|
|
268
|
+
updateSubtitle(subtitle) {
|
|
269
|
+
this.options.subtitle = subtitle;
|
|
270
|
+
const subtitleElement = this.container.querySelector('.page-subtitle');
|
|
271
|
+
if (subtitleElement) {
|
|
272
|
+
subtitleElement.textContent = subtitle;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Update status dot state
|
|
278
|
+
* @param {boolean} active - Whether status should be active
|
|
279
|
+
*/
|
|
280
|
+
updateStatusDot(active) {
|
|
281
|
+
const statusDot = this.container.querySelector('#session-status-dot');
|
|
282
|
+
if (statusDot) {
|
|
283
|
+
statusDot.classList.toggle('active', active);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Get current theme
|
|
289
|
+
* @returns {string} Current theme ('light' or 'dark')
|
|
290
|
+
*/
|
|
291
|
+
getCurrentTheme() {
|
|
292
|
+
return document.body.getAttribute('data-theme') || 'dark';
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Destroy header component
|
|
297
|
+
*/
|
|
298
|
+
destroy() {
|
|
299
|
+
this.stopUpdateInterval();
|
|
300
|
+
this.container.innerHTML = '';
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Export for module use
|
|
305
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
306
|
+
module.exports = HeaderComponent;
|
|
307
|
+
}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
<script src="services/StateService.js"></script>
|
|
13
13
|
|
|
14
14
|
<!-- Component Scripts -->
|
|
15
|
+
<script src="components/HeaderComponent.js"></script>
|
|
15
16
|
<script src="components/Charts.js"></script>
|
|
16
17
|
<!-- Dashboard.js removed - deprecated architecture -->
|
|
17
18
|
<script src="components/SessionTimer.js"></script>
|
|
@@ -2644,6 +2645,7 @@
|
|
|
2644
2645
|
flex: 1;
|
|
2645
2646
|
display: flex;
|
|
2646
2647
|
flex-direction: column;
|
|
2648
|
+
margin-left: 56px;
|
|
2647
2649
|
transition: margin-left 0.3s ease;
|
|
2648
2650
|
}
|
|
2649
2651
|
|
|
@@ -2662,7 +2664,9 @@
|
|
|
2662
2664
|
display: flex;
|
|
2663
2665
|
flex-direction: column;
|
|
2664
2666
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
2665
|
-
position:
|
|
2667
|
+
position: fixed;
|
|
2668
|
+
top: 0;
|
|
2669
|
+
left: 0;
|
|
2666
2670
|
z-index: 1000;
|
|
2667
2671
|
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
|
|
2668
2672
|
overflow: visible;
|
|
@@ -2673,6 +2677,10 @@
|
|
|
2673
2677
|
background: var(--bg-secondary);
|
|
2674
2678
|
border-right: 1px solid var(--text-accent);
|
|
2675
2679
|
box-shadow: 4px 0 16px rgba(213, 116, 85, 0.1);
|
|
2680
|
+
position: fixed;
|
|
2681
|
+
top: 0;
|
|
2682
|
+
left: 0;
|
|
2683
|
+
height: 100vh;
|
|
2676
2684
|
}
|
|
2677
2685
|
|
|
2678
2686
|
.sidebar-header {
|
|
@@ -4616,32 +4624,7 @@
|
|
|
4616
4624
|
margin: 0 auto;
|
|
4617
4625
|
}
|
|
4618
4626
|
|
|
4619
|
-
/* App Layout Styles (App.js + Sidebar.js) */
|
|
4620
|
-
.app {
|
|
4621
|
-
display: flex;
|
|
4622
|
-
min-height: 100vh;
|
|
4623
|
-
background: var(--bg-primary);
|
|
4624
|
-
}
|
|
4625
|
-
|
|
4626
|
-
.app-sidebar {
|
|
4627
|
-
width: 60px;
|
|
4628
|
-
background: var(--bg-secondary);
|
|
4629
|
-
border-right: 1px solid var(--border-primary);
|
|
4630
|
-
transition: width 0.3s ease;
|
|
4631
|
-
}
|
|
4632
|
-
|
|
4633
|
-
.app-main {
|
|
4634
|
-
flex: 1;
|
|
4635
|
-
display: flex;
|
|
4636
|
-
flex-direction: column;
|
|
4637
|
-
min-width: 0;
|
|
4638
|
-
}
|
|
4639
|
-
|
|
4640
|
-
.app-content {
|
|
4641
|
-
flex: 1;
|
|
4642
|
-
padding: 20px;
|
|
4643
|
-
overflow-y: auto;
|
|
4644
|
-
}
|
|
4627
|
+
/* App Layout Styles (App.js + Sidebar.js) - Duplicates removed, using definitions above */
|
|
4645
4628
|
|
|
4646
4629
|
/* Metrics Section */
|
|
4647
4630
|
.metrics-section {
|
|
@@ -409,6 +409,19 @@ class DataService {
|
|
|
409
409
|
this.setupWebSocketIntegration();
|
|
410
410
|
}
|
|
411
411
|
|
|
412
|
+
/**
|
|
413
|
+
* Get application version from backend
|
|
414
|
+
* @returns {Promise<Object>} Version information
|
|
415
|
+
*/
|
|
416
|
+
async getVersion() {
|
|
417
|
+
try {
|
|
418
|
+
return await this.cachedFetch('/api/version', 300000); // Cache for 5 minutes
|
|
419
|
+
} catch (error) {
|
|
420
|
+
console.error('Error fetching version:', error);
|
|
421
|
+
return { version: '1.13.2', name: 'claude-code-templates' }; // Fallback
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
412
425
|
/**
|
|
413
426
|
* Get cache statistics
|
|
414
427
|
* @returns {Object} Cache statistics
|
package/src/health-check.js
CHANGED
|
@@ -394,58 +394,62 @@ class HealthChecker {
|
|
|
394
394
|
}
|
|
395
395
|
|
|
396
396
|
checkAuthentication() {
|
|
397
|
+
const homeDir = os.homedir();
|
|
398
|
+
|
|
399
|
+
// Check for Claude Code OAuth authentication in ~/.claude.json
|
|
400
|
+
const claudeJsonPath = path.join(homeDir, '.claude.json');
|
|
401
|
+
|
|
397
402
|
try {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
403
|
+
if (fs.existsSync(claudeJsonPath)) {
|
|
404
|
+
const claudeConfig = JSON.parse(fs.readFileSync(claudeJsonPath, 'utf8'));
|
|
405
|
+
|
|
406
|
+
// Check for OAuth authentication
|
|
407
|
+
if (claudeConfig.oauthAccount && claudeConfig.oauthAccount.accountUuid) {
|
|
408
|
+
const email = claudeConfig.oauthAccount.emailAddress || 'OAuth user';
|
|
409
|
+
return {
|
|
410
|
+
status: 'pass',
|
|
411
|
+
message: `Authenticated via OAuth (${email})`
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Check for API key authentication
|
|
416
|
+
if (claudeConfig.apiKey) {
|
|
417
|
+
return {
|
|
418
|
+
status: 'pass',
|
|
419
|
+
message: 'Authenticated via API key'
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
}
|
|
404
423
|
|
|
405
|
-
|
|
424
|
+
// Check for environment variable API key
|
|
425
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
406
426
|
return {
|
|
407
427
|
status: 'pass',
|
|
408
|
-
message: 'Authenticated
|
|
428
|
+
message: 'Authenticated via ANTHROPIC_API_KEY environment variable'
|
|
409
429
|
};
|
|
410
430
|
}
|
|
411
431
|
|
|
412
|
-
//
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
for (const authPath of possibleAuthPaths) {
|
|
431
|
-
if (fs.existsSync(authPath)) {
|
|
432
|
-
try {
|
|
433
|
-
const stats = fs.statSync(authPath);
|
|
434
|
-
// Check if file was modified recently (within last 30 days)
|
|
435
|
-
const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
|
|
436
|
-
if (stats.mtime > thirtyDaysAgo) {
|
|
437
|
-
return {
|
|
438
|
-
status: 'pass',
|
|
439
|
-
message: 'Authentication session found'
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
} catch (statError) {
|
|
443
|
-
// Continue to next path
|
|
444
|
-
}
|
|
445
|
-
}
|
|
432
|
+
// Try to check if we can make a simple claude command
|
|
433
|
+
try {
|
|
434
|
+
execSync('claude --version', {
|
|
435
|
+
encoding: 'utf8',
|
|
436
|
+
stdio: 'pipe',
|
|
437
|
+
timeout: 3000
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
return {
|
|
441
|
+
status: 'warn',
|
|
442
|
+
message: 'Claude CLI available but authentication not configured'
|
|
443
|
+
};
|
|
444
|
+
} catch (cliError) {
|
|
445
|
+
return {
|
|
446
|
+
status: 'fail',
|
|
447
|
+
message: 'Claude CLI not available or not authenticated'
|
|
448
|
+
};
|
|
446
449
|
}
|
|
447
450
|
|
|
448
|
-
|
|
451
|
+
} catch (error) {
|
|
452
|
+
// If we can't read the config file, check if CLI is at least installed
|
|
449
453
|
try {
|
|
450
454
|
execSync('claude --version', {
|
|
451
455
|
encoding: 'utf8',
|
|
@@ -460,7 +464,7 @@ class HealthChecker {
|
|
|
460
464
|
} catch (cliError) {
|
|
461
465
|
return {
|
|
462
466
|
status: 'fail',
|
|
463
|
-
message: 'Claude CLI not available or
|
|
467
|
+
message: 'Claude CLI not available or authentication check failed'
|
|
464
468
|
};
|
|
465
469
|
}
|
|
466
470
|
}
|
package/src/index.js
CHANGED
|
@@ -14,6 +14,7 @@ const { runHookStats } = require('./hook-stats');
|
|
|
14
14
|
const { runMCPStats } = require('./mcp-stats');
|
|
15
15
|
const { runAnalytics } = require('./analytics');
|
|
16
16
|
const { runHealthCheck } = require('./health-check');
|
|
17
|
+
const { trackingService } = require('./tracking-service');
|
|
17
18
|
|
|
18
19
|
async function showMainMenu() {
|
|
19
20
|
console.log('');
|
|
@@ -49,12 +50,14 @@ async function showMainMenu() {
|
|
|
49
50
|
|
|
50
51
|
if (initialChoice.action === 'analytics') {
|
|
51
52
|
console.log(chalk.blue('📊 Launching Claude Code Analytics Dashboard...'));
|
|
53
|
+
trackingService.trackAnalyticsDashboard({ page: 'dashboard', source: 'interactive_menu' });
|
|
52
54
|
await runAnalytics({});
|
|
53
55
|
return;
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
if (initialChoice.action === 'chats') {
|
|
57
59
|
console.log(chalk.blue('💬 Launching Claude Code Chats Dashboard...'));
|
|
60
|
+
trackingService.trackAnalyticsDashboard({ page: 'agents', source: 'interactive_menu' });
|
|
58
61
|
await runAnalytics({ openTo: 'agents' });
|
|
59
62
|
return;
|
|
60
63
|
}
|
|
@@ -62,6 +65,13 @@ async function showMainMenu() {
|
|
|
62
65
|
if (initialChoice.action === 'health') {
|
|
63
66
|
console.log(chalk.blue('🔍 Running Health Check...'));
|
|
64
67
|
const healthResult = await runHealthCheck();
|
|
68
|
+
|
|
69
|
+
// Track health check usage
|
|
70
|
+
trackingService.trackHealthCheck({
|
|
71
|
+
setup_recommended: healthResult.runSetup,
|
|
72
|
+
issues_found: healthResult.issues || 0
|
|
73
|
+
});
|
|
74
|
+
|
|
65
75
|
if (healthResult.runSetup) {
|
|
66
76
|
console.log(chalk.blue('⚙️ Starting Project Setup...'));
|
|
67
77
|
// Continue with setup flow
|
|
@@ -116,12 +126,14 @@ async function createClaudeConfig(options = {}) {
|
|
|
116
126
|
|
|
117
127
|
// Handle analytics dashboard
|
|
118
128
|
if (options.analytics) {
|
|
129
|
+
trackingService.trackAnalyticsDashboard({ page: 'dashboard', source: 'command_line' });
|
|
119
130
|
await runAnalytics(options);
|
|
120
131
|
return;
|
|
121
132
|
}
|
|
122
133
|
|
|
123
134
|
// Handle chats/agents dashboard
|
|
124
135
|
if (options.chats || options.agents) {
|
|
136
|
+
trackingService.trackAnalyticsDashboard({ page: 'agents', source: 'command_line' });
|
|
125
137
|
await runAnalytics({ ...options, openTo: 'agents' });
|
|
126
138
|
return;
|
|
127
139
|
}
|
|
@@ -130,6 +142,14 @@ async function createClaudeConfig(options = {}) {
|
|
|
130
142
|
let shouldRunSetup = false;
|
|
131
143
|
if (options.healthCheck || options.health || options.check || options.verify) {
|
|
132
144
|
const healthResult = await runHealthCheck();
|
|
145
|
+
|
|
146
|
+
// Track health check usage
|
|
147
|
+
trackingService.trackHealthCheck({
|
|
148
|
+
setup_recommended: healthResult.runSetup,
|
|
149
|
+
issues_found: healthResult.issues || 0,
|
|
150
|
+
source: 'command_line'
|
|
151
|
+
});
|
|
152
|
+
|
|
133
153
|
if (healthResult.runSetup) {
|
|
134
154
|
console.log(chalk.blue('⚙️ Starting Project Setup...'));
|
|
135
155
|
shouldRunSetup = true;
|
|
@@ -244,8 +264,8 @@ async function createClaudeConfig(options = {}) {
|
|
|
244
264
|
console.log(chalk.white(' 2. Customize the configuration for your project'));
|
|
245
265
|
console.log(chalk.white(' 3. Start using Claude Code with: claude'));
|
|
246
266
|
console.log('');
|
|
247
|
-
console.log(chalk.blue('🌐 View all available templates at: https://
|
|
248
|
-
console.log(chalk.blue('📖 Read the complete documentation at: https://
|
|
267
|
+
console.log(chalk.blue('🌐 View all available templates at: https://aitmpl.com/'));
|
|
268
|
+
console.log(chalk.blue('📖 Read the complete documentation at: https://aitmpl.com/docu/'));
|
|
249
269
|
|
|
250
270
|
if (config.language !== 'common') {
|
|
251
271
|
console.log(chalk.yellow(`💡 Language-specific features for ${config.language} have been configured`));
|
|
@@ -262,6 +282,17 @@ async function createClaudeConfig(options = {}) {
|
|
|
262
282
|
if (config.mcps && config.mcps.length > 0) {
|
|
263
283
|
console.log(chalk.blue(`🔧 ${config.mcps.length} MCP servers have been configured`));
|
|
264
284
|
}
|
|
285
|
+
|
|
286
|
+
// Track successful template installation
|
|
287
|
+
if (!options.agent && !options.command && !options.mcp) {
|
|
288
|
+
trackingService.trackTemplateInstallation(config.language, config.framework, {
|
|
289
|
+
installation_method: options.setupFromMenu ? 'interactive_menu' : 'command_line',
|
|
290
|
+
dry_run: options.dryRun || false,
|
|
291
|
+
hooks_count: config.hooks ? config.hooks.length : 0,
|
|
292
|
+
mcps_count: config.mcps ? config.mcps.length : 0,
|
|
293
|
+
project_detected: !!options.detectedProject
|
|
294
|
+
});
|
|
295
|
+
}
|
|
265
296
|
|
|
266
297
|
// Run post-installation validation
|
|
267
298
|
if (!options.dryRun) {
|
|
@@ -315,6 +346,14 @@ async function installIndividualAgent(agentName, targetDir, options) {
|
|
|
315
346
|
|
|
316
347
|
console.log(chalk.green(`✅ Agent "${agentName}" installed successfully!`));
|
|
317
348
|
|
|
349
|
+
// Track successful agent installation
|
|
350
|
+
trackingService.trackDownload('agent', agentName, {
|
|
351
|
+
language: language,
|
|
352
|
+
framework: framework,
|
|
353
|
+
installation_type: 'individual_agent',
|
|
354
|
+
template_installed: true
|
|
355
|
+
});
|
|
356
|
+
|
|
318
357
|
} catch (error) {
|
|
319
358
|
console.log(chalk.red(`❌ Error installing agent: ${error.message}`));
|
|
320
359
|
}
|
|
@@ -353,6 +392,12 @@ async function installIndividualCommand(commandName, targetDir, options) {
|
|
|
353
392
|
console.log(chalk.green(`✅ Command "${commandName}" installed successfully!`));
|
|
354
393
|
console.log(chalk.cyan(`📁 Installed to: ${path.relative(targetDir, targetFile)}`));
|
|
355
394
|
|
|
395
|
+
// Track successful command installation
|
|
396
|
+
trackingService.trackDownload('command', commandName, {
|
|
397
|
+
installation_type: 'individual_command',
|
|
398
|
+
target_directory: path.relative(process.cwd(), targetDir)
|
|
399
|
+
});
|
|
400
|
+
|
|
356
401
|
} catch (error) {
|
|
357
402
|
console.log(chalk.red(`❌ Error installing command: ${error.message}`));
|
|
358
403
|
}
|
|
@@ -404,6 +449,13 @@ async function installIndividualMCP(mcpName, targetDir, options) {
|
|
|
404
449
|
console.log(chalk.green(`✅ MCP "${mcpName}" installed successfully!`));
|
|
405
450
|
console.log(chalk.cyan(`📁 Configuration merged into: ${path.relative(targetDir, targetMcpFile)}`));
|
|
406
451
|
|
|
452
|
+
// Track successful MCP installation
|
|
453
|
+
trackingService.trackDownload('mcp', mcpName, {
|
|
454
|
+
installation_type: 'individual_mcp',
|
|
455
|
+
merged_with_existing: existingConfig !== null,
|
|
456
|
+
servers_count: Object.keys(mergedConfig.mcpServers || {}).length
|
|
457
|
+
});
|
|
458
|
+
|
|
407
459
|
} catch (error) {
|
|
408
460
|
console.log(chalk.red(`❌ Error installing MCP: ${error.message}`));
|
|
409
461
|
}
|