claude-code-templates 1.10.1 → 1.12.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.
@@ -119,28 +119,12 @@ class DataCache {
119
119
  return cached.messages;
120
120
  }
121
121
 
122
- // Cache miss - parse conversation
122
+ // Cache miss - parse conversation with tool correlation
123
123
  this.metrics.misses++;
124
124
  const content = await this.getFileContent(filepath);
125
+ const lines = content.trim().split('\n').filter(line => line.trim());
125
126
 
126
- const messages = content.trim().split('\n')
127
- .filter(line => line.trim())
128
- .map(line => {
129
- try {
130
- const item = JSON.parse(line);
131
- if (item.message && item.message.role) {
132
- return {
133
- role: item.message.role,
134
- timestamp: new Date(item.timestamp),
135
- content: item.message.content,
136
- model: item.message.model || null,
137
- usage: item.message.usage || null,
138
- };
139
- }
140
- } catch {}
141
- return null;
142
- })
143
- .filter(Boolean);
127
+ const messages = this.parseAndCorrelateToolMessages(lines);
144
128
 
145
129
  this.caches.parsedConversations.set(filepath, {
146
130
  messages,
@@ -150,6 +134,127 @@ class DataCache {
150
134
  return messages;
151
135
  }
152
136
 
137
+ /**
138
+ * Parse JSONL lines and correlate tool_use with tool_result
139
+ * @param {Array} lines - JSONL lines
140
+ * @returns {Array} Parsed and correlated messages
141
+ */
142
+ parseAndCorrelateToolMessages(lines) {
143
+ const entries = [];
144
+ const toolUseMap = new Map();
145
+
146
+ // First pass: parse all entries and map tool_use entries
147
+ for (const line of lines) {
148
+ try {
149
+ const item = JSON.parse(line);
150
+ if (item.message && (item.type === 'assistant' || item.type === 'user')) {
151
+ entries.push(item);
152
+
153
+ // Track tool_use entries by their ID
154
+ if (item.type === 'assistant' && item.message.content) {
155
+ const toolUseBlock = Array.isArray(item.message.content)
156
+ ? item.message.content.find(c => c.type === 'tool_use')
157
+ : (item.message.content.type === 'tool_use' ? item.message.content : null);
158
+
159
+ if (toolUseBlock && toolUseBlock.id) {
160
+ toolUseMap.set(toolUseBlock.id, item);
161
+ if (toolUseBlock.id === 'toolu_01D8RMQYDySWAscQCC6pfDWf') {
162
+ // Debug: Specific tool_use mapped for debugging
163
+ }
164
+ }
165
+ }
166
+ }
167
+ } catch (error) {
168
+ // Skip invalid JSONL lines
169
+ }
170
+ }
171
+
172
+ // Second pass: correlate tool_result with tool_use (first process ALL tool_results)
173
+ for (const item of entries) {
174
+ if (item.type === 'user' && item.message.content) {
175
+ // Check if this is a tool_result entry
176
+ const toolResultBlock = Array.isArray(item.message.content)
177
+ ? item.message.content.find(c => c.type === 'tool_result')
178
+ : (item.message.content.type === 'tool_result' ? item.message.content : null);
179
+
180
+ if (toolResultBlock && toolResultBlock.tool_use_id) {
181
+ // This is a tool_result - attach it to the corresponding tool_use
182
+ const toolUseEntry = toolUseMap.get(toolResultBlock.tool_use_id);
183
+ if (toolUseEntry) {
184
+ // Enhance tool result with additional metadata
185
+ const enhancedToolResult = {
186
+ ...toolResultBlock,
187
+ // Include additional metadata from toolUseResult if available
188
+ ...(item.toolUseResult && {
189
+ stdout: item.toolUseResult.stdout,
190
+ stderr: item.toolUseResult.stderr,
191
+ interrupted: item.toolUseResult.interrupted,
192
+ isImage: item.toolUseResult.isImage,
193
+ returnCodeInterpretation: item.toolUseResult.returnCodeInterpretation
194
+ })
195
+ };
196
+
197
+ // Attach tool result to the tool use entry
198
+ if (!toolUseEntry.toolResults) {
199
+ toolUseEntry.toolResults = [];
200
+ }
201
+ toolUseEntry.toolResults.push(enhancedToolResult);
202
+ // console.log: Tool result attached successfully
203
+ }
204
+ }
205
+ }
206
+ }
207
+
208
+ // Third pass: process messages and filter out standalone tool_result entries
209
+ const processedMessages = [];
210
+
211
+ for (const item of entries) {
212
+ if (item.type === 'user' && item.message.content) {
213
+ // Check if this is a tool_result entry (skip it as we've already processed it)
214
+ const toolResultBlock = Array.isArray(item.message.content)
215
+ ? item.message.content.find(c => c.type === 'tool_result')
216
+ : (item.message.content.type === 'tool_result' ? item.message.content : null);
217
+
218
+ if (toolResultBlock && toolResultBlock.tool_use_id) {
219
+ // Skip standalone tool_result entries - they've been attached to their tool_use
220
+ continue;
221
+ }
222
+ }
223
+
224
+ // Convert to our standard format
225
+ if (item.toolResults) {
226
+ // console.log: Processing item with tool results
227
+ }
228
+
229
+ // Debug specific item we're looking for
230
+ if (item.message && item.message.content && Array.isArray(item.message.content)) {
231
+ const toolUseBlock = item.message.content.find(c => c.type === 'tool_use' && c.id === 'toolu_01D8RMQYDySWAscQCC6pfDWf');
232
+ if (toolUseBlock) {
233
+ // Debug: Processing tool_use item
234
+ }
235
+ }
236
+ const parsed = {
237
+ id: item.message.id || item.uuid || null,
238
+ role: item.message.role || (item.type === 'assistant' ? 'assistant' : 'user'),
239
+ timestamp: new Date(item.timestamp),
240
+ content: item.message.content,
241
+ model: item.message.model || null,
242
+ usage: item.message.usage || null,
243
+ toolResults: item.toolResults || null, // Include attached tool results (populated during correlation)
244
+ isCompactSummary: item.isCompactSummary || false, // Preserve compact summary flag
245
+ uuid: item.uuid || null, // Include UUID for message identification
246
+ type: item.type || null // Include type field
247
+ };
248
+
249
+ // Debug log for our specific tool_use
250
+ // Debug: Final message processing completed
251
+
252
+ processedMessages.push(parsed);
253
+ }
254
+
255
+ return processedMessages;
256
+ }
257
+
153
258
  /**
154
259
  * Get file stats with caching
155
260
  * @param {string} filepath - Path to file
@@ -104,6 +104,43 @@ class NotificationManager {
104
104
  console.log(chalk.green(`📊 Data refreshed (source: ${source})`));
105
105
  }
106
106
 
107
+ /**
108
+ * Send new message notification for real-time updates
109
+ * @param {string} conversationId - Conversation ID
110
+ * @param {Object} message - New message object
111
+ * @param {Object} metadata - Additional metadata
112
+ */
113
+ notifyNewMessage(conversationId, message, metadata = {}) {
114
+ const notification = {
115
+ type: 'new_message',
116
+ conversationId,
117
+ message,
118
+ metadata,
119
+ timestamp: new Date().toISOString(),
120
+ id: this.generateNotificationId()
121
+ };
122
+
123
+ // Don't throttle new message notifications - they should be immediate
124
+ this.addToHistory(notification);
125
+
126
+ // Send via WebSocket to conversation_updates channel
127
+ if (this.webSocketServer) {
128
+ this.webSocketServer.broadcast({
129
+ type: 'new_message',
130
+ data: {
131
+ conversationId,
132
+ message,
133
+ metadata
134
+ }
135
+ }, 'conversation_updates');
136
+ }
137
+
138
+ // Send to local subscribers
139
+ this.notifySubscribers('new_message', notification);
140
+
141
+ console.log(chalk.blue(`📨 New message notification sent for conversation ${conversationId}`));
142
+ }
143
+
107
144
  /**
108
145
  * Send system status notification
109
146
  * @param {Object} status - System status
@@ -281,7 +281,7 @@ class WebSocketServer {
281
281
  });
282
282
 
283
283
  if (sentCount > 0) {
284
- console.log(chalk.green(`📢 Broadcasted ${message.type} to ${sentCount} clients${channel ? ` on channel ${channel}` : ''}`));
284
+ //console.log(chalk.green(`📢 Broadcasted ${message.type} to ${sentCount} clients${channel ? ` on channel ${channel}` : ''}`));
285
285
  }
286
286
 
287
287
  // Queue message if no clients connected
@@ -0,0 +1,46 @@
1
+ # Analytics Web Architecture
2
+
3
+ ## Current Architecture (Active)
4
+
5
+ ### Main Components:
6
+ - **App.js** - Main application orchestrator with sidebar navigation
7
+ - **Sidebar.js** - Navigation sidebar component
8
+ - **DashboardPage.js** - Dashboard page with metrics and charts
9
+ - **AgentsPage.js** - Agents/conversations page
10
+
11
+ ### Services:
12
+ - **WebSocketService.js** - Real-time communication
13
+ - **DataService.js** - API data fetching and caching
14
+ - **StateService.js** - Application state management
15
+
16
+ ### Layout Structure:
17
+ ```
18
+ App.js
19
+ ├── Sidebar.js (navigation)
20
+ └── Page Components
21
+ ├── DashboardPage.js
22
+ └── AgentsPage.js
23
+ ```
24
+
25
+ ## Deprecated Architecture (Removed)
26
+
27
+ ### Deprecated Files:
28
+ - **main.js** → `main.js.deprecated` - Old initialization system
29
+ - **Dashboard.js** → `Dashboard.js.deprecated` - Old monolithic dashboard
30
+
31
+ ### Reason for Deprecation:
32
+ The old architecture used a single Dashboard.js component without navigation, while the new architecture uses App.js with proper routing and a sidebar navigation system.
33
+
34
+ ## WebSocket Integration
35
+
36
+ The WebSocket system is fully functional and provides real-time updates for:
37
+ - Conversation state changes
38
+ - Data refresh events
39
+ - System status updates
40
+
41
+ ## Loading State Fix
42
+
43
+ Fixed issue where loading states weren't clearing properly by:
44
+ 1. Reordering DOM rendering before setting loading states
45
+ 2. Adding proper error handling and fallback mechanisms
46
+ 3. Ensuring `setLoading(false)` is called in finally blocks
@@ -39,8 +39,11 @@ class AnalyticsDashboard {
39
39
  async initializeServices() {
40
40
  console.log('🔧 Initializing services...');
41
41
 
42
- // Initialize DataService
43
- this.services.data = new DataService();
42
+ // Initialize WebSocketService
43
+ this.services.webSocket = new WebSocketService();
44
+
45
+ // Initialize DataService with WebSocket integration
46
+ this.services.data = new DataService(this.services.webSocket);
44
47
 
45
48
  // Initialize StateService
46
49
  this.services.state = new StateService();
@@ -48,7 +51,33 @@ class AnalyticsDashboard {
48
51
  // Initialize Charts service (placeholder)
49
52
  this.services.chart = new Charts(null, this.services.data, this.services.state);
50
53
 
51
- // Start periodic data refresh
54
+ // Setup DataService -> StateService integration for real-time updates
55
+ this.services.data.addEventListener((type, data) => {
56
+ switch (type) {
57
+ case 'new_message':
58
+ // Route new message events to StateService which will notify AgentsPage
59
+ this.services.state.notifyListeners('new_message', data);
60
+ break;
61
+ case 'conversation_state_change':
62
+ // Route state changes to StateService
63
+ this.services.state.notifyListeners('conversation_state_change', data);
64
+ break;
65
+ case 'data_refresh':
66
+ // Route data refresh events to StateService
67
+ this.services.state.notifyListeners('data_refresh', data);
68
+ break;
69
+ }
70
+ });
71
+
72
+ // Connect WebSocket (will fallback to polling if connection fails)
73
+ try {
74
+ await this.services.webSocket.connect();
75
+ console.log('✅ WebSocket connected successfully');
76
+ } catch (error) {
77
+ console.log('⚠️ WebSocket connection failed, falling back to polling');
78
+ }
79
+
80
+ // Start periodic data refresh (will adjust based on WebSocket availability)
52
81
  this.services.data.startPeriodicRefresh();
53
82
 
54
83
  console.log('✅ Services initialized');