claude-code-templates 1.8.0 → 1.8.2
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 +246 -0
- package/package.json +26 -12
- package/src/analytics/core/ConversationAnalyzer.js +754 -0
- package/src/analytics/core/FileWatcher.js +285 -0
- package/src/analytics/core/ProcessDetector.js +242 -0
- package/src/analytics/core/SessionAnalyzer.js +631 -0
- package/src/analytics/core/StateCalculator.js +190 -0
- package/src/analytics/data/DataCache.js +550 -0
- package/src/analytics/notifications/NotificationManager.js +448 -0
- package/src/analytics/notifications/WebSocketServer.js +526 -0
- package/src/analytics/utils/PerformanceMonitor.js +455 -0
- package/src/analytics-web/assets/js/main.js +312 -0
- package/src/analytics-web/components/Charts.js +114 -0
- package/src/analytics-web/components/ConversationTable.js +437 -0
- package/src/analytics-web/components/Dashboard.js +573 -0
- package/src/analytics-web/components/SessionTimer.js +596 -0
- package/src/analytics-web/index.html +882 -49
- package/src/analytics-web/index.html.original +1939 -0
- package/src/analytics-web/services/DataService.js +357 -0
- package/src/analytics-web/services/StateService.js +276 -0
- package/src/analytics-web/services/WebSocketService.js +523 -0
- package/src/analytics.js +641 -2311
- package/src/analytics.log +0 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* StateCalculator - Handles all conversation state determination logic
|
|
5
|
+
* Extracted from monolithic analytics.js for better maintainability
|
|
6
|
+
*/
|
|
7
|
+
class StateCalculator {
|
|
8
|
+
constructor() {
|
|
9
|
+
// Cache for process states to avoid repeated calculations
|
|
10
|
+
this.processCache = new Map();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Main state determination logic with process information
|
|
15
|
+
* @param {Array} messages - Parsed conversation messages
|
|
16
|
+
* @param {Date} lastModified - File last modification time
|
|
17
|
+
* @param {Object} runningProcess - Active process information
|
|
18
|
+
* @returns {string} Conversation state
|
|
19
|
+
*/
|
|
20
|
+
determineConversationState(messages, lastModified, runningProcess = null) {
|
|
21
|
+
const now = new Date();
|
|
22
|
+
const timeDiff = now - lastModified;
|
|
23
|
+
const minutesAgo = timeDiff / (1000 * 60);
|
|
24
|
+
|
|
25
|
+
// If there's an active process, use simpler and more stable logic
|
|
26
|
+
if (runningProcess && runningProcess.hasActiveCommand) {
|
|
27
|
+
// Check conversation flow first for immediate response
|
|
28
|
+
if (messages.length > 0) {
|
|
29
|
+
const sortedMessages = messages.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
|
|
30
|
+
const lastMessage = sortedMessages[sortedMessages.length - 1];
|
|
31
|
+
const lastMessageTime = new Date(lastMessage.timestamp);
|
|
32
|
+
const lastMessageMinutesAgo = (now - lastMessageTime) / (1000 * 60);
|
|
33
|
+
|
|
34
|
+
if (lastMessage.role === 'user') {
|
|
35
|
+
// User sent message - be more conservative about "working" state
|
|
36
|
+
if (lastMessageMinutesAgo < 1) {
|
|
37
|
+
return 'Claude Code working...';
|
|
38
|
+
} else {
|
|
39
|
+
return 'Awaiting response...';
|
|
40
|
+
}
|
|
41
|
+
} else if (lastMessage.role === 'assistant') {
|
|
42
|
+
// Claude responded - check if file activity indicates continued work
|
|
43
|
+
const fileTimeDiff = (now - lastModified) / 1000; // seconds
|
|
44
|
+
if (fileTimeDiff < 30) {
|
|
45
|
+
return 'Claude Code working...';
|
|
46
|
+
}
|
|
47
|
+
// Use broader time ranges for more stability
|
|
48
|
+
if (lastMessageMinutesAgo < 5) {
|
|
49
|
+
return 'Awaiting user input...';
|
|
50
|
+
} else {
|
|
51
|
+
return 'User typing...';
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Very recent file activity = Claude working (fallback)
|
|
57
|
+
const fileTimeDiff = (now - lastModified) / 1000; // seconds
|
|
58
|
+
if (fileTimeDiff < 30) {
|
|
59
|
+
return 'Claude Code working...';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Default for active process
|
|
63
|
+
return 'Awaiting user input...';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (messages.length === 0) {
|
|
67
|
+
return minutesAgo < 5 ? 'Waiting for input...' : 'Idle';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Sort messages by timestamp to get the actual conversation flow
|
|
71
|
+
const sortedMessages = messages.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
|
|
72
|
+
const lastMessage = sortedMessages[sortedMessages.length - 1];
|
|
73
|
+
const lastMessageTime = new Date(lastMessage.timestamp);
|
|
74
|
+
const lastMessageMinutesAgo = (now - lastMessageTime) / (1000 * 60);
|
|
75
|
+
|
|
76
|
+
// Simplified and more stable state logic
|
|
77
|
+
if (lastMessage.role === 'user') {
|
|
78
|
+
// User sent last message
|
|
79
|
+
if (lastMessageMinutesAgo < 1) {
|
|
80
|
+
return 'Claude Code working...';
|
|
81
|
+
} else if (lastMessageMinutesAgo < 5) {
|
|
82
|
+
return 'Awaiting response...';
|
|
83
|
+
} else {
|
|
84
|
+
return 'User typing...';
|
|
85
|
+
}
|
|
86
|
+
} else if (lastMessage.role === 'assistant') {
|
|
87
|
+
// Assistant sent last message - consolidate similar states
|
|
88
|
+
if (lastMessageMinutesAgo < 5) {
|
|
89
|
+
return 'Awaiting user input...';
|
|
90
|
+
} else {
|
|
91
|
+
return 'User typing...';
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Fallback states
|
|
96
|
+
if (minutesAgo < 5) return 'Recently active';
|
|
97
|
+
if (minutesAgo < 60) return 'Idle';
|
|
98
|
+
return 'Inactive';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Quick state calculation without file I/O for ultra-fast updates
|
|
103
|
+
* @param {Object} conversation - Conversation object
|
|
104
|
+
* @param {Array} runningProcesses - Array of active processes
|
|
105
|
+
* @returns {string|null} Conversation state or null if not active
|
|
106
|
+
*/
|
|
107
|
+
quickStateCalculation(conversation, runningProcesses) {
|
|
108
|
+
// Check if there's an active process for this conversation
|
|
109
|
+
const hasActiveProcess = runningProcesses.some(process =>
|
|
110
|
+
process.workingDir.includes(conversation.project) ||
|
|
111
|
+
process.command.includes(conversation.project) ||
|
|
112
|
+
conversation.runningProcess // Already matched
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
if (!hasActiveProcess) {
|
|
116
|
+
return null; // Not active, skip
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Simple heuristic based on file modification time - use broader ranges for stability
|
|
120
|
+
const now = new Date();
|
|
121
|
+
const timeDiff = (now - new Date(conversation.lastModified)) / 1000; // seconds
|
|
122
|
+
|
|
123
|
+
// More stable state logic - fewer transitions
|
|
124
|
+
if (timeDiff < 30) {
|
|
125
|
+
return 'Claude Code working...';
|
|
126
|
+
} else if (timeDiff < 300) { // 5 minutes
|
|
127
|
+
return 'Awaiting user input...';
|
|
128
|
+
} else {
|
|
129
|
+
return 'User typing...';
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Determine conversation status (active/recent/inactive)
|
|
135
|
+
* @param {Array} messages - Parsed conversation messages
|
|
136
|
+
* @param {Date} lastModified - File last modification time
|
|
137
|
+
* @returns {string} Conversation status
|
|
138
|
+
*/
|
|
139
|
+
determineConversationStatus(messages, lastModified) {
|
|
140
|
+
const now = new Date();
|
|
141
|
+
const timeDiff = now - lastModified;
|
|
142
|
+
const minutesAgo = timeDiff / (1000 * 60);
|
|
143
|
+
|
|
144
|
+
if (messages.length === 0) {
|
|
145
|
+
return minutesAgo < 5 ? 'active' : 'inactive';
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Sort messages by timestamp
|
|
149
|
+
const sortedMessages = messages.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
|
|
150
|
+
const lastMessage = sortedMessages[sortedMessages.length - 1];
|
|
151
|
+
const lastMessageTime = new Date(lastMessage.timestamp);
|
|
152
|
+
const lastMessageMinutesAgo = (now - lastMessageTime) / (1000 * 60);
|
|
153
|
+
|
|
154
|
+
// More balanced logic - active conversations and recent activity
|
|
155
|
+
if (lastMessage.role === 'user' && lastMessageMinutesAgo < 3) {
|
|
156
|
+
return 'active';
|
|
157
|
+
} else if (lastMessage.role === 'assistant' && lastMessageMinutesAgo < 5) {
|
|
158
|
+
return 'active';
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Use file modification time for recent activity
|
|
162
|
+
if (minutesAgo < 5) return 'active';
|
|
163
|
+
if (minutesAgo < 30) return 'recent';
|
|
164
|
+
return 'inactive';
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Get CSS class for conversation state styling
|
|
169
|
+
* @param {string} conversationState - The conversation state
|
|
170
|
+
* @returns {string} CSS class name
|
|
171
|
+
*/
|
|
172
|
+
getStateClass(conversationState) {
|
|
173
|
+
if (conversationState.includes('working') || conversationState.includes('Working')) {
|
|
174
|
+
return 'working';
|
|
175
|
+
}
|
|
176
|
+
if (conversationState.includes('typing') || conversationState.includes('Typing')) {
|
|
177
|
+
return 'typing';
|
|
178
|
+
}
|
|
179
|
+
return '';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Clear any cached state information
|
|
184
|
+
*/
|
|
185
|
+
clearCache() {
|
|
186
|
+
this.processCache.clear();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
module.exports = StateCalculator;
|