crg-dev-kit 1.0.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/lib/roi.js ADDED
@@ -0,0 +1,250 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const ANALYTICS_DIR = path.join(process.env.HOME || process.env.USERPROFILE, '.crg-analytics');
5
+ const HISTORY_FILE = path.join(process.env.HOME || process.env.USERPROFILE, '.claude', 'history.jsonl');
6
+
7
+ function ensureDir() {
8
+ if (!fs.existsSync(ANALYTICS_DIR)) {
9
+ fs.mkdirSync(ANALYTICS_DIR, { recursive: true });
10
+ }
11
+ }
12
+
13
+ function getInstallDate(projectPath) {
14
+ const installFile = path.join(ANALYTICS_DIR, 'install.json');
15
+ try {
16
+ const data = JSON.parse(fs.readFileSync(installFile, 'utf8'));
17
+ if (projectPath) {
18
+ return data.projectPath === projectPath ? data.installedAt : null;
19
+ }
20
+ return data.installedAt;
21
+ } catch {
22
+ return null;
23
+ }
24
+ }
25
+
26
+ function setInstallDate(projectPath) {
27
+ ensureDir();
28
+ const installFile = path.join(ANALYTICS_DIR, 'install.json');
29
+ const record = {
30
+ projectPath: projectPath,
31
+ installedAt: new Date().toISOString()
32
+ };
33
+ fs.writeFileSync(installFile, JSON.stringify(record, null, 2));
34
+ return record;
35
+ }
36
+
37
+ function parseHistory(fromDate, toDate) {
38
+ const from = new Date(fromDate).getTime();
39
+ const to = new Date(toDate).getTime();
40
+
41
+ const stats = {
42
+ totalPrompts: 0,
43
+ totalTokens: 0,
44
+ sessions: new Map(),
45
+ modelUsage: {},
46
+ dailyTokens: {},
47
+ dailyPrompts: {}
48
+ };
49
+
50
+ if (!fs.existsSync(HISTORY_FILE)) {
51
+ return stats;
52
+ }
53
+
54
+ const lines = fs.readFileSync(HISTORY_FILE, 'utf8').split('\n').filter(Boolean);
55
+
56
+ for (const line of lines) {
57
+ try {
58
+ const entry = JSON.parse(line);
59
+ const ts = new Date(entry.timestamp || entry.ts || Date.now()).getTime();
60
+
61
+ if (ts < from || ts > to) continue;
62
+
63
+ stats.totalPrompts++;
64
+
65
+ const tokens = entry.tokens || entry.input_tokens + (entry.output_tokens || 0) || 0;
66
+ stats.totalTokens += tokens;
67
+
68
+ const sessionId = entry.session_id || entry.sessionId || 'unknown';
69
+ if (!stats.sessions.has(sessionId)) {
70
+ stats.sessions.set(sessionId, { prompts: 0, tokens: 0 });
71
+ }
72
+ const sess = stats.sessions.get(sessionId);
73
+ sess.prompts++;
74
+ sess.tokens += tokens;
75
+
76
+ const model = entry.model || entry.model_name || 'claude';
77
+ stats.modelUsage[model] = (stats.modelUsage[model] || 0) + tokens;
78
+
79
+ const day = new Date(ts).toISOString().split('T')[0];
80
+ stats.dailyTokens[day] = (stats.dailyTokens[day] || 0) + tokens;
81
+ stats.dailyPrompts[day] = (stats.dailyPrompts[day] || 0) + 1;
82
+ } catch {
83
+ continue;
84
+ }
85
+ }
86
+
87
+ const sessionCount = stats.sessions.size;
88
+ return {
89
+ totalPrompts: stats.totalPrompts,
90
+ totalTokens: stats.totalTokens,
91
+ avgTokensPerSession: sessionCount > 0 ? Math.round(stats.totalTokens / sessionCount) : 0,
92
+ avgTokensPerPrompt: stats.totalPrompts > 0 ? Math.round(stats.totalTokens / stats.totalPrompts) : 0,
93
+ sessionCount,
94
+ modelUsage: stats.modelUsage,
95
+ dailyTokens: stats.dailyTokens,
96
+ dailyPrompts: stats.dailyPrompts
97
+ };
98
+ }
99
+
100
+ function calculateROI(projectPath) {
101
+ const installDate = getInstallDate(projectPath);
102
+
103
+ if (!installDate) {
104
+ return {
105
+ error: 'CRG not installed. Run: npx crg-dev-kit install',
106
+ installed: false
107
+ };
108
+ }
109
+
110
+ const now = new Date();
111
+ const thirtyDaysBefore = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
112
+
113
+ const baselineStart = thirtyDaysBefore < new Date(installDate) ? thirtyDaysBefore : new Date(new Date(installDate).getTime() - 30 * 24 * 60 * 60 * 1000);
114
+ const baselineEnd = new Date(installDate);
115
+
116
+ const postStart = new Date(installDate);
117
+ const postEnd = now;
118
+
119
+ const baseline = parseHistory(baselineStart, baselineEnd);
120
+ const post = parseHistory(postStart, postEnd);
121
+
122
+ const baselineAvg = baseline.avgTokensPerSession || 1;
123
+ const postAvg = post.avgTokensPerSession || 0;
124
+
125
+ const tokenDelta = baselineAvg - postAvg;
126
+ const tokenSavingsPercent = baselineAvg > 0 ? Math.round((tokenDelta / baselineAvg) * 100) : 0;
127
+
128
+ const baselinePrompts = baseline.totalPrompts || 1;
129
+ const postPrompts = post.totalPrompts || 0;
130
+ const promptReduction = baselinePrompts - postPrompts;
131
+
132
+ const avgTokensPerPromptBefore = baseline.avgTokensPerPrompt || 1;
133
+ const avgTokensPerPromptAfter = post.avgTokensPerPrompt || 0;
134
+ const tokensSaved = (avgTokensPerPromptBefore - avgTokensPerPromptAfter) * postPrompts;
135
+
136
+ const timeSavedSeconds = promptReduction * 30;
137
+ const timeSavedHours = Math.round(timeSavedSeconds / 3600 * 10) / 10;
138
+
139
+ const costPerMillion = 10;
140
+ const estimatedCostSavings = Math.round(tokensSaved / 1000000 * costPerMillion * 100) / 100;
141
+
142
+ return {
143
+ installed: true,
144
+ installDate,
145
+ projectPath,
146
+ baseline: {
147
+ period: { start: baselineStart.toISOString(), end: baselineEnd.toISOString() },
148
+ totalPrompts: baseline.totalPrompts,
149
+ totalTokens: baseline.totalTokens,
150
+ avgTokensPerSession: baseline.avgTokensPerSession,
151
+ sessionCount: baseline.sessionCount
152
+ },
153
+ post: {
154
+ period: { start: postStart.toISOString(), end: postEnd.toISOString() },
155
+ totalPrompts: post.totalPrompts,
156
+ totalTokens: post.totalTokens,
157
+ avgTokensPerSession: post.avgTokensPerSession,
158
+ sessionCount: post.sessionCount
159
+ },
160
+ roi: {
161
+ tokenSavingsPercent,
162
+ tokenDelta,
163
+ tokensSaved: Math.max(0, tokensSaved),
164
+ promptReduction: Math.max(0, promptReduction),
165
+ timeSavedHours,
166
+ estimatedCostSavings,
167
+ verdict: tokenSavingsPercent > 10 ? 'positive' : tokenSavingsPercent > 0 ? 'neutral' : 'negative'
168
+ }
169
+ };
170
+ }
171
+
172
+ function getAllProjectsROI() {
173
+ const installFile = path.join(ANALYTICS_DIR, 'install.json');
174
+ try {
175
+ const installData = JSON.parse(fs.readFileSync(installFile, 'utf8'));
176
+ return calculateROI(installData.projectPath);
177
+ } catch {
178
+ return { error: 'No installation found', installed: false };
179
+ }
180
+ }
181
+
182
+ function generateROIReport(projectPath) {
183
+ const roi = calculateROI(projectPath);
184
+
185
+ if (roi.error) {
186
+ return `# CRG ROI Report\n\n${roi.error}`;
187
+ }
188
+
189
+ let report = `# CRG ROI Report
190
+
191
+ **Project**: ${roi.projectPath}
192
+ **Installed**: ${new Date(roi.installDate).toLocaleDateString()}
193
+
194
+ ## Summary
195
+
196
+ | Metric | Before CRG | After CRG | Change |
197
+ |--------|:----------:|:---------:|--------|
198
+ | Avg Tokens/Session | ${roi.baseline.avgTokensPerSession.toLocaleString()} | ${roi.post.avgTokensPerSession.toLocaleString()} | ${roi.roi.tokenDelta >= 0 ? '-' : '+'}${Math.abs(roi.roi.tokenDelta).toLocaleString()} |
199
+ | Total Prompts | ${roi.baseline.totalPrompts} | ${roi.post.totalPrompts} | ${roi.roi.promptReduction >= 0 ? '-' : '+'}${Math.abs(roi.roi.promptReduction)} |
200
+ | Sessions | ${roi.baseline.sessionCount} | ${roi.post.sessionCount} | ${roi.post.sessionCount - roi.baseline.sessionCount} |
201
+
202
+ ## ROI Metrics
203
+
204
+ | Metric | Value |
205
+ |--------|-------|
206
+ | **Token Savings** | ${roi.roi.tokenSavingsPercent}% |
207
+ | **Tokens Saved** | ${roi.roi.tokensSaved.toLocaleString()} |
208
+ | **Time Saved** | ${roi.roi.timeSavedHours} hours |
209
+ | **Est. Cost Savings** | $${roi.roi.estimatedCostSavings} |
210
+ | **Verdict** | ${roi.roi.verdict === 'positive' ? '✅ CRG is helping' : roi.roi.verdict === 'neutral' ? '➖ No significant change' : '⚠️ CRG may not be helping'}
211
+
212
+ ---
213
+
214
+ *Generated: ${new Date().toISOString()}*
215
+ *Data source: ~/.claude/history.jsonl*
216
+ `;
217
+
218
+ return report;
219
+ }
220
+
221
+ function generateDailyBreakdown(fromDate, toDate) {
222
+ const data = parseHistory(fromDate, toDate);
223
+ const days = Object.keys(data.dailyTokens).sort();
224
+
225
+ if (days.length === 0) {
226
+ return 'No usage data found in the specified period.';
227
+ }
228
+
229
+ let report = `# Daily Usage Breakdown
230
+
231
+ | Date | Tokens | Prompts |
232
+ |------|-------:|--------:|
233
+ `;
234
+
235
+ days.forEach(day => {
236
+ report += `| ${day} | ${data.dailyTokens[day].toLocaleString()} | ${data.dailyPrompts[day]} |\n`;
237
+ });
238
+
239
+ return report;
240
+ }
241
+
242
+ module.exports = {
243
+ getInstallDate,
244
+ setInstallDate,
245
+ parseHistory,
246
+ calculateROI,
247
+ getAllProjectsROI,
248
+ generateROIReport,
249
+ generateDailyBreakdown
250
+ };
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "crg-dev-kit",
3
+ "version": "1.0.0",
4
+ "description": "One-click setup for code-review-graph — AI-powered codebase knowledge graph with token analytics",
5
+ "main": "server.js",
6
+ "bin": {
7
+ "crg-dev-kit": "./bin/cli.js"
8
+ },
9
+ "files": [
10
+ "bin/",
11
+ "assets/",
12
+ "lib/",
13
+ "server.js"
14
+ ],
15
+ "scripts": {
16
+ "start": "node server.js",
17
+ "test": "node tests/*.test.js"
18
+ },
19
+ "keywords": [
20
+ "code-review",
21
+ "knowledge-graph",
22
+ "mcp",
23
+ "claude-code",
24
+ "cursor",
25
+ "ai-tools",
26
+ "developer-tools",
27
+ "token-analytics"
28
+ ],
29
+ "author": "shloke",
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/tirth8205/code-review-graph"
34
+ },
35
+ "engines": {
36
+ "node": ">=16"
37
+ }
38
+ }