crg-dev-kit 2.0.0 → 2.0.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/lib/analytics.js CHANGED
@@ -156,9 +156,14 @@ function getProjectStats(projectPath) {
156
156
  };
157
157
  }
158
158
 
159
- function getAllStats() {
159
+ function getAllStats(scopeProject) {
160
160
  const sessions = readJSON(SESSIONS_FILE, []);
161
- const completedSessions = sessions.filter(s => s.status === 'completed');
161
+ let completedSessions = sessions.filter(s => s.status === 'completed');
162
+
163
+ if (scopeProject) {
164
+ const resolved = path.resolve(scopeProject);
165
+ completedSessions = completedSessions.filter(s => s.project === resolved);
166
+ }
162
167
 
163
168
  if (completedSessions.length === 0) return null;
164
169
 
package/lib/roi.js CHANGED
@@ -6,10 +6,20 @@ function getInstallDate(projectPath) {
6
6
  const installFile = path.join(ANALYTICS_DIR, 'install.json');
7
7
  try {
8
8
  const data = JSON.parse(fs.readFileSync(installFile, 'utf8'));
9
+ // Support both old format (single object) and new format (keyed by path)
10
+ if (data.projectPath) {
11
+ // Old format: { projectPath, installedAt }
12
+ if (projectPath) return data.projectPath === projectPath ? data.installedAt : null;
13
+ return data.installedAt;
14
+ }
15
+ // New format: { "/path": { installedAt } }
9
16
  if (projectPath) {
10
- return data.projectPath === projectPath ? data.installedAt : null;
17
+ const resolved = path.resolve(projectPath);
18
+ return data[resolved] ? data[resolved].installedAt : null;
11
19
  }
12
- return data.installedAt;
20
+ // Return first entry
21
+ const first = Object.values(data)[0];
22
+ return first ? first.installedAt : null;
13
23
  } catch {
14
24
  return null;
15
25
  }
@@ -18,12 +28,16 @@ function getInstallDate(projectPath) {
18
28
  function setInstallDate(projectPath) {
19
29
  ensureDir();
20
30
  const installFile = path.join(ANALYTICS_DIR, 'install.json');
21
- const record = {
22
- projectPath: projectPath,
23
- installedAt: new Date().toISOString()
24
- };
25
- fs.writeFileSync(installFile, JSON.stringify(record, null, 2));
26
- return record;
31
+ const resolved = path.resolve(projectPath);
32
+ let data = {};
33
+ try { data = JSON.parse(fs.readFileSync(installFile, 'utf8')); } catch {}
34
+ // Migrate old format
35
+ if (data.projectPath) {
36
+ data = { [data.projectPath]: { installedAt: data.installedAt } };
37
+ }
38
+ data[resolved] = { installedAt: new Date().toISOString() };
39
+ fs.writeFileSync(installFile, JSON.stringify(data, null, 2));
40
+ return data[resolved];
27
41
  }
28
42
 
29
43
  function parseHistory(fromDate, toDate) {
@@ -161,11 +175,18 @@ function calculateROI(projectPath) {
161
175
  };
162
176
  }
163
177
 
164
- function getAllProjectsROI() {
178
+ function getAllProjectsROI(scopeProject) {
179
+ if (scopeProject) {
180
+ return calculateROI(scopeProject);
181
+ }
165
182
  const installFile = path.join(ANALYTICS_DIR, 'install.json');
166
183
  try {
167
184
  const installData = JSON.parse(fs.readFileSync(installFile, 'utf8'));
168
- return calculateROI(installData.projectPath);
185
+ // Old format
186
+ if (installData.projectPath) return calculateROI(installData.projectPath);
187
+ // New format — return first
188
+ const first = Object.keys(installData)[0];
189
+ return first ? calculateROI(first) : { error: 'No installation found', installed: false };
169
190
  } catch {
170
191
  return { error: 'No installation found', installed: false };
171
192
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crg-dev-kit",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "One-click setup for code-review-graph — AI-powered codebase knowledge graph with token analytics",
5
5
  "main": "server.js",
6
6
  "bin": {
package/server.js CHANGED
@@ -359,10 +359,10 @@ function tabStatusFragment(statusData) {
359
359
  </section>`;
360
360
  }
361
361
 
362
- function tabAnalytics() {
362
+ function tabAnalytics(cwd) {
363
363
  let roiHtml = '';
364
364
  try {
365
- const roiData = roi.getAllProjectsROI();
365
+ const roiData = roi.getAllProjectsROI(cwd);
366
366
  if (roiData.installed) {
367
367
  const verdictColor = roiData.roi.verdict === 'positive' ? '#16a34a' : roiData.roi.verdict === 'neutral' ? '#ea580c' : '#dc2626';
368
368
  const verdictIcon = roiData.roi.verdict === 'positive' ? '&#10003;' : roiData.roi.verdict === 'neutral' ? '&#8722;' : '&#9888;';
@@ -428,7 +428,7 @@ function tabAnalytics() {
428
428
  }
429
429
 
430
430
  let tokenHtml = '';
431
- const analyticsData = analytics.getAllStats();
431
+ const analyticsData = analytics.getAllStats(cwd);
432
432
  if (analyticsData) {
433
433
  const savingsColor = analyticsData.avgSavingsPercent >= 70 ? '#16a34a' : analyticsData.avgSavingsPercent >= 50 ? '#ea580c' : '#dc2626';
434
434
  tokenHtml = `
@@ -563,7 +563,15 @@ function readBody(req, res, cb) {
563
563
  req.on('end', () => {
564
564
  if (res.writableEnded) return;
565
565
  try {
566
- cb(JSON.parse(body));
566
+ const ct = (req.headers['content-type'] || '');
567
+ let data;
568
+ if (ct.includes('application/json')) {
569
+ data = JSON.parse(body);
570
+ } else {
571
+ // Parse URL-encoded form data (htmx default)
572
+ data = Object.fromEntries(new URLSearchParams(body));
573
+ }
574
+ cb(data);
567
575
  } catch (e) {
568
576
  res.writeHead(400, { 'Content-Type': 'application/json' });
569
577
  res.end(JSON.stringify({ error: e.message }));
@@ -607,7 +615,7 @@ function start(port, noOpen) {
607
615
 
608
616
  } else if (url.pathname === '/tab/analytics') {
609
617
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
610
- res.end(tabAnalytics());
618
+ res.end(tabAnalytics(cwd));
611
619
 
612
620
  } else if (url.pathname === '/tab/tools') {
613
621
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
@@ -659,12 +667,12 @@ function start(port, noOpen) {
659
667
 
660
668
  /* ── Existing JSON API endpoints ────────────────────────────── */
661
669
  } else if (url.pathname === '/api/analytics') {
662
- const data = analytics.getAllStats();
670
+ const data = analytics.getAllStats(cwd);
663
671
  res.writeHead(200, { 'Content-Type': 'application/json' });
664
672
  res.end(JSON.stringify(data || { message: 'No analytics data yet' }));
665
673
 
666
674
  } else if (url.pathname === '/api/roi') {
667
- const data = roi.getAllProjectsROI();
675
+ const data = roi.getAllProjectsROI(cwd);
668
676
  res.writeHead(200, { 'Content-Type': 'application/json' });
669
677
  res.end(JSON.stringify(data));
670
678
 
@@ -768,7 +776,7 @@ function start(port, noOpen) {
768
776
  const url = `http://localhost:${availablePort}`;
769
777
  const portMsg = availablePort !== port ? ` (port ${port} was busy, using ${availablePort})` : '';
770
778
  console.log(`\n \x1b[36mCRG Dev Kit\x1b[0m running at \x1b[1m${url}\x1b[0m${portMsg}\n`);
771
- const analyticsData = analytics.getAllStats();
779
+ const analyticsData = analytics.getAllStats(cwd);
772
780
  if (analyticsData) {
773
781
  console.log(` \x1b[32m${analyticsData.avgSavingsPercent}% avg token savings\x1b[0m across \x1b[1m${analyticsData.totalSessions}\x1b[0m sessions\n`);
774
782
  }