claude-code-templates 1.5.5 → 1.5.7

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/analytics.js +132 -18
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-templates",
3
- "version": "1.5.5",
3
+ "version": "1.5.7",
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": {
package/src/analytics.js CHANGED
@@ -112,17 +112,33 @@ class ClaudeAnalytics {
112
112
  // Extract project name from path
113
113
  const projectFromPath = this.extractProjectFromPath(filePath);
114
114
 
115
+ // Parse messages to get their content for status determination
116
+ const parsedMessages = lines.map(line => {
117
+ try {
118
+ const item = JSON.parse(line);
119
+ if (item.message && item.message.role) {
120
+ return {
121
+ role: item.message.role,
122
+ timestamp: new Date(item.timestamp),
123
+ content: item.message.content
124
+ };
125
+ }
126
+ } catch {}
127
+ return null;
128
+ }).filter(Boolean);
129
+
115
130
  const conversation = {
116
131
  id: filename.replace('.jsonl', ''),
117
132
  filename: filename,
118
133
  filePath: filePath,
119
- messageCount: messages.length,
134
+ messageCount: parsedMessages.length,
120
135
  fileSize: stats.size,
121
136
  lastModified: stats.mtime,
122
137
  created: stats.birthtime,
123
138
  tokens: this.estimateTokens(content),
124
- project: projectFromPath || this.extractProjectFromConversation(messages),
125
- status: this.determineConversationStatus(messages, stats.mtime)
139
+ project: projectFromPath || this.extractProjectFromConversation(parsedMessages),
140
+ status: this.determineConversationStatus(parsedMessages, stats.mtime),
141
+ conversationState: this.determineConversationState(parsedMessages, stats.mtime)
126
142
  };
127
143
 
128
144
  conversations.push(conversation);
@@ -224,11 +240,69 @@ class ClaudeAnalytics {
224
240
  const timeDiff = now - lastModified;
225
241
  const minutesAgo = timeDiff / (1000 * 60);
226
242
 
243
+ if (messages.length === 0) {
244
+ return minutesAgo < 5 ? 'active' : 'inactive';
245
+ }
246
+
247
+ // Sort messages by timestamp to get the actual conversation flow
248
+ const sortedMessages = messages.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
249
+ const lastMessage = sortedMessages[sortedMessages.length - 1];
250
+ const lastMessageTime = new Date(lastMessage.timestamp);
251
+ const lastMessageMinutesAgo = (now - lastMessageTime) / (1000 * 60);
252
+
253
+ // Simplified status logic - typing is now part of active
254
+ if (lastMessage.role === 'user' && lastMessageMinutesAgo < 3) {
255
+ return 'active';
256
+ } else if (lastMessage.role === 'assistant' && lastMessageMinutesAgo < 5) {
257
+ return 'active';
258
+ }
259
+
260
+ // Fallback to file modification time for edge cases
227
261
  if (minutesAgo < 5) return 'active';
228
262
  if (minutesAgo < 60) return 'recent';
229
263
  return 'inactive';
230
264
  }
231
265
 
266
+ determineConversationState(messages, lastModified) {
267
+ const now = new Date();
268
+ const timeDiff = now - lastModified;
269
+ const minutesAgo = timeDiff / (1000 * 60);
270
+
271
+ if (messages.length === 0) {
272
+ return minutesAgo < 5 ? 'Waiting for input...' : 'Idle';
273
+ }
274
+
275
+ // Sort messages by timestamp to get the actual conversation flow
276
+ const sortedMessages = messages.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
277
+ const lastMessage = sortedMessages[sortedMessages.length - 1];
278
+ const lastMessageTime = new Date(lastMessage.timestamp);
279
+ const lastMessageMinutesAgo = (now - lastMessageTime) / (1000 * 60);
280
+
281
+ // Detailed conversation state logic
282
+ if (lastMessage.role === 'user') {
283
+ // User sent last message
284
+ if (lastMessageMinutesAgo < 0.5) {
285
+ return 'Claude Code working...';
286
+ } else if (lastMessageMinutesAgo < 3) {
287
+ return 'Awaiting response...';
288
+ } else {
289
+ return 'User typing...';
290
+ }
291
+ } else if (lastMessage.role === 'assistant') {
292
+ // Assistant sent last message
293
+ if (lastMessageMinutesAgo < 2) {
294
+ return 'Awaiting user input...';
295
+ } else if (lastMessageMinutesAgo < 5) {
296
+ return 'User may be typing...';
297
+ }
298
+ }
299
+
300
+ // Fallback states
301
+ if (minutesAgo < 5) return 'Recently active';
302
+ if (minutesAgo < 60) return 'Idle';
303
+ return 'Inactive';
304
+ }
305
+
232
306
  determineProjectStatus(lastActivity) {
233
307
  const now = new Date();
234
308
  const timeDiff = now - lastActivity;
@@ -537,7 +611,7 @@ async function createWebDashboard() {
537
611
  }
538
612
 
539
613
  .terminal-title {
540
- color: #58a6ff;
614
+ color: #d57455;
541
615
  font-size: 1.25rem;
542
616
  font-weight: normal;
543
617
  display: flex;
@@ -583,7 +657,7 @@ async function createWebDashboard() {
583
657
  }
584
658
 
585
659
  .stat-value {
586
- color: #58a6ff;
660
+ color: #d57455;
587
661
  font-weight: bold;
588
662
  }
589
663
 
@@ -620,13 +694,13 @@ async function createWebDashboard() {
620
694
  }
621
695
 
622
696
  .filter-btn:hover {
623
- border-color: #58a6ff;
624
- color: #58a6ff;
697
+ border-color: #d57455;
698
+ color: #d57455;
625
699
  }
626
700
 
627
701
  .filter-btn.active {
628
- background: #58a6ff;
629
- border-color: #58a6ff;
702
+ background: #d57455;
703
+ border-color: #d57455;
630
704
  color: #0d1117;
631
705
  }
632
706
 
@@ -655,7 +729,7 @@ async function createWebDashboard() {
655
729
  }
656
730
 
657
731
  .session-id {
658
- color: #58a6ff;
732
+ color: #d57455;
659
733
  font-family: monospace;
660
734
  }
661
735
 
@@ -689,6 +763,30 @@ async function createWebDashboard() {
689
763
  color: #7d8590;
690
764
  }
691
765
 
766
+ .conversation-state {
767
+ color: #d57455;
768
+ font-style: italic;
769
+ font-size: 0.8rem;
770
+ }
771
+
772
+ .conversation-state.working {
773
+ animation: working-pulse 1.5s infinite;
774
+ }
775
+
776
+ .conversation-state.typing {
777
+ animation: typing-pulse 1.5s infinite;
778
+ }
779
+
780
+ @keyframes working-pulse {
781
+ 0%, 100% { opacity: 1; }
782
+ 50% { opacity: 0.7; }
783
+ }
784
+
785
+ @keyframes typing-pulse {
786
+ 0%, 100% { opacity: 1; }
787
+ 50% { opacity: 0.6; }
788
+ }
789
+
692
790
  .loading, .error {
693
791
  text-align: center;
694
792
  padding: 40px;
@@ -725,7 +823,7 @@ async function createWebDashboard() {
725
823
  }
726
824
 
727
825
  .detail-title {
728
- color: #58a6ff;
826
+ color: #d57455;
729
827
  font-size: 1.1rem;
730
828
  }
731
829
 
@@ -747,19 +845,19 @@ async function createWebDashboard() {
747
845
  }
748
846
 
749
847
  .btn:hover {
750
- border-color: #58a6ff;
751
- color: #58a6ff;
848
+ border-color: #d57455;
849
+ color: #d57455;
752
850
  }
753
851
 
754
852
  .btn-primary {
755
- background: #58a6ff;
756
- border-color: #58a6ff;
853
+ background: #d57455;
854
+ border-color: #d57455;
757
855
  color: #0d1117;
758
856
  }
759
857
 
760
858
  .btn-primary:hover {
761
- background: #79c0ff;
762
- border-color: #79c0ff;
859
+ background: #e8956f;
860
+ border-color: #e8956f;
763
861
  }
764
862
 
765
863
  .session-info {
@@ -820,7 +918,7 @@ async function createWebDashboard() {
820
918
  }
821
919
 
822
920
  .message-role.assistant {
823
- color: #58a6ff;
921
+ color: #d57455;
824
922
  }
825
923
 
826
924
  .message-time {
@@ -919,6 +1017,7 @@ async function createWebDashboard() {
919
1017
  <th>messages</th>
920
1018
  <th>tokens</th>
921
1019
  <th>last activity</th>
1020
+ <th>conversation state</th>
922
1021
  <th>status</th>
923
1022
  </tr>
924
1023
  </thead>
@@ -1018,6 +1117,7 @@ async function createWebDashboard() {
1018
1117
  <td class="session-messages">\${conv.messageCount}</td>
1019
1118
  <td class="session-tokens">\${conv.tokens.toLocaleString()}</td>
1020
1119
  <td class="session-time">\${formatTime(conv.lastModified)}</td>
1120
+ <td class="conversation-state \${getStateClass(conv.conversationState)}">\${conv.conversationState}</td>
1021
1121
  <td class="status-\${conv.status}">\${conv.status}</td>
1022
1122
  </tr>
1023
1123
  \`).join('');
@@ -1046,6 +1146,16 @@ async function createWebDashboard() {
1046
1146
  });
1047
1147
  }
1048
1148
 
1149
+ function getStateClass(conversationState) {
1150
+ if (conversationState.includes('working') || conversationState.includes('Working')) {
1151
+ return 'working';
1152
+ }
1153
+ if (conversationState.includes('typing') || conversationState.includes('Typing')) {
1154
+ return 'typing';
1155
+ }
1156
+ return '';
1157
+ }
1158
+
1049
1159
  // Filter button handlers
1050
1160
  document.addEventListener('DOMContentLoaded', function() {
1051
1161
  const filterButtons = document.querySelectorAll('.filter-btn');
@@ -1128,6 +1238,10 @@ async function createWebDashboard() {
1128
1238
  <div class="info-label">last modified</div>
1129
1239
  <div class="info-value">\${new Date(session.lastModified).toLocaleString()}</div>
1130
1240
  </div>
1241
+ <div class="info-item">
1242
+ <div class="info-label">conversation state</div>
1243
+ <div class="info-value conversation-state \${getStateClass(session.conversationState)}">\${session.conversationState}</div>
1244
+ </div>
1131
1245
  <div class="info-item">
1132
1246
  <div class="info-label">status</div>
1133
1247
  <div class="info-value status-\${session.status}">\${session.status}</div>