claude-code-templates 1.12.1 → 1.13.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/src/analytics.js CHANGED
@@ -10,6 +10,7 @@ const ProcessDetector = require('./analytics/core/ProcessDetector');
10
10
  const ConversationAnalyzer = require('./analytics/core/ConversationAnalyzer');
11
11
  const FileWatcher = require('./analytics/core/FileWatcher');
12
12
  const SessionAnalyzer = require('./analytics/core/SessionAnalyzer');
13
+ const AgentAnalyzer = require('./analytics/core/AgentAnalyzer');
13
14
  const DataCache = require('./analytics/data/DataCache');
14
15
  const WebSocketServer = require('./analytics/notifications/WebSocketServer');
15
16
  const NotificationManager = require('./analytics/notifications/NotificationManager');
@@ -24,6 +25,7 @@ class ClaudeAnalytics {
24
25
  this.processDetector = new ProcessDetector();
25
26
  this.fileWatcher = new FileWatcher();
26
27
  this.sessionAnalyzer = new SessionAnalyzer();
28
+ this.agentAnalyzer = new AgentAnalyzer();
27
29
  this.dataCache = new DataCache();
28
30
  this.performanceMonitor = new PerformanceMonitor({
29
31
  enabled: true,
@@ -667,6 +669,28 @@ class ClaudeAnalytics {
667
669
  }
668
670
  });
669
671
 
672
+ // Agent usage analytics endpoint
673
+ this.app.get('/api/agents', async (req, res) => {
674
+ try {
675
+ const startDate = req.query.startDate;
676
+ const endDate = req.query.endDate;
677
+ const dateRange = (startDate || endDate) ? { startDate, endDate } : null;
678
+
679
+ const agentAnalysis = await this.agentAnalyzer.analyzeAgentUsage(this.data.conversations, dateRange);
680
+ const agentSummary = this.agentAnalyzer.generateSummary(agentAnalysis);
681
+
682
+ res.json({
683
+ ...agentAnalysis,
684
+ summary: agentSummary,
685
+ dateRange,
686
+ timestamp: new Date().toISOString()
687
+ });
688
+ } catch (error) {
689
+ console.error('Error getting agent analytics:', error);
690
+ res.status(500).json({ error: 'Failed to get agent analytics' });
691
+ }
692
+ });
693
+
670
694
  this.app.get('/api/realtime', async (req, res) => {
671
695
  const realtimeWithTimestamp = {
672
696
  ...this.data.realtimeStats,
@@ -1661,12 +1685,88 @@ class ClaudeAnalytics {
1661
1685
  const timeSinceLastUpdate = now - lastUpdate;
1662
1686
  const timeSinceLastUpdateMinutes = Math.floor(timeSinceLastUpdate / (1000 * 60));
1663
1687
 
1664
- // Based on observed pattern: ~2 hours and 21 minutes session limit
1665
- const sessionLimitMs = 2 * 60 * 60 * 1000 + 21 * 60 * 1000; // 2h 21m
1666
- const timeRemaining = sessionLimitMs - sessionDuration;
1667
- const timeRemainingMinutes = Math.floor(timeRemaining / (1000 * 60));
1668
- const timeRemainingHours = Math.floor(timeRemainingMinutes / 60);
1669
- const remainingMinutesDisplay = timeRemainingMinutes % 60;
1688
+ // CORRECTED: Calculate next reset time based on scheduled reset hours
1689
+ // Claude sessions reset at specific times, not fixed durations
1690
+ const resetHours = [1, 7, 13, 19]; // 1am, 7am, 1pm, 7pm local time
1691
+ const sessionStartDate = new Date(startTime);
1692
+
1693
+ // Find next reset time after session start
1694
+ let nextResetTime = new Date(sessionStartDate);
1695
+ nextResetTime.setMinutes(0, 0, 0); // Set to exact hour
1696
+
1697
+ // Find the next reset hour
1698
+ let foundReset = false;
1699
+ for (let i = 0; i < resetHours.length * 2; i++) { // Check up to 2 full days
1700
+ const currentDay = Math.floor(i / resetHours.length);
1701
+ const hourIndex = i % resetHours.length;
1702
+ const resetHour = resetHours[hourIndex];
1703
+
1704
+ const testResetTime = new Date(sessionStartDate);
1705
+ testResetTime.setDate(testResetTime.getDate() + currentDay);
1706
+ testResetTime.setHours(resetHour, 0, 0, 0);
1707
+
1708
+ if (testResetTime > sessionStartDate) {
1709
+ nextResetTime = testResetTime;
1710
+ foundReset = true;
1711
+ break;
1712
+ }
1713
+ }
1714
+
1715
+ if (!foundReset) {
1716
+ // Fallback: assume next day 7am if no pattern found
1717
+ nextResetTime.setDate(nextResetTime.getDate() + 1);
1718
+ nextResetTime.setHours(7, 0, 0, 0);
1719
+ }
1720
+
1721
+ const sessionLimitMs = nextResetTime.getTime() - startTime;
1722
+ let timeRemaining = nextResetTime.getTime() - now;
1723
+ let timeRemainingMinutes = Math.floor(timeRemaining / (1000 * 60));
1724
+ let timeRemainingHours = Math.floor(timeRemainingMinutes / 60);
1725
+ let remainingMinutesDisplay = timeRemainingMinutes % 60;
1726
+
1727
+ // If session is expired but has recent activity, calculate next reset time
1728
+ if (timeRemaining <= 0) {
1729
+ const RECENT_ACTIVITY_THRESHOLD = 5 * 60 * 1000; // 5 minutes
1730
+ const timeSinceLastUpdate = now - lastUpdate;
1731
+
1732
+ if (timeSinceLastUpdate < RECENT_ACTIVITY_THRESHOLD) {
1733
+ // Session was renewed, find the NEXT reset time
1734
+ let nextNextResetTime = new Date(nextResetTime);
1735
+ let foundNextReset = false;
1736
+
1737
+ for (let i = 0; i < resetHours.length; i++) {
1738
+ const resetHour = resetHours[i];
1739
+ const testResetTime = new Date(nextResetTime);
1740
+
1741
+ if (resetHour > nextResetTime.getHours()) {
1742
+ // Same day, later hour
1743
+ testResetTime.setHours(resetHour, 0, 0, 0);
1744
+ } else {
1745
+ // Next day
1746
+ testResetTime.setDate(testResetTime.getDate() + 1);
1747
+ testResetTime.setHours(resetHour, 0, 0, 0);
1748
+ }
1749
+
1750
+ if (testResetTime > nextResetTime) {
1751
+ nextNextResetTime = testResetTime;
1752
+ foundNextReset = true;
1753
+ break;
1754
+ }
1755
+ }
1756
+
1757
+ if (!foundNextReset) {
1758
+ // Default to next day 1am
1759
+ nextNextResetTime.setDate(nextNextResetTime.getDate() + 1);
1760
+ nextNextResetTime.setHours(1, 0, 0, 0);
1761
+ }
1762
+
1763
+ timeRemaining = nextNextResetTime.getTime() - now;
1764
+ timeRemainingMinutes = Math.floor(timeRemaining / (1000 * 60));
1765
+ timeRemainingHours = Math.floor(timeRemainingMinutes / 60);
1766
+ remainingMinutesDisplay = timeRemainingMinutes % 60;
1767
+ nextResetTime = nextNextResetTime;
1768
+ }
1769
+ }
1670
1770
 
1671
1771
  return {
1672
1772
  hasSession: true,
@@ -1691,13 +1791,15 @@ class ClaudeAnalytics {
1691
1791
  hours: timeRemainingHours,
1692
1792
  remainingMinutes: remainingMinutesDisplay,
1693
1793
  formatted: timeRemaining > 0 ? `${timeRemainingHours}h ${remainingMinutesDisplay}m` : 'Session expired',
1694
- isExpired: timeRemaining <= 0
1794
+ isExpired: timeRemaining <= 0 && timeSinceLastUpdate >= (5 * 60 * 1000) // Expired only if past reset time AND no recent activity
1695
1795
  },
1696
1796
  sessionLimit: {
1697
1797
  ms: sessionLimitMs,
1698
- hours: 2,
1699
- minutes: 21,
1700
- formatted: '2h 21m'
1798
+ hours: Math.floor(sessionLimitMs / (1000 * 60 * 60)),
1799
+ minutes: Math.floor((sessionLimitMs % (1000 * 60 * 60)) / (1000 * 60)),
1800
+ formatted: `${Math.floor(sessionLimitMs / (1000 * 60 * 60))}h ${Math.floor((sessionLimitMs % (1000 * 60 * 60)) / (1000 * 60))}m`,
1801
+ nextResetTime: nextResetTime.toISOString(),
1802
+ resetHour: nextResetTime.getHours()
1701
1803
  }
1702
1804
  };
1703
1805
  } catch (error) {