crg-dev-kit 2.0.7 → 2.2.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/assets/CLAUDE.md CHANGED
@@ -1,48 +1,78 @@
1
- ## MCP Tools: code-review-graph
2
-
3
- **Graph first, grep second.** Use CRG tools before Grep/Glob/Read — fewer tokens, structural context.
4
-
5
- ### Tools that work (no extras)
6
-
7
- | Tool | When |
8
- |------|------|
9
- | `detect_changes` | Review changes risk-scored, prioritized |
10
- | `get_review_context` | Token-efficient source snippets for review |
11
- | `get_impact_radius` | Blast radius of changed files (2-hop BFS) |
12
- | `query_graph` | 8 patterns: callers_of, callees_of, imports_of, importers_of, children_of, tests_for, inheritors_of, file_summary |
13
- | `semantic_search_nodes` | Find functions/classes by keyword |
14
- | `refactor_tool` | mode=rename (preview+apply), mode=dead_code, mode=suggest |
15
- | `apply_refactor` | Execute previewed rename (10min expiry) |
16
- | `find_large_functions` | Functions/classes over N lines |
17
- | `build_or_update_graph` | Build or refresh the graph |
18
- | `list_graph_stats` | Node/edge counts, health |
19
- | `cross_repo_search` | Search across registered repos |
20
-
21
- ### Tools that need [communities] extra
22
-
23
- | Tool | Note |
24
- |------|------|
25
- | `get_architecture_overview` | **Returns empty without communities — no error** |
26
- | `list_communities` | Use min_size=20 to avoid token overflow |
27
- | `get_community` | include_members=true for deep dive |
28
- | `generate_wiki` | Also needs Ollama running |
29
-
30
- ### Broken in v2.1.0 skip these
31
-
32
- - `get_affected_flows` depends on flows (broken)
33
- - `list_flows` / `get_flow` — trace_flows TypeError bug
34
-
35
- ### Workflow
36
-
37
- 1. Graph auto-updates via hooks on file save.
38
- 2. `detect_changes` for code review.
39
- 3. `query_graph pattern="tests_for"` for coverage check.
40
- 4. `get_impact_radius` before merging.
41
- 5. Fall back to Grep/Glob/Read only when graph doesn't cover it.
42
-
43
- ### Gotchas
44
-
45
- - Communities return 0? Restart AI tool (MCP server caches old env).
46
- - Over-clustered? `list_communities(min_size=20)`.
47
- - Package name is `code-review-graph` NOT `crg`.
48
- - Linux: use pipx, not pip. Extras via `pipx inject`.
1
+ # ${PROJECT_NAME}
2
+
3
+ ## MANDATORY: Use CRG Tools First
4
+
5
+ **CRITICAL: You MUST use code-review-graph (CRG) tools for ALL code exploration. Never use Read/Glob/Grep directly.**
6
+
7
+ ### Required Workflow
8
+
9
+ 1. **Every file read** Use `get_review_context` or `query_graph` first
10
+ 2. **Code review** Use `detect_changes`
11
+ 3. **Finding tests** Use `query_graph pattern="tests_for"`
12
+ 4. **Understanding code** Use `query_graph pattern="callees_of"` or `callers_of"`
13
+ 5. **Impact analysis** Use `get_impact_radius`
14
+
15
+ ### Why This Matters
16
+
17
+ - **CRG**: 6.8x fewer tokens on file reads
18
+ - **token-optimizer**: 95% compression on tool outputs
19
+ - **mem0**: Persistent memory across sessions
20
+ - **Combined**: Up to 49x token reduction + memory that persists
21
+
22
+ ### When You MUST Use CRG
23
+
24
+ | Task | CRG Tool |
25
+ |------|----------|
26
+ | Read any file | `get_review_context` |
27
+ | Find function usages | `query_graph` with callers_of |
28
+ | Find tests for code | `query_graph pattern="tests_for"` |
29
+ | Understand imports | `query_graph pattern="imports_of"` |
30
+ | Review changes | `detect_changes` |
31
+ | See impact radius | `get_impact_radius` |
32
+ | Search code | `semantic_search_nodes` |
33
+
34
+ ### Memory: Log Your Decisions
35
+
36
+ After completing a significant task, log the decision:
37
+ ```
38
+ mem0.add("Added auth middleware. Why: Need rate limiting. Files: auth.js, rate.js. Context: 3 endpoints.")
39
+ ```
40
+
41
+ This builds persistent memory for future sessions.
42
+
43
+ ### Fallback Only If
44
+
45
+ - CRG returns insufficient info
46
+ - File not in graph yet
47
+ - Need very recent changes not yet in graph
48
+
49
+ ### Available Tools
50
+
51
+ ```
52
+ CRG (code-review-graph):
53
+ detect_changes - Risk-scored change review
54
+ get_review_context - Token-efficient snippets
55
+ get_impact_radius - Blast radius analysis
56
+ query_graph - Callers, callees, tests, imports
57
+ semantic_search - Find by meaning
58
+ build_or_update_graph - Refresh graph
59
+ list_graph_stats - Check graph health
60
+
61
+ Memory (mem0):
62
+ mem0.add - Store decision/context
63
+ mem0.search - Retrieve past context
64
+ ```
65
+
66
+ ### Quick Commands
67
+
68
+ ```bash
69
+ # Build graph when new code is added
70
+ code-review-graph build
71
+
72
+ # Check graph stats
73
+ code-review-graph list_graph_stats
74
+ ```
75
+
76
+ ---
77
+
78
+ *This file auto-generated by crg-dev-kit.*
package/lib/actions.js CHANGED
@@ -53,6 +53,9 @@ function install(targetDir, trackROI = true) {
53
53
  }
54
54
  }
55
55
 
56
+ // Don't update global CLAUDE.md - preserve user's custom rules
57
+ // The project CLAUDE.md already has all CRG instructions
58
+
56
59
  if (trackROI) {
57
60
  roi.setInstallDate(targetDir);
58
61
  }
@@ -99,11 +102,9 @@ function uninstall(targetDir) {
99
102
  function healthCheck(targetDir) {
100
103
  const results = [];
101
104
 
102
- // Check graph DB
103
105
  const dbPath = path.join(targetDir, '.code-review-graph', 'graph.db');
104
106
  results.push({ check: 'Graph DB exists', pass: fs.existsSync(dbPath) });
105
107
 
106
- // Check setup files
107
108
  results.push({ check: 'setup-crg.sh present', pass: fs.existsSync(path.join(targetDir, 'setup-crg.sh')) });
108
109
  results.push({ check: 'CLAUDE.md present', pass: fs.existsSync(path.join(targetDir, 'CLAUDE.md')) });
109
110
 
@@ -111,11 +112,8 @@ function healthCheck(targetDir) {
111
112
  execFile('code-review-graph', ['--version'], (err, stdout) => {
112
113
  results.push({ check: 'code-review-graph CLI', pass: !err, version: err ? null : (stdout || '').trim() });
113
114
 
114
- // Try getting graph stats
115
115
  if (fs.existsSync(dbPath)) {
116
116
  try {
117
- const sqlite3 = require('child_process');
118
- // Use python to read SQLite since we can't require sqlite3
119
117
  const cmd = `python3 -c "import sqlite3,json;db=sqlite3.connect('${dbPath}');r=dict(nodes=db.execute('SELECT COUNT(*) FROM nodes').fetchone()[0],edges=db.execute('SELECT COUNT(*) FROM edges').fetchone()[0]);print(json.dumps(r))"`;
120
118
  execFile('bash', ['-c', cmd], (err2, stdout2) => {
121
119
  if (!err2 && stdout2) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crg-dev-kit",
3
- "version": "2.0.7",
3
+ "version": "2.2.1",
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
@@ -1,7 +1,7 @@
1
1
  const http = require('http');
2
2
  const fs = require('fs');
3
3
  const path = require('path');
4
- const { execFile } = require('child_process');
4
+ const { execFile, exec } = require('child_process');
5
5
  const analytics = require('./lib/analytics');
6
6
  const roi = require('./lib/roi');
7
7
  const actions = require('./lib/actions');
@@ -171,6 +171,14 @@ section{max-width:clamp(320px,90vw,780px);margin:0 auto;padding:0 clamp(16px,4vw
171
171
  .dl-size{font-family:var(--m);font-size:11px;color:var(--t4);flex-shrink:0}
172
172
  .dl-btn{background:var(--a);color:#fff;padding:6px 14px;border-radius:5px;font-size:12px;font-weight:700;flex-shrink:0;transition:background .15s}
173
173
  .dl-card:hover .dl-btn{background:#c2410c}
174
+ .install-options{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:16px;margin-bottom:24px}
175
+ .install-card{background:var(--s);border:1px solid var(--b);border-radius:8px;padding:20px;display:flex;flex-direction:column}
176
+ .install-card:hover{border-color:var(--a)}
177
+ .install-header{display:flex;gap:12px;align-items:flex-start;margin-bottom:12px}
178
+ .install-icon{font-size:24px;line-height:1}
179
+ .install-title{font-weight:700;font-size:15px}
180
+ .install-desc{font-size:12px;color:var(--t3);margin-top:2px}
181
+ .install-card .btn{margin-top:auto}
174
182
  .rc{background:var(--s);border:1px solid var(--b);border-radius:8px;padding:clamp(16px,3vw,32px)}
175
183
  .rc h1{font-size:22px;font-weight:700;margin-bottom:16px;letter-spacing:-0.02em}
176
184
  .rc h2{font-size:17px;font-weight:700;margin:24px 0 8px}
@@ -236,6 +244,9 @@ footer a{color:var(--a);text-decoration:none;font-weight:600}
236
244
  .tool-name{font-family:var(--m);font-size:11px;color:var(--a);font-weight:600}
237
245
  .tool-label{font-size:14px;font-weight:600;color:var(--t)}
238
246
  .tool-desc{font-size:12px;color:var(--t3);line-height:1.4}
247
+ .btn-tool{background:var(--a);color:#fff;border:none;border-radius:4px;padding:4px 12px;font-size:11px;font-weight:600;cursor:pointer;margin-top:8px;width:100%}
248
+ .btn-tool:hover{background:#c2410c}
249
+ .btn-tool:disabled{opacity:.5;cursor:not-allowed}
239
250
  .btn{background:var(--a);color:#fff;padding:10px 20px;border:none;border-radius:6px;font-weight:700;font-size:13px;cursor:pointer;font-family:inherit;transition:background .15s}
240
251
  .btn:hover{background:#c2410c}
241
252
  .btn:focus-visible{outline:2px solid var(--a);outline-offset:2px}
@@ -245,7 +256,7 @@ footer a{color:var(--a);text-decoration:none;font-weight:600}
245
256
  .btn-secondary{background:var(--bg);color:var(--t2);border:1px solid var(--b)}
246
257
  .check-pass{color:#16a34a}
247
258
  .check-fail{color:#dc2626}
248
- .check-item{display:flex;align-items:center;gap:8px;padding:8px 0;border-bottom:1px solid var(--b)}
259
+ .check-item{display:flex;align-items:center;gap:8px;padding:8px 0;border-bottom:1px solid var(--b);color:var(--t)}
249
260
  .check-item:last-child{border-bottom:none}
250
261
  .result-box{background:var(--s);border:1px solid var(--b);border-radius:8px;padding:20px;margin-top:16px}
251
262
  .result-success{border-color:#16a34a;background:#f0fdf4}
@@ -261,9 +272,18 @@ footer a{color:var(--a);text-decoration:none;font-weight:600}
261
272
  @media(prefers-color-scheme:dark){
262
273
  :root{--bg:#1c1917;--s:#292524;--b:#44403c;--t:#fafaf9;--t2:#d6d3d1;--t3:#a8a29e;--t4:#78716c;--al:#292524;--ab:#44403c}
263
274
  .dl-card:hover,.roi-card,.stat-card,.tool-card,.project-table table,.roi-compare{background:var(--s)}
275
+ .result-box{background:var(--s)}
276
+ .result-success{background:#16653420;border-color:#22c55e}
277
+ .result-error{background:#991b1b20;border-color:#ef4444}
278
+ .check-pass{color:#22c55e}
279
+ .check-fail{color:#ef4444}
264
280
  }
265
281
  @media(prefers-color-scheme:light){
266
282
  :root{--bg:#fafaf9;--s:#fff;--b:#e7e5e4;--t:#1c1917;--t2:#57534e;--t3:#78716c;--t4:#a8a29e;--al:#fff7ed;--ab:#fed7aa}
283
+ .result-success{background:#f0fdf4;border-color:#16a34a}
284
+ .result-error{background:#fef2f2;border-color:#dc2626}
285
+ .check-pass{color:#16a34a}
286
+ .check-fail{color:#dc2626}
267
287
  }
268
288
  @media(max-width:768px){
269
289
  .analytics-grid,.roi-grid{grid-template-columns:repeat(2,1fr)}
@@ -348,31 +368,97 @@ function tabDashboard(cwd) {
348
368
  function tabInstall(cwd) {
349
369
  return `
350
370
  <section>
351
- <div class="st">Install CRG Dev Kit</div>
352
- <p style="color:var(--t2);font-size:14px;margin-bottom:16px">Copy setup scripts, CLAUDE.md, and health check to your project directory.</p>
353
- <div style="margin-bottom:16px">
354
- <label for="install-dir" style="font-size:12px;font-weight:600;color:var(--t3);display:block;margin-bottom:6px">Target Directory</label>
355
- <input class="input" id="install-dir" name="targetDir" value="${esc(cwd)}" />
371
+ <div class="st">Quick Install Maximum Token Savings</div>
372
+ <p style="color:var(--t2);font-size:14px;margin-bottom:20px">Install both CRG + token-optimizer-mcp for 95%+ token reduction.</p>
373
+ <div class="install-options">
374
+ <div class="install-card">
375
+ <div class="install-header">
376
+ <span class="install-icon">⚡</span>
377
+ <div>
378
+ <div class="install-title">Full Stack (Recommended)</div>
379
+ <div class="install-desc">CRG + token-optimizer + mem0 — 95%+ token savings + persistent memory</div>
380
+ </div>
381
+ </div>
382
+ <button class="btn" style="width:100%;margin-top:12px"
383
+ hx-post="/api/install-fullstack"
384
+ hx-target="#install-result"
385
+ hx-swap="innerHTML">
386
+ Install Full Stack
387
+ </button>
388
+ </div>
389
+
390
+ <div class="install-card">
391
+ <div class="install-header">
392
+ <span class="install-icon">📊</span>
393
+ <div>
394
+ <div class="install-title">CRG Only</div>
395
+ <div class="install-desc">Knowledge graph — reads less, understands more</div>
396
+ </div>
397
+ </div>
398
+ <button class="btn btn-secondary" style="width:100%;margin-top:12px"
399
+ hx-post="/api/install"
400
+ hx-target="#install-result"
401
+ hx-swap="innerHTML"
402
+ hx-include="#install-dir">
403
+ Install CRG Only
404
+ </button>
405
+ </div>
406
+
407
+ <div class="install-card">
408
+ <div class="install-header">
409
+ <span class="install-icon">🎯</span>
410
+ <div>
411
+ <div class="install-title">token-optimizer Only</div>
412
+ <div class="install-desc">Smart caching + compression for tool outputs</div>
413
+ </div>
414
+ </div>
415
+ <button class="btn btn-secondary" style="width:100%;margin-top:12px"
416
+ hx-post="/api/install-optimizer"
417
+ hx-target="#install-result"
418
+ hx-swap="innerHTML">
419
+ Install token-optimizer
420
+ </button>
421
+ </div>
422
+
423
+ <div class="install-card">
424
+ <div class="install-header">
425
+ <span class="install-icon">🧠</span>
426
+ <div>
427
+ <div class="install-title">mem0 Only</div>
428
+ <div class="install-desc">Persistent memory layer for sessions</div>
429
+ </div>
430
+ </div>
431
+ <button class="btn btn-secondary" style="width:100%;margin-top:12px"
432
+ hx-post="/api/install-mem0"
433
+ hx-target="#install-result"
434
+ hx-swap="innerHTML">
435
+ Install mem0
436
+ </button>
437
+ </div>
356
438
  </div>
357
- <div style="display:flex;gap:12px;flex-wrap:wrap">
358
- <button class="btn"
359
- hx-post="/api/install"
360
- hx-target="#install-result"
361
- hx-swap="innerHTML"
362
- hx-include="#install-dir"
363
- hx-indicator="#install-spinner">
364
- Install Files <span id="install-spinner" class="htmx-indicator"><span class="spinner"></span></span>
365
- </button>
366
- <button class="btn btn-danger"
367
- hx-post="/api/uninstall"
368
- hx-target="#install-result"
369
- hx-swap="innerHTML"
370
- hx-include="#install-dir"
371
- hx-indicator="#uninstall-spinner">
372
- Uninstall <span id="uninstall-spinner" class="htmx-indicator"><span class="spinner"></span></span>
373
- </button>
439
+
440
+ <div style="margin-top:24px">
441
+ <label for="install-dir" style="font-size:12px;font-weight:600;color:var(--t3);display:block;margin-bottom:6px">Target Directory (for CRG files)</label>
442
+ <input class="input" id="install-dir" name="targetDir" value="${esc(cwd)}" />
374
443
  </div>
444
+
375
445
  <div id="install-result"></div>
446
+
447
+ <div class="st" style="margin-top:32px">Why Full Stack?</div>
448
+ <div class="roi-grid" style="grid-template-columns:repeat(3,1fr)">
449
+ <div class="roi-card">
450
+ <div class="roi-value" style="color:#16a34a">6.8x</div>
451
+ <div class="roi-label">CRG Reduction</div>
452
+ </div>
453
+ <div class="roi-card">
454
+ <div class="roi-value" style="color:#16a34a">95%</div>
455
+ <div class="roi-label">token-optimizer</div>
456
+ </div>
457
+ <div class="roi-card">
458
+ <div class="roi-value" style="color:#16a34a">🧠</div>
459
+ <div class="roi-label">mem0 Memory</div>
460
+ </div>
461
+ </div>
376
462
  </section>`;
377
463
  }
378
464
 
@@ -549,17 +635,31 @@ function tabAnalytics(cwd) {
549
635
  return `<section>${roiHtml}${tokenHtml}</section>`;
550
636
  }
551
637
 
552
- function tabTools() {
638
+ function tabTools(cwd) {
553
639
  const toolCards = CRG_TOOLS.map(t => `
554
- <div class="tool-card" tabindex="0">
640
+ <div class="tool-card">
555
641
  <span class="tool-name">${t.name}</span>
556
642
  <span class="tool-label">${t.label}</span>
557
643
  <span class="tool-desc">${t.desc}</span>
644
+ <button class="btn-tool" onclick="runTool('${t.name}')">Run</button>
558
645
  </div>`).join('');
559
646
  return `<section>
560
- <div class="st">Available Tools</div>
561
- <p style="font-size:14px;color:var(--t3);margin-bottom:16px">These MCP tools are available in your projects after setup. They provide token-efficient code review and analysis.</p>
647
+ <div class="st">CRG Tools</div>
648
+ <p style="font-size:14px;color:var(--t3);margin-bottom:16px">Run tools directly from here. Click Run to execute.</p>
562
649
  <div class="tools-grid">${toolCards}</div>
650
+ <div id="tool-result" style="margin-top:16px"></div>
651
+
652
+ <div class="st" style="margin-top:32px">Graph Actions</div>
653
+ <div style="display:flex;gap:12px;flex-wrap:wrap;margin-bottom:16px">
654
+ <button class="btn" hx-post="/api/graph/update" hx-target="#graph-result" hx-swap="innerHTML">
655
+ ↻ Rebuild Graph
656
+ </button>
657
+ <button class="btn btn-secondary" hx-get="/api/graph/stats" hx-target="#graph-stats" hx-swap="innerHTML">
658
+ View Stats
659
+ </button>
660
+ </div>
661
+ <div id="graph-result"></div>
662
+ <div id="graph-stats" class="result-box" style="display:none"></div>
563
663
  </section>`;
564
664
  }
565
665
 
@@ -685,7 +785,7 @@ function start(port, noOpen) {
685
785
 
686
786
  } else if (url.pathname === '/tab/tools') {
687
787
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
688
- res.end(tabTools());
788
+ res.end(tabTools(cwd));
689
789
 
690
790
  } else if (url.pathname === '/tab/downloads') {
691
791
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
@@ -709,6 +809,60 @@ function start(port, noOpen) {
709
809
  }
710
810
  });
711
811
 
812
+ } else if (url.pathname === '/api/install-optimizer' && req.method === 'POST') {
813
+ exec('npm install -g token-optimizer-mcp', (err, stdout, stderr) => {
814
+ if (err) {
815
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
816
+ res.end(`<div class="result-box result-error"><p>Failed to install: ${esc(stderr || err.message)}</p></div>`);
817
+ return;
818
+ }
819
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
820
+ res.end(`<div class="result-box result-success"><p>✓ token-optimizer-mcp installed globally</p><p style="font-size:13px;color:var(--t3);margin-top:8px">Add to your Claude Code config to activate.</p></div>`);
821
+ });
822
+
823
+ } else if (url.pathname === '/api/install-mem0' && req.method === 'POST') {
824
+ exec('pip install mem0ai 2>&1', (err, stdout, stderr) => {
825
+ if (err) {
826
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
827
+ res.end(`<div class="result-box result-error"><p>Failed to install mem0: ${esc(stderr || err.message)}</p></div>`);
828
+ return;
829
+ }
830
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
831
+ res.end(`<div class="result-box result-success"><p>✓ mem0 installed</p><p style="font-size:13px;color:var(--t3);margin-top:8px">Persistent memory layer ready.</p></div>`);
832
+ });
833
+
834
+ } else if (url.pathname === '/api/install-fullstack' && req.method === 'POST') {
835
+ readBody(req, res, (data) => {
836
+ const targetDir = data.targetDir || cwd;
837
+ let output = '';
838
+
839
+ try {
840
+ const crgResult = actions.install(targetDir);
841
+ output += `<div class="check-item"><span class="check-pass">✓</span> CRG installed (${crgResult.copied} files)</div>`;
842
+ } catch (err) {
843
+ output += `<div class="check-item"><span class="check-fail">✗</span> CRG: ${esc(err.message)}</div>`;
844
+ }
845
+
846
+ exec('npm install -g token-optimizer-mcp', (err, stdout, stderr) => {
847
+ if (err) {
848
+ output += `<div class="check-item"><span class="check-fail">✗</span> token-optimizer: ${esc(stderr || err.message)}</div>`;
849
+ } else {
850
+ output += `<div class="check-item"><span class="check-pass">✓</span> token-optimizer installed</div>`;
851
+ }
852
+
853
+ exec('pip install mem0ai 2>&1', (err2, stdout2, stderr2) => {
854
+ if (err2) {
855
+ output += `<div class="check-item"><span class="check-fail">✗</span> mem0: ${esc(stderr2 || err2.message)}</div>`;
856
+ } else {
857
+ output += `<div class="check-item"><span class="check-pass">✓</span> mem0 (memory layer) installed</div>`;
858
+ }
859
+
860
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
861
+ res.end(`<div class="result-box result-success">${output}<p style="margin-top:16px;font-size:13px;color:var(--t3)">⚡ All 3 tools installed! See docs for configuration.</p></div>`);
862
+ });
863
+ });
864
+ });
865
+
712
866
  } else if (url.pathname === '/api/uninstall' && req.method === 'POST') {
713
867
  readBody(req, res, (data) => {
714
868
  const targetDir = data.targetDir || cwd;
@@ -744,18 +898,43 @@ function start(port, noOpen) {
744
898
 
745
899
  } else if (url.pathname === '/api/report') {
746
900
  const report = analytics.generateReport();
747
- res.writeHead(200, { 'Content-Type': 'text/markdown' });
748
- res.end(report);
901
+ const html = mdToHtml(report);
902
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
903
+ res.end(`<div class="rc">${html}</div>`);
749
904
 
750
905
  } else if (url.pathname === '/api/roi-report') {
751
906
  const report = roi.generateROIReport(cwd);
752
- res.writeHead(200, { 'Content-Type': 'text/markdown' });
753
- res.end(report);
907
+ const html = mdToHtml(report);
908
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
909
+ res.end(`<div class="rc">${html}</div>`);
910
+
911
+ } else if (url.pathname === '/api/graph/update' && req.method === 'POST') {
912
+ exec('code-review-graph build', { cwd: cwd }, (err, stdout, stderr) => {
913
+ if (err) {
914
+ res.writeHead(200, { 'Content-Type': 'text/html' });
915
+ res.end(`<div class="result-box result-error"><p>Build failed: ${esc(stderr || err.message)}</p></div>`);
916
+ return;
917
+ }
918
+ res.writeHead(200, { 'Content-Type': 'text/html' });
919
+ res.end(`<div class="result-box result-success"><p>✓ Graph updated successfully</p><p style="font-size:12px;color:var(--t3)">${esc(stdout)}</p></div>`);
920
+ });
921
+
922
+ } else if (url.pathname === '/api/graph/stats') {
923
+ exec('code-review-graph list_graph_stats', { cwd: cwd }, (err, stdout, stderr) => {
924
+ if (err) {
925
+ res.writeHead(200, { 'Content-Type': 'text/html' });
926
+ res.end(`<p style="color:var(--t3)">Graph stats unavailable</p>`);
927
+ return;
928
+ }
929
+ res.writeHead(200, { 'Content-Type': 'text/html' });
930
+ res.end(`<pre style="font-family:var(--m);font-size:12px;background:var(--bg);padding:12px;border-radius:6px">${esc(stdout)}</pre>`);
931
+ });
754
932
 
755
933
  } else if (url.pathname === '/api/daily-breakdown') {
756
934
  const breakdown = roi.generateDailyBreakdown();
757
- res.writeHead(200, { 'Content-Type': 'text/markdown' });
758
- res.end(breakdown);
935
+ const html = mdToHtml(breakdown);
936
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
937
+ res.end(`<div class="rc">${html}</div>`);
759
938
 
760
939
  } else if (url.pathname === '/api/session' && req.method === 'POST') {
761
940
  readBody(req, res, (data) => {