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.
- package/README.md +6 -0
- package/bin/create-claude-config.js +1 -0
- package/package.json +1 -2
- package/src/analytics/core/ConversationAnalyzer.js +159 -43
- package/src/analytics/core/FileWatcher.js +146 -11
- package/src/analytics/data/DataCache.js +124 -19
- package/src/analytics/notifications/NotificationManager.js +37 -0
- package/src/analytics/notifications/WebSocketServer.js +1 -1
- package/src/analytics-web/FRONT_ARCHITECTURE.md +46 -0
- package/src/analytics-web/assets/js/{main.js → main.js.deprecated} +32 -3
- package/src/analytics-web/components/AgentsPage.js +4744 -0
- package/src/analytics-web/components/App.js +441 -0
- package/src/analytics-web/components/{Dashboard.js → Dashboard.js.deprecated} +23 -7
- package/src/analytics-web/components/DashboardPage.js +1531 -0
- package/src/analytics-web/components/Sidebar.js +197 -0
- package/src/analytics-web/components/ToolDisplay.js +554 -0
- package/src/analytics-web/index.html +5189 -1760
- package/src/analytics-web/services/DataService.js +89 -16
- package/src/analytics-web/services/StateService.js +9 -0
- package/src/analytics-web/services/WebSocketService.js +17 -5
- package/src/analytics.js +665 -38
- package/src/console-bridge.js +610 -0
- package/src/file-operations.js +143 -23
- package/src/index.js +24 -1
- package/src/templates.js +4 -0
- package/src/test-console-bridge.js +67 -0
|
@@ -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 =
|
|
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
|
|
43
|
-
this.services.
|
|
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
|
-
//
|
|
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');
|