claude-code-templates 1.11.0 → 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/package.json +1 -2
- package/src/analytics/core/ConversationAnalyzer.js +65 -23
- package/src/analytics-web/components/AgentsPage.js +2365 -156
- package/src/analytics-web/components/App.js +11 -0
- package/src/analytics-web/components/DashboardPage.js +4 -0
- package/src/analytics-web/components/ToolDisplay.js +17 -2
- package/src/analytics-web/index.html +3005 -1059
- package/src/analytics.js +342 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-templates",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
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": {
|
|
@@ -31,7 +31,6 @@
|
|
|
31
31
|
"dev:link": "npm link",
|
|
32
32
|
"dev:unlink": "npm unlink -g claude-code-templates",
|
|
33
33
|
"pretest:commands": "npm run dev:link",
|
|
34
|
-
"prepublishOnly": "echo 'Skipping tests for emergency publish'",
|
|
35
34
|
"analytics:start": "node src/analytics.js",
|
|
36
35
|
"analytics:test": "npm run test:analytics"
|
|
37
36
|
},
|
|
@@ -101,7 +101,7 @@ class ConversationAnalyzer {
|
|
|
101
101
|
|
|
102
102
|
try {
|
|
103
103
|
// Extract project name from path
|
|
104
|
-
const projectFromPath = this.extractProjectFromPath(filePath);
|
|
104
|
+
const projectFromPath = await this.extractProjectFromPath(filePath);
|
|
105
105
|
|
|
106
106
|
// Use cached parsed conversation if available
|
|
107
107
|
const parsedMessages = await this.getParsedConversation(filePath);
|
|
@@ -113,6 +113,9 @@ class ConversationAnalyzer {
|
|
|
113
113
|
// Calculate tool usage data with caching
|
|
114
114
|
const toolUsage = await this.getCachedToolUsage(filePath, parsedMessages);
|
|
115
115
|
|
|
116
|
+
const projectFromConversation = await this.extractProjectFromConversation(filePath);
|
|
117
|
+
const finalProject = projectFromConversation || projectFromPath;
|
|
118
|
+
|
|
116
119
|
const conversation = {
|
|
117
120
|
id: filename.replace('.jsonl', ''),
|
|
118
121
|
filename: filename,
|
|
@@ -125,7 +128,7 @@ class ConversationAnalyzer {
|
|
|
125
128
|
tokenUsage: tokenUsage,
|
|
126
129
|
modelInfo: modelInfo,
|
|
127
130
|
toolUsage: toolUsage,
|
|
128
|
-
project:
|
|
131
|
+
project: finalProject,
|
|
129
132
|
status: stateCalculator.determineConversationStatus(parsedMessages, stats.mtime),
|
|
130
133
|
conversationState: stateCalculator.determineConversationState(parsedMessages, stats.mtime),
|
|
131
134
|
statusSquares: await this.getCachedStatusSquares(filePath, parsedMessages),
|
|
@@ -433,11 +436,11 @@ class ConversationAnalyzer {
|
|
|
433
436
|
}
|
|
434
437
|
|
|
435
438
|
/**
|
|
436
|
-
* Extract project name from Claude directory file path
|
|
439
|
+
* Extract project name from Claude directory file path using settings.json
|
|
437
440
|
* @param {string} filePath - Full path to conversation file
|
|
438
|
-
* @returns {string|null} Project name or null
|
|
441
|
+
* @returns {Promise<string|null>} Project name or null
|
|
439
442
|
*/
|
|
440
|
-
extractProjectFromPath(filePath) {
|
|
443
|
+
async extractProjectFromPath(filePath) {
|
|
441
444
|
// Extract project name from file path like:
|
|
442
445
|
// /Users/user/.claude/projects/-Users-user-Projects-MyProject/conversation.jsonl
|
|
443
446
|
const pathParts = filePath.split('/');
|
|
@@ -445,34 +448,73 @@ class ConversationAnalyzer {
|
|
|
445
448
|
|
|
446
449
|
if (projectIndex !== -1 && projectIndex + 1 < pathParts.length) {
|
|
447
450
|
const projectDir = pathParts[projectIndex + 1];
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
.
|
|
452
|
-
.
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
451
|
+
|
|
452
|
+
// Try to read the settings.json file for this project
|
|
453
|
+
try {
|
|
454
|
+
const projectPath = path.join(path.dirname(filePath)); // Directory containing the conversation file
|
|
455
|
+
const settingsPath = path.join(projectPath, 'settings.json');
|
|
456
|
+
|
|
457
|
+
if (await fs.pathExists(settingsPath)) {
|
|
458
|
+
const settingsContent = await fs.readFile(settingsPath, 'utf8');
|
|
459
|
+
const settings = JSON.parse(settingsContent);
|
|
460
|
+
|
|
461
|
+
if (settings.projectName) {
|
|
462
|
+
return settings.projectName;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// If no projectName in settings, try to extract from projectPath
|
|
466
|
+
if (settings.projectPath) {
|
|
467
|
+
return path.basename(settings.projectPath);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
} catch (error) {
|
|
471
|
+
// If we can't read settings.json, fall back to parsing the directory name
|
|
472
|
+
console.warn(chalk.yellow(`Warning: Could not read settings.json for project ${projectDir}:`, error.message));
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Fallback: we'll extract project name from conversation content instead
|
|
476
|
+
// For now, return null to trigger reading from conversation file
|
|
477
|
+
return null;
|
|
456
478
|
}
|
|
457
479
|
|
|
458
480
|
return null;
|
|
459
481
|
}
|
|
460
482
|
|
|
483
|
+
|
|
461
484
|
/**
|
|
462
485
|
* Attempt to extract project information from conversation content
|
|
463
|
-
* @param {
|
|
464
|
-
* @returns {string} Project name or 'Unknown'
|
|
465
|
-
*/
|
|
466
|
-
extractProjectFromConversation(
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
486
|
+
* @param {string} filePath - Path to the conversation file
|
|
487
|
+
* @returns {Promise<string>} Project name or 'Unknown'
|
|
488
|
+
*/
|
|
489
|
+
async extractProjectFromConversation(filePath) {
|
|
490
|
+
try {
|
|
491
|
+
// Read the conversation file and look for cwd field
|
|
492
|
+
const content = await this.getFileContent(filePath);
|
|
493
|
+
const lines = content.trim().split('\n').filter(line => line.trim());
|
|
494
|
+
|
|
495
|
+
for (const line of lines.slice(0, 10)) { // Check first 10 lines
|
|
496
|
+
try {
|
|
497
|
+
const item = JSON.parse(line);
|
|
498
|
+
|
|
499
|
+
// Look for cwd field in the message
|
|
500
|
+
if (item.cwd) {
|
|
501
|
+
const projectName = path.basename(item.cwd);
|
|
502
|
+
return projectName;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Also check if it's in nested objects
|
|
506
|
+
if (item.message && item.message.cwd) {
|
|
507
|
+
return path.basename(item.message.cwd);
|
|
508
|
+
}
|
|
509
|
+
} catch (parseError) {
|
|
510
|
+
// Skip invalid JSON lines
|
|
511
|
+
continue;
|
|
473
512
|
}
|
|
474
513
|
}
|
|
514
|
+
} catch (error) {
|
|
515
|
+
console.warn(chalk.yellow(`Warning: Could not extract project from conversation ${filePath}:`, error.message));
|
|
475
516
|
}
|
|
517
|
+
|
|
476
518
|
return 'Unknown';
|
|
477
519
|
}
|
|
478
520
|
|