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.
- package/package.json +1 -1
- package/src/analytics.js +132 -18
package/package.json
CHANGED
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:
|
|
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(
|
|
125
|
-
status: this.determineConversationStatus(
|
|
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: #
|
|
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: #
|
|
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: #
|
|
624
|
-
color: #
|
|
697
|
+
border-color: #d57455;
|
|
698
|
+
color: #d57455;
|
|
625
699
|
}
|
|
626
700
|
|
|
627
701
|
.filter-btn.active {
|
|
628
|
-
background: #
|
|
629
|
-
border-color: #
|
|
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: #
|
|
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: #
|
|
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: #
|
|
751
|
-
color: #
|
|
848
|
+
border-color: #d57455;
|
|
849
|
+
color: #d57455;
|
|
752
850
|
}
|
|
753
851
|
|
|
754
852
|
.btn-primary {
|
|
755
|
-
background: #
|
|
756
|
-
border-color: #
|
|
853
|
+
background: #d57455;
|
|
854
|
+
border-color: #d57455;
|
|
757
855
|
color: #0d1117;
|
|
758
856
|
}
|
|
759
857
|
|
|
760
858
|
.btn-primary:hover {
|
|
761
|
-
background: #
|
|
762
|
-
border-color: #
|
|
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: #
|
|
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>
|