claude-code-templates 1.5.4 → 1.5.6

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 +121 -22
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-templates",
3
- "version": "1.5.4",
3
+ "version": "1.5.6",
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,32 @@ 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)
126
141
  };
127
142
 
128
143
  conversations.push(conversation);
@@ -224,6 +239,35 @@ class ClaudeAnalytics {
224
239
  const timeDiff = now - lastModified;
225
240
  const minutesAgo = timeDiff / (1000 * 60);
226
241
 
242
+ if (messages.length === 0) {
243
+ return minutesAgo < 5 ? 'active' : 'inactive';
244
+ }
245
+
246
+ // Sort messages by timestamp to get the actual conversation flow
247
+ const sortedMessages = messages.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
248
+ const lastMessage = sortedMessages[sortedMessages.length - 1];
249
+ const lastMessageTime = new Date(lastMessage.timestamp);
250
+ const lastMessageMinutesAgo = (now - lastMessageTime) / (1000 * 60);
251
+
252
+ // Advanced status logic
253
+ if (lastMessage.role === 'user') {
254
+ // User sent last message
255
+ if (lastMessageMinutesAgo < 0.5) {
256
+ // Very recent user message, likely typing or waiting for response
257
+ return 'typing';
258
+ } else if (lastMessageMinutesAgo < 3) {
259
+ // Recent user message, waiting for assistant
260
+ return 'active';
261
+ }
262
+ } else if (lastMessage.role === 'assistant') {
263
+ // Assistant sent last message
264
+ if (lastMessageMinutesAgo < 2) {
265
+ // Recent assistant response, conversation is active
266
+ return 'active';
267
+ }
268
+ }
269
+
270
+ // Fallback to file modification time for edge cases
227
271
  if (minutesAgo < 5) return 'active';
228
272
  if (minutesAgo < 60) return 'recent';
229
273
  return 'inactive';
@@ -377,7 +421,7 @@ class ClaudeAnalytics {
377
421
  // Read the actual conversation file
378
422
  const content = await fs.readFile(session.filePath, 'utf8');
379
423
  const lines = content.trim().split('\n').filter(line => line.trim());
380
- const messages = lines.map(line => {
424
+ const rawMessages = lines.map(line => {
381
425
  try {
382
426
  return JSON.parse(line);
383
427
  } catch {
@@ -385,6 +429,36 @@ class ClaudeAnalytics {
385
429
  }
386
430
  }).filter(Boolean);
387
431
 
432
+ // Extract actual messages from Claude Code format
433
+ const messages = rawMessages.map(item => {
434
+ if (item.message && item.message.role) {
435
+ let content = '';
436
+
437
+ if (typeof item.message.content === 'string') {
438
+ content = item.message.content;
439
+ } else if (Array.isArray(item.message.content)) {
440
+ content = item.message.content
441
+ .map(block => {
442
+ if (block.type === 'text') return block.text;
443
+ if (block.type === 'tool_use') return `[Tool: ${block.name}]`;
444
+ if (block.type === 'tool_result') return '[Tool Result]';
445
+ return block.content || '';
446
+ })
447
+ .join('\n');
448
+ } else if (item.message.content && item.message.content.length) {
449
+ content = item.message.content[0].text || '';
450
+ }
451
+
452
+ return {
453
+ role: item.message.role,
454
+ content: content || 'No content',
455
+ timestamp: item.timestamp,
456
+ type: item.type
457
+ };
458
+ }
459
+ return null;
460
+ }).filter(Boolean);
461
+
388
462
  res.json({
389
463
  session: session,
390
464
  messages: messages,
@@ -507,7 +581,7 @@ async function createWebDashboard() {
507
581
  }
508
582
 
509
583
  .terminal-title {
510
- color: #58a6ff;
584
+ color: #d57455;
511
585
  font-size: 1.25rem;
512
586
  font-weight: normal;
513
587
  display: flex;
@@ -553,7 +627,7 @@ async function createWebDashboard() {
553
627
  }
554
628
 
555
629
  .stat-value {
556
- color: #58a6ff;
630
+ color: #d57455;
557
631
  font-weight: bold;
558
632
  }
559
633
 
@@ -590,13 +664,13 @@ async function createWebDashboard() {
590
664
  }
591
665
 
592
666
  .filter-btn:hover {
593
- border-color: #58a6ff;
594
- color: #58a6ff;
667
+ border-color: #d57455;
668
+ color: #d57455;
595
669
  }
596
670
 
597
671
  .filter-btn.active {
598
- background: #58a6ff;
599
- border-color: #58a6ff;
672
+ background: #d57455;
673
+ border-color: #d57455;
600
674
  color: #0d1117;
601
675
  }
602
676
 
@@ -625,7 +699,7 @@ async function createWebDashboard() {
625
699
  }
626
700
 
627
701
  .session-id {
628
- color: #58a6ff;
702
+ color: #d57455;
629
703
  font-family: monospace;
630
704
  }
631
705
 
@@ -659,6 +733,17 @@ async function createWebDashboard() {
659
733
  color: #7d8590;
660
734
  }
661
735
 
736
+ .status-typing {
737
+ color: #d57455;
738
+ font-weight: bold;
739
+ animation: typing-pulse 1.5s infinite;
740
+ }
741
+
742
+ @keyframes typing-pulse {
743
+ 0%, 100% { opacity: 1; }
744
+ 50% { opacity: 0.6; }
745
+ }
746
+
662
747
  .loading, .error {
663
748
  text-align: center;
664
749
  padding: 40px;
@@ -695,7 +780,7 @@ async function createWebDashboard() {
695
780
  }
696
781
 
697
782
  .detail-title {
698
- color: #58a6ff;
783
+ color: #d57455;
699
784
  font-size: 1.1rem;
700
785
  }
701
786
 
@@ -717,19 +802,19 @@ async function createWebDashboard() {
717
802
  }
718
803
 
719
804
  .btn:hover {
720
- border-color: #58a6ff;
721
- color: #58a6ff;
805
+ border-color: #d57455;
806
+ color: #d57455;
722
807
  }
723
808
 
724
809
  .btn-primary {
725
- background: #58a6ff;
726
- border-color: #58a6ff;
810
+ background: #d57455;
811
+ border-color: #d57455;
727
812
  color: #0d1117;
728
813
  }
729
814
 
730
815
  .btn-primary:hover {
731
- background: #79c0ff;
732
- border-color: #79c0ff;
816
+ background: #e8956f;
817
+ border-color: #e8956f;
733
818
  }
734
819
 
735
820
  .session-info {
@@ -790,7 +875,7 @@ async function createWebDashboard() {
790
875
  }
791
876
 
792
877
  .message-role.assistant {
793
- color: #58a6ff;
878
+ color: #d57455;
794
879
  }
795
880
 
796
881
  .message-time {
@@ -875,6 +960,7 @@ async function createWebDashboard() {
875
960
  <span class="filter-label">filter sessions:</span>
876
961
  <div class="filter-buttons">
877
962
  <button class="filter-btn active" data-filter="active">active</button>
963
+ <button class="filter-btn" data-filter="typing">typing</button>
878
964
  <button class="filter-btn" data-filter="recent">recent</button>
879
965
  <button class="filter-btn" data-filter="inactive">inactive</button>
880
966
  <button class="filter-btn" data-filter="all">all</button>
@@ -1006,6 +1092,16 @@ async function createWebDashboard() {
1006
1092
  return \`\${days}d ago\`;
1007
1093
  }
1008
1094
 
1095
+ function formatMessageTime(timestamp) {
1096
+ const date = new Date(timestamp);
1097
+ return date.toLocaleTimeString('en-US', {
1098
+ hour12: false,
1099
+ hour: '2-digit',
1100
+ minute: '2-digit',
1101
+ second: '2-digit'
1102
+ });
1103
+ }
1104
+
1009
1105
  // Filter button handlers
1010
1106
  document.addEventListener('DOMContentLoaded', function() {
1011
1107
  const filterButtons = document.querySelectorAll('.filter-btn');
@@ -1111,7 +1207,9 @@ async function createWebDashboard() {
1111
1207
  <div class="message">
1112
1208
  <div class="message-header">
1113
1209
  <div class="message-role \${message.role}">\${message.role}</div>
1114
- <div class="message-time">message #\${index + 1}</div>
1210
+ <div class="message-time">
1211
+ #\${index + 1} • \${message.timestamp ? formatMessageTime(message.timestamp) : 'unknown time'}
1212
+ </div>
1115
1213
  </div>
1116
1214
  <div class="message-content">\${truncateContent(message.content || 'no content')}</div>
1117
1215
  </div>
@@ -1124,10 +1222,11 @@ async function createWebDashboard() {
1124
1222
  }
1125
1223
  }
1126
1224
 
1127
- function truncateContent(content, maxLength = 500) {
1225
+ function truncateContent(content, maxLength = 1000) {
1128
1226
  if (typeof content !== 'string') return 'no content';
1227
+ if (!content.trim()) return 'empty message';
1129
1228
  if (content.length <= maxLength) return content;
1130
- return content.substring(0, maxLength) + '...';
1229
+ return content.substring(0, maxLength) + '\\n\\n[... message truncated ...]';
1131
1230
  }
1132
1231
 
1133
1232
  function formatBytes(bytes) {