claude-code-templates 1.14.16 → 1.15.1
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/bin/create-claude-config.js +5 -1
- package/package.json +2 -1
- package/src/analytics/core/AgentAnalyzer.js +17 -3
- package/src/analytics/core/ProcessDetector.js +23 -7
- package/src/analytics/core/StateCalculator.js +102 -33
- package/src/analytics/data/DataCache.js +7 -7
- package/src/analytics-web/chats_mobile.html +2590 -0
- package/src/analytics-web/components/App.js +10 -10
- package/src/analytics-web/components/SessionTimer.js +1 -1
- package/src/analytics-web/components/Sidebar.js +5 -14
- package/src/analytics-web/index.html +932 -78
- package/src/analytics.js +263 -5
- package/src/chats-mobile.js +682 -0
- package/src/claude-api-proxy.js +460 -0
- package/src/index.js +43 -5
- package/src/analytics-web/components/AgentsPage.js +0 -4761
|
@@ -53,7 +53,11 @@ program
|
|
|
53
53
|
.option('--hook-stats, --hooks-stats', 'analyze existing automation hooks and offer optimization')
|
|
54
54
|
.option('--mcp-stats, --mcps-stats', 'analyze existing MCP server configurations and offer optimization')
|
|
55
55
|
.option('--analytics', 'launch real-time Claude Code analytics dashboard')
|
|
56
|
-
.option('--chats
|
|
56
|
+
.option('--chats', 'launch mobile-first chats interface (AI-optimized for mobile devices)')
|
|
57
|
+
.option('--agents', 'launch Claude Code agents dashboard (opens directly to conversations)')
|
|
58
|
+
.option('--chats-mobile', 'launch mobile-first chats interface (AI-optimized for mobile devices)')
|
|
59
|
+
.option('--tunnel', 'enable Cloudflare Tunnel for remote access (use with --analytics or --chats)')
|
|
60
|
+
.option('--verbose', 'enable verbose logging for debugging and development')
|
|
57
61
|
.option('--health-check, --health, --check, --verify', 'run comprehensive health check to verify Claude Code setup')
|
|
58
62
|
.option('--agent <agent>', 'install specific agent component (supports comma-separated values)')
|
|
59
63
|
.option('--command <command>', 'install specific command component (supports comma-separated values)')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-templates",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.1",
|
|
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": {
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
"inquirer": "^8.2.6",
|
|
70
70
|
"open": "^8.4.2",
|
|
71
71
|
"ora": "^5.4.1",
|
|
72
|
+
"uuid": "^11.1.0",
|
|
72
73
|
"ws": "^8.18.3"
|
|
73
74
|
},
|
|
74
75
|
"engines": {
|
|
@@ -287,11 +287,25 @@ class AgentAnalyzer {
|
|
|
287
287
|
const content = await fs.readFile(filePath, 'utf8');
|
|
288
288
|
const lines = content.trim().split('\n').filter(line => line.trim());
|
|
289
289
|
|
|
290
|
-
return lines.map(line => {
|
|
290
|
+
return lines.map((line, index) => {
|
|
291
291
|
try {
|
|
292
|
-
|
|
292
|
+
// Skip empty or whitespace-only lines
|
|
293
|
+
if (!line.trim()) {
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Basic validation - must start with { and end with }
|
|
298
|
+
const trimmed = line.trim();
|
|
299
|
+
if (!trimmed.startsWith('{') || !trimmed.endsWith('}')) {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return JSON.parse(trimmed);
|
|
293
304
|
} catch (error) {
|
|
294
|
-
|
|
305
|
+
// Only log the error occasionally to avoid spam
|
|
306
|
+
if (index % 10 === 0) {
|
|
307
|
+
console.warn(`Error parsing JSONL line ${index + 1} in ${filePath}:`, error.message.substring(0, 100));
|
|
308
|
+
}
|
|
295
309
|
return null;
|
|
296
310
|
}
|
|
297
311
|
}).filter(Boolean);
|
|
@@ -29,22 +29,38 @@ class ProcessDetector {
|
|
|
29
29
|
|
|
30
30
|
return new Promise((resolve) => {
|
|
31
31
|
// Search for processes containing 'claude' but exclude our own analytics process and system processes
|
|
32
|
-
exec('ps aux | grep -i claude | grep -v grep | grep -v analytics | grep -v "/Applications/Claude.app" | grep -v "npm start"', (error, stdout) => {
|
|
32
|
+
exec('ps aux | grep -i claude | grep -v grep | grep -v analytics | grep -v "/Applications/Claude.app" | grep -v "npm start" | grep -v chats-mobile', (error, stdout) => {
|
|
33
33
|
if (error) {
|
|
34
34
|
resolve([]);
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
console.log('🔍 Raw Claude processes output:', stdout); // Debug output
|
|
39
|
+
|
|
38
40
|
const processes = stdout.split('\n')
|
|
39
41
|
.filter(line => line.trim())
|
|
40
42
|
.filter(line => {
|
|
41
|
-
//
|
|
43
|
+
// More flexible Claude CLI process detection
|
|
42
44
|
const fullCommand = line.split(/\s+/).slice(10).join(' ');
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
const isClaudeProcess = (
|
|
46
|
+
fullCommand.includes('claude') &&
|
|
47
|
+
!fullCommand.includes('chrome_crashpad_handler') &&
|
|
48
|
+
!fullCommand.includes('create-claude-config') &&
|
|
49
|
+
!fullCommand.includes('chats-mobile') &&
|
|
50
|
+
!fullCommand.includes('analytics') &&
|
|
51
|
+
// Allow various Claude CLI invocations
|
|
52
|
+
(fullCommand.trim() === 'claude' ||
|
|
53
|
+
fullCommand.includes('claude --') ||
|
|
54
|
+
fullCommand.includes('claude ') ||
|
|
55
|
+
fullCommand.includes('/claude') ||
|
|
56
|
+
fullCommand.includes('bin/claude'))
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
if (isClaudeProcess) {
|
|
60
|
+
console.log('✅ Found Claude process:', fullCommand);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return isClaudeProcess;
|
|
48
64
|
})
|
|
49
65
|
.map(line => {
|
|
50
66
|
const parts = line.split(/\s+/);
|
|
@@ -19,10 +19,21 @@ class StateCalculator {
|
|
|
19
19
|
*/
|
|
20
20
|
determineConversationState(messages, lastModified, runningProcess = null) {
|
|
21
21
|
const now = new Date();
|
|
22
|
-
const
|
|
23
|
-
const
|
|
22
|
+
const fileTimeDiff = now - lastModified;
|
|
23
|
+
const fileMinutesAgo = fileTimeDiff / (1000 * 60);
|
|
24
|
+
|
|
25
|
+
// Enhanced detection: Look for real Claude Code activity indicators
|
|
26
|
+
const claudeActivity = this.detectRealClaudeActivity(messages, lastModified);
|
|
27
|
+
if (claudeActivity.isActive) {
|
|
28
|
+
return claudeActivity.status;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// If there's very recent file activity (within 5 minutes), consider it active
|
|
32
|
+
if (fileMinutesAgo < 5) {
|
|
33
|
+
return 'Claude Code working...';
|
|
34
|
+
}
|
|
24
35
|
|
|
25
|
-
// If there's an active process,
|
|
36
|
+
// If there's an active process, prioritize that
|
|
26
37
|
if (runningProcess && runningProcess.hasActiveCommand) {
|
|
27
38
|
// Check conversation flow first for immediate response
|
|
28
39
|
if (messages.length > 0) {
|
|
@@ -32,39 +43,30 @@ class StateCalculator {
|
|
|
32
43
|
const lastMessageMinutesAgo = (now - lastMessageTime) / (1000 * 60);
|
|
33
44
|
|
|
34
45
|
if (lastMessage.role === 'user') {
|
|
35
|
-
// User sent message - be more
|
|
36
|
-
if (lastMessageMinutesAgo <
|
|
46
|
+
// User sent message - be more generous about active state
|
|
47
|
+
if (lastMessageMinutesAgo < 3) {
|
|
37
48
|
return 'Claude Code working...';
|
|
38
|
-
} else {
|
|
49
|
+
} else if (lastMessageMinutesAgo < 10) {
|
|
39
50
|
return 'Awaiting response...';
|
|
51
|
+
} else {
|
|
52
|
+
return 'Active session';
|
|
40
53
|
}
|
|
41
54
|
} else if (lastMessage.role === 'assistant') {
|
|
42
|
-
// Claude responded -
|
|
43
|
-
|
|
44
|
-
if (fileTimeDiff < 30) {
|
|
45
|
-
return 'Claude Code working...';
|
|
46
|
-
}
|
|
47
|
-
// Use broader time ranges for more stability
|
|
48
|
-
if (lastMessageMinutesAgo < 5) {
|
|
55
|
+
// Claude responded - if there's an active process, still active
|
|
56
|
+
if (lastMessageMinutesAgo < 10) {
|
|
49
57
|
return 'Awaiting user input...';
|
|
50
58
|
} else {
|
|
51
|
-
return '
|
|
59
|
+
return 'Active session';
|
|
52
60
|
}
|
|
53
61
|
}
|
|
54
62
|
}
|
|
55
63
|
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
if (fileTimeDiff < 30) {
|
|
59
|
-
return 'Claude Code working...';
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Default for active process
|
|
63
|
-
return 'Awaiting user input...';
|
|
64
|
+
// Default for active process - be more generous
|
|
65
|
+
return 'Active session';
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
if (messages.length === 0) {
|
|
67
|
-
return
|
|
69
|
+
return fileMinutesAgo < 5 ? 'Waiting for input...' : 'Idle';
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
// Sort messages by timestamp to get the actual conversation flow
|
|
@@ -73,28 +75,32 @@ class StateCalculator {
|
|
|
73
75
|
const lastMessageTime = new Date(lastMessage.timestamp);
|
|
74
76
|
const lastMessageMinutesAgo = (now - lastMessageTime) / (1000 * 60);
|
|
75
77
|
|
|
76
|
-
//
|
|
78
|
+
// More generous logic for active conversations
|
|
77
79
|
if (lastMessage.role === 'user') {
|
|
78
80
|
// User sent last message
|
|
79
|
-
if (lastMessageMinutesAgo <
|
|
81
|
+
if (lastMessageMinutesAgo < 3) {
|
|
80
82
|
return 'Claude Code working...';
|
|
81
|
-
} else if (lastMessageMinutesAgo <
|
|
83
|
+
} else if (lastMessageMinutesAgo < 10) {
|
|
82
84
|
return 'Awaiting response...';
|
|
83
|
-
} else {
|
|
85
|
+
} else if (lastMessageMinutesAgo < 30) {
|
|
84
86
|
return 'User typing...';
|
|
87
|
+
} else {
|
|
88
|
+
return 'Recently active';
|
|
85
89
|
}
|
|
86
90
|
} else if (lastMessage.role === 'assistant') {
|
|
87
|
-
// Assistant sent last message
|
|
88
|
-
if (lastMessageMinutesAgo <
|
|
91
|
+
// Assistant sent last message
|
|
92
|
+
if (lastMessageMinutesAgo < 10) {
|
|
89
93
|
return 'Awaiting user input...';
|
|
90
|
-
} else {
|
|
94
|
+
} else if (lastMessageMinutesAgo < 30) {
|
|
91
95
|
return 'User typing...';
|
|
96
|
+
} else {
|
|
97
|
+
return 'Recently active';
|
|
92
98
|
}
|
|
93
99
|
}
|
|
94
100
|
|
|
95
|
-
// Fallback states
|
|
96
|
-
if (
|
|
97
|
-
if (
|
|
101
|
+
// Fallback states - be more generous about "active" state
|
|
102
|
+
if (fileMinutesAgo < 10 || lastMessageMinutesAgo < 30) return 'Recently active';
|
|
103
|
+
if (fileMinutesAgo < 60 || lastMessageMinutesAgo < 120) return 'Idle';
|
|
98
104
|
return 'Inactive';
|
|
99
105
|
}
|
|
100
106
|
|
|
@@ -164,6 +170,69 @@ class StateCalculator {
|
|
|
164
170
|
return 'inactive';
|
|
165
171
|
}
|
|
166
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Detect real Claude Code activity based on conversation patterns and file activity
|
|
175
|
+
* @param {Array} messages - Conversation messages
|
|
176
|
+
* @param {Date} lastModified - File last modification time
|
|
177
|
+
* @returns {Object} Activity detection result
|
|
178
|
+
*/
|
|
179
|
+
detectRealClaudeActivity(messages, lastModified) {
|
|
180
|
+
const now = new Date();
|
|
181
|
+
const fileMinutesAgo = (now - lastModified) / (1000 * 60);
|
|
182
|
+
|
|
183
|
+
if (!messages || messages.length === 0) {
|
|
184
|
+
return { isActive: false, status: 'No messages' };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Sort messages by timestamp
|
|
188
|
+
const sortedMessages = messages.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
|
|
189
|
+
const lastMessage = sortedMessages[sortedMessages.length - 1];
|
|
190
|
+
const lastMessageTime = new Date(lastMessage.timestamp);
|
|
191
|
+
const messageMinutesAgo = (now - lastMessageTime) / (1000 * 60);
|
|
192
|
+
|
|
193
|
+
// Real activity indicators:
|
|
194
|
+
|
|
195
|
+
// 1. Very recent file modification (Claude Code just wrote to the conversation file)
|
|
196
|
+
if (fileMinutesAgo < 1) {
|
|
197
|
+
return { isActive: true, status: 'Claude Code working...' };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// 2. Recent user message with recent file activity (Claude is processing)
|
|
201
|
+
if (lastMessage.role === 'user' && messageMinutesAgo < 5 && fileMinutesAgo < 10) {
|
|
202
|
+
return { isActive: true, status: 'Claude Code working...' };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// 3. Recent assistant message with very recent file activity (might still be working)
|
|
206
|
+
if (lastMessage.role === 'assistant' && messageMinutesAgo < 2 && fileMinutesAgo < 5) {
|
|
207
|
+
return { isActive: true, status: 'Claude Code finishing...' };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// 4. Look for tool activity patterns (tools often indicate active sessions)
|
|
211
|
+
const recentMessages = sortedMessages.slice(-3);
|
|
212
|
+
const hasRecentTools = recentMessages.some(msg =>
|
|
213
|
+
(Array.isArray(msg.content) && msg.content.some(block => block.type === 'tool_use')) ||
|
|
214
|
+
(msg.toolResults && msg.toolResults.length > 0)
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
if (hasRecentTools && messageMinutesAgo < 10 && fileMinutesAgo < 15) {
|
|
218
|
+
return { isActive: true, status: 'Active session' };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// 5. Rapid message exchange pattern (back-and-forth conversation)
|
|
222
|
+
if (sortedMessages.length >= 2) {
|
|
223
|
+
const lastTwoMessages = sortedMessages.slice(-2);
|
|
224
|
+
const timeBetweenLast = Math.abs(
|
|
225
|
+
new Date(lastTwoMessages[1].timestamp) - new Date(lastTwoMessages[0].timestamp)
|
|
226
|
+
) / (1000 * 60); // minutes
|
|
227
|
+
|
|
228
|
+
if (timeBetweenLast < 5 && messageMinutesAgo < 15 && fileMinutesAgo < 20) {
|
|
229
|
+
return { isActive: true, status: 'Active conversation' };
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return { isActive: false, status: null };
|
|
234
|
+
}
|
|
235
|
+
|
|
167
236
|
/**
|
|
168
237
|
* Get CSS class for conversation state styling
|
|
169
238
|
* @param {string} conversationState - The conversation state
|
|
@@ -33,14 +33,14 @@ class DataCache {
|
|
|
33
33
|
projectStats: new Map(), // projectPath -> { data, timestamp }
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
-
// Cache configuration
|
|
36
|
+
// Cache configuration - balanced for performance vs memory
|
|
37
37
|
this.config = {
|
|
38
|
-
fileContentTTL:
|
|
39
|
-
parsedDataTTL:
|
|
40
|
-
computationTTL:
|
|
41
|
-
metadataTTL:
|
|
42
|
-
processTTL:
|
|
43
|
-
maxCacheSize:
|
|
38
|
+
fileContentTTL: 60000, // 1 minute for file content
|
|
39
|
+
parsedDataTTL: 30000, // 30 seconds for parsed data
|
|
40
|
+
computationTTL: 20000, // 20 seconds for expensive computations
|
|
41
|
+
metadataTTL: 10000, // 10 seconds for metadata
|
|
42
|
+
processTTL: 1000, // 1 second for process data
|
|
43
|
+
maxCacheSize: 50, // Increased to reduce evictions
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
// Dependency tracking for smart invalidation
|