claude-code-templates 1.12.2 → 1.13.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.
@@ -0,0 +1,710 @@
1
+ /**
2
+ * AgentAnalytics - Component for displaying agent usage analytics and charts
3
+ * Shows comprehensive agent usage statistics, trends, and workflow patterns
4
+ */
5
+ class AgentAnalytics {
6
+ constructor(container, services) {
7
+ this.container = container;
8
+ this.dataService = services.data;
9
+ this.stateService = services.state;
10
+
11
+ this.agentData = null;
12
+ this.charts = {};
13
+ this.isInitialized = false;
14
+
15
+ // Date filter state
16
+ this.dateFilters = {
17
+ startDate: null,
18
+ endDate: null
19
+ };
20
+
21
+ // Subscribe to data refresh events
22
+ this.dataService.onDataRefresh(this.handleDataRefresh.bind(this));
23
+ }
24
+
25
+ /**
26
+ * Initialize the agent analytics component
27
+ */
28
+ async initialize() {
29
+ if (this.isInitialized) return;
30
+
31
+ try {
32
+ this.stateService.setLoading(true);
33
+ await this.render();
34
+ await this.loadAgentData();
35
+ this.setupEventListeners();
36
+ this.isInitialized = true;
37
+ } catch (error) {
38
+ console.error('Error initializing agent analytics:', error);
39
+ this.stateService.setError(error);
40
+ } finally {
41
+ this.stateService.setLoading(false);
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Render the agent analytics UI
47
+ */
48
+ async render() {
49
+ this.container.innerHTML = `
50
+ <div class="agent-analytics">
51
+ <!-- Header Section -->
52
+ <div class="analytics-header">
53
+ <div class="header-content">
54
+ <h2 class="analytics-title">
55
+ <span class="title-icon">🤖</span>
56
+ Agent Usage Analytics
57
+ </h2>
58
+ <p class="analytics-subtitle">Specialized Claude Code agent usage patterns and workflow insights</p>
59
+ </div>
60
+
61
+ <!-- Date Filter Controls -->
62
+ <div class="date-filters">
63
+ <div class="filter-group">
64
+ <label for="start-date">From:</label>
65
+ <input type="date" id="start-date" class="date-input">
66
+ </div>
67
+ <div class="filter-group">
68
+ <label for="end-date">To:</label>
69
+ <input type="date" id="end-date" class="date-input">
70
+ </div>
71
+ <button class="refresh-btn" id="refresh-analytics">
72
+ <span class="btn-icon">🔄</span>
73
+ Refresh
74
+ </button>
75
+ </div>
76
+ </div>
77
+
78
+ <!-- Summary Metrics -->
79
+ <div class="metrics-grid" id="metrics-grid">
80
+ <div class="metric-card loading">
81
+ <div class="metric-icon">🔄</div>
82
+ <div class="metric-content">
83
+ <div class="metric-value">Loading...</div>
84
+ <div class="metric-label">Agent Data</div>
85
+ </div>
86
+ </div>
87
+ </div>
88
+
89
+ <!-- Charts Section -->
90
+ <div class="charts-container">
91
+ <!-- Agent Usage Overview -->
92
+ <div class="chart-card">
93
+ <div class="chart-header">
94
+ <h3>Agent Usage Distribution</h3>
95
+ <p>Total invocations per specialized agent type</p>
96
+ </div>
97
+ <div class="chart-content">
98
+ <canvas id="agent-usage-chart" width="400" height="200"></canvas>
99
+ </div>
100
+ </div>
101
+
102
+ <!-- Usage Timeline -->
103
+ <div class="chart-card">
104
+ <div class="chart-header">
105
+ <h3>Agent Usage Timeline</h3>
106
+ <p>Agent invocations over time showing workflow patterns</p>
107
+ </div>
108
+ <div class="chart-content">
109
+ <canvas id="agent-timeline-chart" width="400" height="200"></canvas>
110
+ </div>
111
+ </div>
112
+
113
+ <!-- Hourly Usage Pattern -->
114
+ <div class="chart-card">
115
+ <div class="chart-header">
116
+ <h3>Popular Usage Hours</h3>
117
+ <p>Agent activity distribution throughout the day</p>
118
+ </div>
119
+ <div class="chart-content">
120
+ <canvas id="hourly-usage-chart" width="400" height="200"></canvas>
121
+ </div>
122
+ </div>
123
+
124
+ <!-- Agent Efficiency Metrics -->
125
+ <div class="chart-card">
126
+ <div class="chart-header">
127
+ <h3>Agent Workflow Efficiency</h3>
128
+ <p>Usage patterns and adoption rates across different agents</p>
129
+ </div>
130
+ <div class="chart-content">
131
+ <div class="efficiency-metrics" id="efficiency-metrics">
132
+ <div class="efficiency-loading">Loading efficiency data...</div>
133
+ </div>
134
+ </div>
135
+ </div>
136
+ </div>
137
+
138
+ <!-- Detailed Agent Stats -->
139
+ <div class="agent-details-section">
140
+ <h3>Detailed Agent Statistics</h3>
141
+ <div class="agent-stats-grid" id="agent-stats-grid">
142
+ <div class="stats-loading">Loading agent statistics...</div>
143
+ </div>
144
+ </div>
145
+
146
+ <!-- Workflow Patterns -->
147
+ <div class="workflow-patterns-section" id="workflow-patterns-section" style="display: none;">
148
+ <h3>Agent Workflow Patterns</h3>
149
+ <p>Common sequences of agent usage within workflow sessions</p>
150
+ <div class="workflow-patterns" id="workflow-patterns">
151
+ </div>
152
+ </div>
153
+ </div>
154
+ `;
155
+ }
156
+
157
+ /**
158
+ * Load agent analytics data from API
159
+ */
160
+ async loadAgentData() {
161
+ try {
162
+ const params = new URLSearchParams();
163
+ if (this.dateFilters.startDate) {
164
+ params.append('startDate', this.dateFilters.startDate);
165
+ }
166
+ if (this.dateFilters.endDate) {
167
+ params.append('endDate', this.dateFilters.endDate);
168
+ }
169
+
170
+ const url = `/api/agents${params.toString() ? '?' + params.toString() : ''}`;
171
+ this.agentData = await this.dataService.cachedFetch(url);
172
+
173
+ if (this.agentData) {
174
+ this.updateMetrics();
175
+ this.renderCharts();
176
+ this.renderAgentStats();
177
+ this.renderWorkflowPatterns();
178
+ } else {
179
+ this.renderNoData();
180
+ }
181
+ } catch (error) {
182
+ console.error('Error loading agent data:', error);
183
+ this.renderError(error);
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Update summary metrics
189
+ */
190
+ updateMetrics() {
191
+ const metricsGrid = this.container.querySelector('#metrics-grid');
192
+ if (!metricsGrid || !this.agentData) return;
193
+
194
+ const { summary, totalAgentInvocations, totalAgentTypes, efficiency } = this.agentData;
195
+
196
+ metricsGrid.innerHTML = `
197
+ <div class="metric-card primary">
198
+ <div class="metric-icon">🚀</div>
199
+ <div class="metric-content">
200
+ <div class="metric-value">${totalAgentInvocations}</div>
201
+ <div class="metric-label">Total Invocations</div>
202
+ </div>
203
+ </div>
204
+
205
+ <div class="metric-card secondary">
206
+ <div class="metric-icon">🤖</div>
207
+ <div class="metric-content">
208
+ <div class="metric-value">${totalAgentTypes}</div>
209
+ <div class="metric-label">Agent Types Used</div>
210
+ </div>
211
+ </div>
212
+
213
+ <div class="metric-card success">
214
+ <div class="metric-icon">⭐</div>
215
+ <div class="metric-content">
216
+ <div class="metric-value">${efficiency.adoptionRate}%</div>
217
+ <div class="metric-label">Adoption Rate</div>
218
+ </div>
219
+ </div>
220
+
221
+ <div class="metric-card info">
222
+ <div class="metric-icon">📊</div>
223
+ <div class="metric-content">
224
+ <div class="metric-value">${efficiency.averageInvocationsPerAgent}</div>
225
+ <div class="metric-label">Avg. per Agent</div>
226
+ </div>
227
+ </div>
228
+ `;
229
+ }
230
+
231
+ /**
232
+ * Render all charts
233
+ */
234
+ renderCharts() {
235
+ if (!this.agentData) return;
236
+
237
+ this.renderAgentUsageChart();
238
+ this.renderTimelineChart();
239
+ this.renderHourlyUsageChart();
240
+ this.renderEfficiencyMetrics();
241
+ }
242
+
243
+ /**
244
+ * Render agent usage distribution chart
245
+ */
246
+ renderAgentUsageChart() {
247
+ const canvas = this.container.querySelector('#agent-usage-chart');
248
+ if (!canvas || !this.agentData.agentStats) return;
249
+
250
+ // Destroy existing chart
251
+ if (this.charts.usage) {
252
+ this.charts.usage.destroy();
253
+ }
254
+
255
+ const ctx = canvas.getContext('2d');
256
+ const agentStats = this.agentData.agentStats;
257
+
258
+ this.charts.usage = new Chart(ctx, {
259
+ type: 'doughnut',
260
+ data: {
261
+ labels: agentStats.map(agent => agent.name),
262
+ datasets: [{
263
+ data: agentStats.map(agent => agent.totalInvocations),
264
+ backgroundColor: agentStats.map(agent => agent.color),
265
+ borderColor: 'var(--bg-primary)',
266
+ borderWidth: 2,
267
+ hoverBorderWidth: 3
268
+ }]
269
+ },
270
+ options: {
271
+ responsive: true,
272
+ maintainAspectRatio: false,
273
+ plugins: {
274
+ legend: {
275
+ position: 'bottom',
276
+ labels: {
277
+ color: 'var(--text-primary)',
278
+ padding: 20,
279
+ usePointStyle: true,
280
+ font: {
281
+ family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
282
+ }
283
+ }
284
+ },
285
+ tooltip: {
286
+ titleFont: {
287
+ family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
288
+ },
289
+ bodyFont: {
290
+ family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
291
+ },
292
+ callbacks: {
293
+ label: function(context) {
294
+ const agent = agentStats[context.dataIndex];
295
+ return `${agent.name}: ${context.parsed} invocations (${agent.uniqueConversations} conversations)`;
296
+ }
297
+ }
298
+ }
299
+ }
300
+ }
301
+ });
302
+ }
303
+
304
+ /**
305
+ * Render agent usage timeline chart
306
+ */
307
+ renderTimelineChart() {
308
+ const canvas = this.container.querySelector('#agent-timeline-chart');
309
+ if (!canvas || !this.agentData.usageByDay) return;
310
+
311
+ // Destroy existing chart
312
+ if (this.charts.timeline) {
313
+ this.charts.timeline.destroy();
314
+ }
315
+
316
+ const ctx = canvas.getContext('2d');
317
+ const timelineData = this.agentData.usageByDay;
318
+
319
+ this.charts.timeline = new Chart(ctx, {
320
+ type: 'line',
321
+ data: {
322
+ labels: timelineData.map(d => new Date(d.date).toLocaleDateString()),
323
+ datasets: [{
324
+ label: 'Agent Invocations',
325
+ data: timelineData.map(d => d.count),
326
+ borderColor: '#3fb950',
327
+ backgroundColor: 'rgba(63, 185, 80, 0.1)',
328
+ borderWidth: 2,
329
+ fill: true,
330
+ tension: 0.3,
331
+ pointBackgroundColor: '#3fb950',
332
+ pointBorderColor: '#ffffff',
333
+ pointBorderWidth: 2,
334
+ pointRadius: 4,
335
+ pointHoverRadius: 6
336
+ }]
337
+ },
338
+ options: {
339
+ responsive: true,
340
+ maintainAspectRatio: false,
341
+ plugins: {
342
+ legend: {
343
+ labels: {
344
+ color: 'var(--text-primary)',
345
+ font: {
346
+ family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
347
+ }
348
+ }
349
+ },
350
+ tooltip: {
351
+ titleFont: {
352
+ family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
353
+ },
354
+ bodyFont: {
355
+ family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
356
+ }
357
+ }
358
+ },
359
+ scales: {
360
+ x: {
361
+ ticks: {
362
+ color: 'var(--text-secondary)',
363
+ font: {
364
+ family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
365
+ }
366
+ },
367
+ grid: {
368
+ color: 'var(--border-primary)'
369
+ }
370
+ },
371
+ y: {
372
+ beginAtZero: true,
373
+ ticks: {
374
+ color: 'var(--text-secondary)',
375
+ font: {
376
+ family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
377
+ }
378
+ },
379
+ grid: {
380
+ color: 'var(--border-primary)'
381
+ }
382
+ }
383
+ }
384
+ }
385
+ });
386
+ }
387
+
388
+ /**
389
+ * Render hourly usage pattern chart
390
+ */
391
+ renderHourlyUsageChart() {
392
+ const canvas = this.container.querySelector('#hourly-usage-chart');
393
+ if (!canvas || !this.agentData.popularHours) return;
394
+
395
+ // Destroy existing chart
396
+ if (this.charts.hourly) {
397
+ this.charts.hourly.destroy();
398
+ }
399
+
400
+ const ctx = canvas.getContext('2d');
401
+ const hourlyData = this.agentData.popularHours;
402
+
403
+ this.charts.hourly = new Chart(ctx, {
404
+ type: 'bar',
405
+ data: {
406
+ labels: hourlyData.map(h => h.label),
407
+ datasets: [{
408
+ label: 'Agent Invocations',
409
+ data: hourlyData.map(h => h.count),
410
+ backgroundColor: 'rgba(217, 116, 85, 0.6)',
411
+ borderColor: '#d57455',
412
+ borderWidth: 1,
413
+ borderRadius: 4
414
+ }]
415
+ },
416
+ options: {
417
+ responsive: true,
418
+ maintainAspectRatio: false,
419
+ plugins: {
420
+ legend: {
421
+ labels: {
422
+ color: 'var(--text-primary)',
423
+ font: {
424
+ family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
425
+ }
426
+ }
427
+ },
428
+ tooltip: {
429
+ titleFont: {
430
+ family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
431
+ },
432
+ bodyFont: {
433
+ family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
434
+ }
435
+ }
436
+ },
437
+ scales: {
438
+ x: {
439
+ ticks: {
440
+ color: 'var(--text-secondary)',
441
+ font: {
442
+ family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
443
+ }
444
+ },
445
+ grid: {
446
+ color: 'var(--border-primary)'
447
+ }
448
+ },
449
+ y: {
450
+ beginAtZero: true,
451
+ ticks: {
452
+ color: 'var(--text-secondary)',
453
+ font: {
454
+ family: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
455
+ }
456
+ },
457
+ grid: {
458
+ color: 'var(--border-primary)'
459
+ }
460
+ }
461
+ }
462
+ }
463
+ });
464
+ }
465
+
466
+ /**
467
+ * Render efficiency metrics
468
+ */
469
+ renderEfficiencyMetrics() {
470
+ const container = this.container.querySelector('#efficiency-metrics');
471
+ if (!container || !this.agentData.efficiency) return;
472
+
473
+ const { efficiency, agentStats } = this.agentData;
474
+ const mostUsedAgent = efficiency.mostUsedAgent;
475
+
476
+ container.innerHTML = `
477
+ <div class="efficiency-grid">
478
+ <div class="efficiency-card">
479
+ <div class="efficiency-icon">🎯</div>
480
+ <div class="efficiency-content">
481
+ <div class="efficiency-value">${efficiency.averageInvocationsPerAgent}</div>
482
+ <div class="efficiency-label">Avg. Invocations/Agent</div>
483
+ </div>
484
+ </div>
485
+
486
+ <div class="efficiency-card">
487
+ <div class="efficiency-icon">📈</div>
488
+ <div class="efficiency-content">
489
+ <div class="efficiency-value">${efficiency.adoptionRate}%</div>
490
+ <div class="efficiency-label">Adoption Rate</div>
491
+ </div>
492
+ </div>
493
+
494
+ <div class="efficiency-card">
495
+ <div class="efficiency-icon">${mostUsedAgent ? mostUsedAgent.icon : '🤖'}</div>
496
+ <div class="efficiency-content">
497
+ <div class="efficiency-value">${mostUsedAgent ? mostUsedAgent.name : 'None'}</div>
498
+ <div class="efficiency-label">Most Used Agent</div>
499
+ </div>
500
+ </div>
501
+
502
+ <div class="efficiency-card">
503
+ <div class="efficiency-icon">🔧</div>
504
+ <div class="efficiency-content">
505
+ <div class="efficiency-value">${efficiency.agentDiversity}</div>
506
+ <div class="efficiency-label">Agent Diversity</div>
507
+ </div>
508
+ </div>
509
+ </div>
510
+ `;
511
+ }
512
+
513
+ /**
514
+ * Render detailed agent statistics
515
+ */
516
+ renderAgentStats() {
517
+ const container = this.container.querySelector('#agent-stats-grid');
518
+ if (!container || !this.agentData.agentStats) return;
519
+
520
+ container.innerHTML = this.agentData.agentStats.map(agent => `
521
+ <div class="agent-stat-card">
522
+ <div class="agent-stat-header">
523
+ <div class="agent-stat-icon">${agent.icon}</div>
524
+ <div class="agent-stat-info">
525
+ <h4>${agent.name}</h4>
526
+ <p>${agent.description}</p>
527
+ </div>
528
+ <div class="agent-stat-badge" style="background-color: ${agent.color}20; color: ${agent.color};">
529
+ ${agent.totalInvocations} uses
530
+ </div>
531
+ </div>
532
+
533
+ <div class="agent-stat-metrics">
534
+ <div class="stat-metric">
535
+ <span class="metric-label">Conversations:</span>
536
+ <span class="metric-value">${agent.uniqueConversations}</span>
537
+ </div>
538
+ <div class="stat-metric">
539
+ <span class="metric-label">Avg. per conversation:</span>
540
+ <span class="metric-value">${agent.averageUsagePerConversation}</span>
541
+ </div>
542
+ <div class="stat-metric">
543
+ <span class="metric-label">First used:</span>
544
+ <span class="metric-value">${new Date(agent.firstUsed).toLocaleDateString()}</span>
545
+ </div>
546
+ <div class="stat-metric">
547
+ <span class="metric-label">Last used:</span>
548
+ <span class="metric-value">${new Date(agent.lastUsed).toLocaleDateString()}</span>
549
+ </div>
550
+ </div>
551
+ </div>
552
+ `).join('');
553
+ }
554
+
555
+ /**
556
+ * Render workflow patterns
557
+ */
558
+ renderWorkflowPatterns() {
559
+ const section = this.container.querySelector('#workflow-patterns-section');
560
+ const container = this.container.querySelector('#workflow-patterns');
561
+
562
+ if (!container || !this.agentData.workflowPatterns) return;
563
+
564
+ if (this.agentData.workflowPatterns.length === 0) {
565
+ section.style.display = 'none';
566
+ return;
567
+ }
568
+
569
+ section.style.display = 'block';
570
+ container.innerHTML = this.agentData.workflowPatterns.map(pattern => `
571
+ <div class="workflow-pattern">
572
+ <div class="pattern-flow">${pattern.pattern}</div>
573
+ <div class="pattern-count">${pattern.count} times</div>
574
+ </div>
575
+ `).join('');
576
+ }
577
+
578
+ /**
579
+ * Render no data state
580
+ */
581
+ renderNoData() {
582
+ this.container.innerHTML = `
583
+ <div class="no-agent-data">
584
+ <div class="no-data-content">
585
+ <div class="no-data-icon">🤖</div>
586
+ <h3>No Agent Usage Data</h3>
587
+ <p>No specialized Claude Code agents have been used yet.</p>
588
+ <div class="agent-types">
589
+ <h4>Available Agent Types:</h4>
590
+ <ul>
591
+ <li><strong>general-purpose:</strong> Multi-step tasks and research</li>
592
+ <li><strong>claude-code-best-practices:</strong> Workflow optimization</li>
593
+ <li><strong>docusaurus-expert:</strong> Documentation management</li>
594
+ </ul>
595
+ </div>
596
+ <button class="refresh-btn" onclick="this.loadAgentData()">
597
+ <span class="btn-icon">🔄</span>
598
+ Refresh Data
599
+ </button>
600
+ </div>
601
+ </div>
602
+ `;
603
+ }
604
+
605
+ /**
606
+ * Render error state
607
+ */
608
+ renderError(error) {
609
+ this.container.innerHTML = `
610
+ <div class="agent-analytics-error">
611
+ <div class="error-content">
612
+ <div class="error-icon">⚠️</div>
613
+ <h3>Error Loading Agent Data</h3>
614
+ <p>${error.message || 'Failed to load agent analytics data'}</p>
615
+ <button class="refresh-btn" onclick="this.loadAgentData()">
616
+ <span class="btn-icon">🔄</span>
617
+ Try Again
618
+ </button>
619
+ </div>
620
+ </div>
621
+ `;
622
+ }
623
+
624
+ /**
625
+ * Setup event listeners
626
+ */
627
+ setupEventListeners() {
628
+ // Date filter change handlers
629
+ const startDateInput = this.container.querySelector('#start-date');
630
+ const endDateInput = this.container.querySelector('#end-date');
631
+ const refreshBtn = this.container.querySelector('#refresh-analytics');
632
+
633
+ if (startDateInput) {
634
+ startDateInput.addEventListener('change', (e) => {
635
+ this.dateFilters.startDate = e.target.value;
636
+ });
637
+ }
638
+
639
+ if (endDateInput) {
640
+ endDateInput.addEventListener('change', (e) => {
641
+ this.dateFilters.endDate = e.target.value;
642
+ });
643
+ }
644
+
645
+ if (refreshBtn) {
646
+ refreshBtn.addEventListener('click', () => {
647
+ this.refreshData();
648
+ });
649
+ }
650
+ }
651
+
652
+ /**
653
+ * Refresh analytics data
654
+ */
655
+ async refreshData() {
656
+ try {
657
+ const refreshBtn = this.container.querySelector('#refresh-analytics');
658
+ if (refreshBtn) {
659
+ refreshBtn.disabled = true;
660
+ const icon = refreshBtn.querySelector('.btn-icon');
661
+ if (icon) icon.style.animation = 'spin 1s linear infinite';
662
+ }
663
+
664
+ await this.loadAgentData();
665
+ } catch (error) {
666
+ console.error('Error refreshing agent data:', error);
667
+ } finally {
668
+ const refreshBtn = this.container.querySelector('#refresh-analytics');
669
+ if (refreshBtn) {
670
+ refreshBtn.disabled = false;
671
+ const icon = refreshBtn.querySelector('.btn-icon');
672
+ if (icon) icon.style.animation = '';
673
+ }
674
+ }
675
+ }
676
+
677
+ /**
678
+ * Handle data refresh events
679
+ */
680
+ handleDataRefresh(data, source) {
681
+ if (source === 'agents' || source === 'all') {
682
+ this.loadAgentData();
683
+ }
684
+ }
685
+
686
+ /**
687
+ * Destroy the component and cleanup
688
+ */
689
+ destroy() {
690
+ // Destroy charts
691
+ Object.values(this.charts).forEach(chart => {
692
+ if (chart && typeof chart.destroy === 'function') {
693
+ chart.destroy();
694
+ }
695
+ });
696
+ this.charts = {};
697
+
698
+ // Clear container
699
+ if (this.container) {
700
+ this.container.innerHTML = '';
701
+ }
702
+
703
+ this.isInitialized = false;
704
+ }
705
+ }
706
+
707
+ // Export for use in other modules
708
+ if (typeof module !== 'undefined' && module.exports) {
709
+ module.exports = AgentAnalytics;
710
+ }