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.
@@ -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;