claude-code-templates 1.20.2 → 1.21.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
@@ -1191,6 +1191,90 @@ class ClaudeAnalytics {
1191
1191
  }
1192
1192
  });
1193
1193
 
1194
+ // Clear cache endpoint
1195
+ this.app.post('/api/clear-cache', async (req, res) => {
1196
+ try {
1197
+ console.log('🔥 Clear cache request received');
1198
+
1199
+ // Clear DataCache
1200
+ if (this.dataCache && typeof this.dataCache.clear === 'function') {
1201
+ this.dataCache.clear();
1202
+ console.log('🔥 Server DataCache cleared');
1203
+ } else {
1204
+ console.log('⚠️ DataCache not available or no clear method');
1205
+ }
1206
+
1207
+ // Also clear ConversationAnalyzer cache if available
1208
+ if (this.conversationAnalyzer && typeof this.conversationAnalyzer.clearCache === 'function') {
1209
+ this.conversationAnalyzer.clearCache();
1210
+ console.log('🔥 ConversationAnalyzer cache cleared');
1211
+ }
1212
+
1213
+ res.json({
1214
+ success: true,
1215
+ message: 'Cache cleared successfully',
1216
+ timestamp: new Date().toISOString()
1217
+ });
1218
+ } catch (error) {
1219
+ console.error('❌ Error clearing cache:', error);
1220
+ res.status(500).json({
1221
+ error: 'Failed to clear cache',
1222
+ details: error.message
1223
+ });
1224
+ }
1225
+ });
1226
+
1227
+ // Activity heatmap data endpoint - needs full conversation history
1228
+ this.app.get('/api/activity', async (req, res) => {
1229
+ try {
1230
+ // TEMPORARY: Use test data for demo/screenshots
1231
+ console.log(`🔥 /api/activity called - using test data for demo...`);
1232
+ const fs = require('fs');
1233
+ const path = require('path');
1234
+ const testDataPath = path.join(__dirname, 'test-activity-data.json');
1235
+
1236
+ if (fs.existsSync(testDataPath)) {
1237
+ const testData = JSON.parse(fs.readFileSync(testDataPath, 'utf8'));
1238
+
1239
+ // Calculate totals
1240
+ const totalContributions = testData.reduce((sum, day) => sum + day.conversations, 0);
1241
+ const totalTools = testData.reduce((sum, day) => sum + day.tools, 0);
1242
+ const totalMessages = testData.reduce((sum, day) => sum + day.messages, 0);
1243
+ const totalTokens = testData.reduce((sum, day) => sum + day.tokens, 0);
1244
+
1245
+ const activityData = {
1246
+ dailyActivity: testData,
1247
+ totalContributions,
1248
+ activeDays: testData.length,
1249
+ longestStreak: 7, // Sample streak
1250
+ currentStreak: 3, // Sample current streak
1251
+ totalTools,
1252
+ totalMessages,
1253
+ totalTokens,
1254
+ timestamp: new Date().toISOString()
1255
+ };
1256
+
1257
+ return res.json(activityData);
1258
+ }
1259
+
1260
+ // Fallback to real data if test file doesn't exist
1261
+ console.log(`🔥 /api/activity called - loading all conversations...`);
1262
+ const allConversations = await this.conversationAnalyzer.loadConversations(this.stateCalculator);
1263
+ console.log(`🔥 Loaded ${allConversations.length} conversations from server`);
1264
+
1265
+ // Generate activity data using complete dataset
1266
+ const activityData = this.generateActivityDataFromConversations(allConversations);
1267
+ res.json({
1268
+ conversations: allConversations, // Also include conversations for the heatmap component
1269
+ ...activityData,
1270
+ timestamp: new Date().toISOString()
1271
+ });
1272
+ } catch (error) {
1273
+ console.error('Error generating activity data:', error);
1274
+ res.status(500).json({ error: 'Failed to generate activity data' });
1275
+ }
1276
+ });
1277
+
1194
1278
  // Main dashboard route
1195
1279
  this.app.get('/', (req, res) => {
1196
1280
  res.sendFile(path.join(__dirname, 'analytics-web', 'index.html'));
@@ -2033,6 +2117,156 @@ class ClaudeAnalytics {
2033
2117
  }
2034
2118
  }
2035
2119
 
2120
+ /**
2121
+ * Generate activity data for the heatmap using complete conversation set
2122
+ * @param {Array} conversations - Complete conversation array (not limited)
2123
+ * @returns {Object} Activity data including daily contributions and stats
2124
+ */
2125
+ generateActivityDataFromConversations(conversations) {
2126
+ const dailyActivity = new Map();
2127
+ const oneYearAgo = new Date();
2128
+ oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);
2129
+ oneYearAgo.setHours(0, 0, 0, 0);
2130
+
2131
+ const today = new Date();
2132
+ const todayEnd = new Date();
2133
+ todayEnd.setHours(23, 59, 59, 999);
2134
+
2135
+ console.log(`🔥 Generating activity data for ${conversations.length} conversations (FULL DATASET)...`);
2136
+
2137
+ // Process conversations for daily activity
2138
+ conversations.forEach(conversation => {
2139
+ if (!conversation.lastModified) return;
2140
+
2141
+ const date = new Date(conversation.lastModified);
2142
+ if (date < oneYearAgo || date > todayEnd) return;
2143
+
2144
+ const dateKey = date.toISOString().split('T')[0]; // YYYY-MM-DD
2145
+
2146
+ const current = dailyActivity.get(dateKey) || {
2147
+ conversations: 0,
2148
+ tokens: 0,
2149
+ messages: 0,
2150
+ tools: 0,
2151
+ date: dateKey
2152
+ };
2153
+
2154
+ current.conversations += 1;
2155
+ current.tokens += conversation.tokens || 0;
2156
+ current.messages += conversation.messageCount || 0;
2157
+ current.tools += (conversation.toolUsage?.totalToolCalls || 0);
2158
+
2159
+ dailyActivity.set(dateKey, current);
2160
+ });
2161
+
2162
+ // Convert to array and sort by date
2163
+ const activityArray = Array.from(dailyActivity.values())
2164
+ .sort((a, b) => a.date.localeCompare(b.date));
2165
+
2166
+ // Calculate stats
2167
+ const totalContributions = activityArray.reduce((sum, day) => sum + day.conversations, 0);
2168
+ const totalTools = activityArray.reduce((sum, day) => sum + day.tools, 0);
2169
+ const { longestStreak, currentStreak } = this.calculateStreaks(activityArray);
2170
+
2171
+ console.log(`🔥 Activity data generated: ${activityArray.length} active days, ${totalContributions} total contributions, ${totalTools} tool calls`);
2172
+
2173
+ return {
2174
+ dailyActivity: activityArray,
2175
+ totalContributions,
2176
+ totalTools,
2177
+ longestStreak,
2178
+ currentStreak,
2179
+ activeDays: activityArray.length,
2180
+ startDate: oneYearAgo.toISOString().split('T')[0],
2181
+ endDate: today.toISOString().split('T')[0]
2182
+ };
2183
+ }
2184
+
2185
+ /**
2186
+ * Generate activity data for the heatmap (legacy method for backward compatibility)
2187
+ * @returns {Object} Activity data including daily contributions and stats
2188
+ */
2189
+ generateActivityData() {
2190
+ if (!this.data || !this.data.conversations) {
2191
+ return {
2192
+ dailyActivity: [],
2193
+ totalContributions: 0,
2194
+ longestStreak: 0,
2195
+ currentStreak: 0
2196
+ };
2197
+ }
2198
+
2199
+ return this.generateActivityDataFromConversations(this.data.conversations);
2200
+ }
2201
+
2202
+ /**
2203
+ * Calculate contribution streaks
2204
+ * @param {Array} activityArray - Sorted array of daily activity
2205
+ * @returns {Object} Longest and current streak counts
2206
+ */
2207
+ calculateStreaks(activityArray) {
2208
+ if (activityArray.length === 0) {
2209
+ return { longestStreak: 0, currentStreak: 0 };
2210
+ }
2211
+
2212
+ let longestStreak = 0;
2213
+ let currentStreak = 0;
2214
+ let tempStreak = 0;
2215
+
2216
+ const today = new Date();
2217
+ today.setHours(0, 0, 0, 0);
2218
+
2219
+ // Create a map of active dates for quick lookup
2220
+ const activeDates = new Set(activityArray.map(day => day.date));
2221
+
2222
+ // Check streak going backwards from today
2223
+ let checkDate = new Date(today);
2224
+ let foundToday = false;
2225
+
2226
+ // First check if today or yesterday has activity
2227
+ for (let i = 0; i < 2; i++) {
2228
+ const dateKey = checkDate.toISOString().split('T')[0];
2229
+ if (activeDates.has(dateKey)) {
2230
+ foundToday = true;
2231
+ break;
2232
+ }
2233
+ checkDate.setDate(checkDate.getDate() - 1);
2234
+ }
2235
+
2236
+ if (foundToday) {
2237
+ // Calculate current streak
2238
+ checkDate = new Date(today);
2239
+ while (true) {
2240
+ const dateKey = checkDate.toISOString().split('T')[0];
2241
+ if (activeDates.has(dateKey)) {
2242
+ currentStreak++;
2243
+ checkDate.setDate(checkDate.getDate() - 1);
2244
+ } else {
2245
+ break;
2246
+ }
2247
+ }
2248
+ }
2249
+
2250
+ // Calculate longest streak by checking all consecutive days
2251
+ const oneYearAgo = new Date(today);
2252
+ oneYearAgo.setFullYear(today.getFullYear() - 1);
2253
+
2254
+ checkDate = new Date(oneYearAgo);
2255
+ while (checkDate <= today) {
2256
+ const dateKey = checkDate.toISOString().split('T')[0];
2257
+
2258
+ if (activeDates.has(dateKey)) {
2259
+ tempStreak++;
2260
+ longestStreak = Math.max(longestStreak, tempStreak);
2261
+ } else {
2262
+ tempStreak = 0;
2263
+ }
2264
+
2265
+ checkDate.setDate(checkDate.getDate() + 1);
2266
+ }
2267
+
2268
+ return { longestStreak, currentStreak };
2269
+ }
2036
2270
 
2037
2271
  stop() {
2038
2272
  // Stop file watchers