claude-code-templates 1.20.3 → 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.
@@ -67,11 +67,15 @@ program
67
67
  .option('--setting <setting>', 'install specific setting component (supports comma-separated values)')
68
68
  .option('--hook <hook>', 'install specific hook component (supports comma-separated values)')
69
69
  .option('--workflow <workflow>', 'install workflow from hash (#hash) OR workflow YAML (base64 encoded) when used with --agent/--command/--mcp')
70
- .option('--prompt <prompt>', 'execute the provided prompt in Claude Code after installation')
70
+ .option('--prompt <prompt>', 'execute the provided prompt in Claude Code after installation or in sandbox')
71
71
  .option('--create-agent <agent>', 'create a global agent accessible from anywhere (e.g., customer-support)')
72
72
  .option('--list-agents', 'list all installed global agents')
73
73
  .option('--remove-agent <agent>', 'remove a global agent')
74
74
  .option('--update-agent <agent>', 'update a global agent to the latest version')
75
+ .option('--studio', 'launch Claude Code Studio interface for local and cloud execution')
76
+ .option('--sandbox <provider>', 'execute Claude Code in isolated sandbox environment (e.g., e2b)')
77
+ .option('--e2b-api-key <key>', 'E2B API key for sandbox execution (alternative to environment variable)')
78
+ .option('--anthropic-api-key <key>', 'Anthropic API key for Claude Code (alternative to environment variable)')
75
79
  .action(async (options) => {
76
80
  try {
77
81
  // Only show banner for non-agent-list commands
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-templates",
3
- "version": "1.20.3",
3
+ "version": "1.21.0",
4
4
  "description": "CLI tool to setup Claude Code configurations with framework-specific commands, automation hooks and MCP Servers for your projects",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -353,6 +353,9 @@ class ActivityHeatmap {
353
353
  */
354
354
  generateCalendarData(dailyActivity) {
355
355
  const today = new Date();
356
+ const todayEnd = new Date(today);
357
+ todayEnd.setHours(23, 59, 59, 999); // End of today
358
+
356
359
  const oneYearAgo = new Date(today);
357
360
  oneYearAgo.setFullYear(today.getFullYear() - 1);
358
361
  oneYearAgo.setDate(today.getDate() + 1);
@@ -365,13 +368,14 @@ class ActivityHeatmap {
365
368
  const months = [];
366
369
  const current = new Date(startDate);
367
370
 
368
- // Generate weeks first
369
- while (current <= today) {
371
+ // Generate weeks first - include today completely
372
+ while (current <= todayEnd) {
370
373
  const week = [];
371
374
 
372
375
  for (let day = 0; day < 7; day++) {
373
- if (current <= today) {
374
- week.push(new Date(current));
376
+ if (current <= todayEnd) {
377
+ const dayDate = new Date(current);
378
+ week.push(dayDate);
375
379
  } else {
376
380
  week.push(null);
377
381
  }
@@ -421,6 +425,7 @@ class ActivityHeatmap {
421
425
  const level = this.getActivityLevel(activity);
422
426
  const modeClass = this.currentMetric === 'tools' ? 'tools-mode' : '';
423
427
 
428
+
424
429
  return `
425
430
  <div class="heatmap-day level-${level} ${modeClass}"
426
431
  data-date="${dateKey}"
@@ -606,7 +611,9 @@ class ActivityHeatmap {
606
611
 
607
612
  if (!date) return;
608
613
 
609
- const dateObj = new Date(date);
614
+ // Fix timezone issue: parse date as local instead of UTC
615
+ const [year, month, dayNum] = date.split('-').map(Number);
616
+ const dateObj = new Date(year, month - 1, dayNum); // month is 0-indexed
610
617
  const formattedDate = dateObj.toLocaleDateString('en-US', {
611
618
  weekday: 'short',
612
619
  month: 'short',
@@ -677,7 +684,16 @@ class ActivityHeatmap {
677
684
  const titleElement = document.getElementById('activity-total');
678
685
 
679
686
  if (titleElement) {
680
- titleElement.textContent = `${this.formatNumber(totalActivity)} ${this.currentMetric} in the last year`;
687
+ // Ensure totalActivity is a number
688
+ const activityCount = totalActivity || 0;
689
+
690
+ if (this.currentMetric === 'messages') {
691
+ titleElement.innerHTML = `${this.formatNumber(activityCount)} <span style="color: #ff7f50;">Claude Code</span> ${this.currentMetric} in the last year`;
692
+ } else if (this.currentMetric === 'tools') {
693
+ titleElement.innerHTML = `${this.formatNumber(activityCount)} <span style="color: #ff7f50;">Claude Code</span> ${this.currentMetric} in the last year`;
694
+ } else {
695
+ titleElement.innerHTML = `${this.formatNumber(activityCount)} ${this.currentMetric} in the last year`;
696
+ }
681
697
  }
682
698
  }
683
699
 
@@ -685,6 +701,11 @@ class ActivityHeatmap {
685
701
  * Format large numbers with commas
686
702
  */
687
703
  formatNumber(num) {
704
+ // Handle undefined, null, or non-numeric values
705
+ if (num == null || typeof num !== 'number' || isNaN(num)) {
706
+ return '0';
707
+ }
708
+
688
709
  if (num >= 1000) {
689
710
  return (num / 1000).toFixed(1) + 'k';
690
711
  }
@@ -705,6 +726,29 @@ class ActivityHeatmap {
705
726
  `;
706
727
  }
707
728
 
729
+ /**
730
+ * Clear cache and refresh the heatmap data
731
+ */
732
+ async clearCacheAndRefresh() {
733
+ try {
734
+ console.log('🔥 Clearing cache and refreshing heatmap data...');
735
+
736
+ // Clear frontend cache
737
+ this.dataService.clearCache();
738
+
739
+ // Clear backend cache
740
+ await fetch('/api/clear-cache', { method: 'POST' });
741
+
742
+ // Force reload activity data
743
+ await this.loadActivityData();
744
+ this.positionMonthLabels();
745
+
746
+ console.log('✅ Cache cleared and data refreshed');
747
+ } catch (error) {
748
+ console.error('❌ Error clearing cache:', error);
749
+ }
750
+ }
751
+
708
752
  /**
709
753
  * Refresh the heatmap data
710
754
  */
@@ -447,6 +447,10 @@ class DashboardPage {
447
447
  activityHeatmapContainer,
448
448
  this.dataService
449
449
  );
450
+
451
+ // Make globally accessible for debugging
452
+ window.activityHeatmap = this.components.activityHeatmap;
453
+
450
454
  await this.components.activityHeatmap.initialize();
451
455
  } catch (error) {
452
456
  console.warn('ActivityHeatmap initialization failed:', error);
@@ -103,6 +103,13 @@ class DataService {
103
103
  }
104
104
  }
105
105
 
106
+ /**
107
+ * Clear all cached data
108
+ */
109
+ clearCache() {
110
+ this.cache.clear();
111
+ console.log('🔥 Frontend cache cleared');
112
+ }
106
113
 
107
114
  /**
108
115
  * Get conversations data
package/src/analytics.js CHANGED
@@ -1191,11 +1191,74 @@ 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
+
1194
1227
  // Activity heatmap data endpoint - needs full conversation history
1195
1228
  this.app.get('/api/activity', async (req, res) => {
1196
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
1197
1261
  console.log(`🔥 /api/activity called - loading all conversations...`);
1198
- // Get all conversations without the 150 limit for accurate heatmap data
1199
1262
  const allConversations = await this.conversationAnalyzer.loadConversations(this.stateCalculator);
1200
1263
  console.log(`🔥 Loaded ${allConversations.length} conversations from server`);
1201
1264
 
@@ -2066,33 +2129,17 @@ class ClaudeAnalytics {
2066
2129
  oneYearAgo.setHours(0, 0, 0, 0);
2067
2130
 
2068
2131
  const today = new Date();
2069
- today.setHours(23, 59, 59, 999);
2132
+ const todayEnd = new Date();
2133
+ todayEnd.setHours(23, 59, 59, 999);
2070
2134
 
2071
2135
  console.log(`🔥 Generating activity data for ${conversations.length} conversations (FULL DATASET)...`);
2072
-
2073
- // Show date range of conversations
2074
- if (conversations.length > 0) {
2075
- const dates = conversations.map(c => c.lastModified ? new Date(c.lastModified) : null).filter(Boolean);
2076
- dates.sort((a, b) => a - b);
2077
- console.log(`🔥 Server conversation date range: ${dates[0]?.toLocaleDateString()} to ${dates[dates.length - 1]?.toLocaleDateString()}`);
2078
-
2079
- // Show some sample conversations with dates
2080
- console.log(`🔥 First 3 conversations by date:`);
2081
- const sortedConversations = conversations
2082
- .filter(c => c.lastModified)
2083
- .sort((a, b) => new Date(a.lastModified) - new Date(b.lastModified))
2084
- .slice(0, 3);
2085
- sortedConversations.forEach((conv, i) => {
2086
- console.log(` ${i+1}: ${conv.filename} - ${new Date(conv.lastModified).toLocaleDateString()}`);
2087
- });
2088
- }
2089
2136
 
2090
2137
  // Process conversations for daily activity
2091
2138
  conversations.forEach(conversation => {
2092
2139
  if (!conversation.lastModified) return;
2093
2140
 
2094
2141
  const date = new Date(conversation.lastModified);
2095
- if (date < oneYearAgo || date > today) return;
2142
+ if (date < oneYearAgo || date > todayEnd) return;
2096
2143
 
2097
2144
  const dateKey = date.toISOString().split('T')[0]; // YYYY-MM-DD
2098
2145