repo-codegraph 0.0.0-stage → 0.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.
Files changed (74) hide show
  1. package/README.md +462 -2
  2. package/dist/analyzer/analyzeProject.d.ts +17 -0
  3. package/dist/analyzer/analyzeProject.d.ts.map +1 -0
  4. package/dist/analyzer/analyzeProject.js +338 -0
  5. package/dist/analyzer/analyzeProject.js.map +1 -0
  6. package/dist/analyzer/buildDefinitionGraph.d.ts +47 -0
  7. package/dist/analyzer/buildDefinitionGraph.d.ts.map +1 -0
  8. package/dist/analyzer/buildDefinitionGraph.js +60 -0
  9. package/dist/analyzer/buildDefinitionGraph.js.map +1 -0
  10. package/dist/analyzer/incrementalIndex.d.ts +21 -0
  11. package/dist/analyzer/incrementalIndex.d.ts.map +1 -0
  12. package/dist/analyzer/incrementalIndex.js +115 -0
  13. package/dist/analyzer/incrementalIndex.js.map +1 -0
  14. package/dist/cli.d.ts +3 -0
  15. package/dist/cli.d.ts.map +1 -0
  16. package/dist/cli.js +249 -0
  17. package/dist/cli.js.map +1 -0
  18. package/dist/files/discoverSourceFiles.d.ts +8 -0
  19. package/dist/files/discoverSourceFiles.d.ts.map +1 -0
  20. package/dist/files/discoverSourceFiles.js +46 -0
  21. package/dist/files/discoverSourceFiles.js.map +1 -0
  22. package/dist/git/diffImpact.d.ts +22 -0
  23. package/dist/git/diffImpact.d.ts.map +1 -0
  24. package/dist/git/diffImpact.js +113 -0
  25. package/dist/git/diffImpact.js.map +1 -0
  26. package/dist/graph/DirectedGraph.d.ts +44 -0
  27. package/dist/graph/DirectedGraph.d.ts.map +1 -0
  28. package/dist/graph/DirectedGraph.js +258 -0
  29. package/dist/graph/DirectedGraph.js.map +1 -0
  30. package/dist/graph/types.d.ts +13 -0
  31. package/dist/graph/types.d.ts.map +1 -0
  32. package/dist/graph/types.js +2 -0
  33. package/dist/graph/types.js.map +1 -0
  34. package/dist/index.d.ts +21 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +13 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/mcp/projectSession.d.ts +18 -0
  39. package/dist/mcp/projectSession.d.ts.map +1 -0
  40. package/dist/mcp/projectSession.js +86 -0
  41. package/dist/mcp/projectSession.js.map +1 -0
  42. package/dist/mcp/server.d.ts +23 -0
  43. package/dist/mcp/server.d.ts.map +1 -0
  44. package/dist/mcp/server.js +512 -0
  45. package/dist/mcp/server.js.map +1 -0
  46. package/dist/mcp/stdio.d.ts +3 -0
  47. package/dist/mcp/stdio.d.ts.map +1 -0
  48. package/dist/mcp/stdio.js +35 -0
  49. package/dist/mcp/stdio.js.map +1 -0
  50. package/dist/parser/extractDeclarations.d.ts +30 -0
  51. package/dist/parser/extractDeclarations.d.ts.map +1 -0
  52. package/dist/parser/extractDeclarations.js +83 -0
  53. package/dist/parser/extractDeclarations.js.map +1 -0
  54. package/dist/parser/parseSourceFile.d.ts +4 -0
  55. package/dist/parser/parseSourceFile.d.ts.map +1 -0
  56. package/dist/parser/parseSourceFile.js +9 -0
  57. package/dist/parser/parseSourceFile.js.map +1 -0
  58. package/dist/persistence/jsonPersistence.d.ts +7 -0
  59. package/dist/persistence/jsonPersistence.d.ts.map +1 -0
  60. package/dist/persistence/jsonPersistence.js +98 -0
  61. package/dist/persistence/jsonPersistence.js.map +1 -0
  62. package/dist/queries/analyzeImpact.d.ts +14 -0
  63. package/dist/queries/analyzeImpact.d.ts.map +1 -0
  64. package/dist/queries/analyzeImpact.js +56 -0
  65. package/dist/queries/analyzeImpact.js.map +1 -0
  66. package/dist/queries/codeGraphQueries.d.ts +7 -0
  67. package/dist/queries/codeGraphQueries.d.ts.map +1 -0
  68. package/dist/queries/codeGraphQueries.js +86 -0
  69. package/dist/queries/codeGraphQueries.js.map +1 -0
  70. package/dist/visualization/viewer.d.ts +17 -0
  71. package/dist/visualization/viewer.d.ts.map +1 -0
  72. package/dist/visualization/viewer.js +349 -0
  73. package/dist/visualization/viewer.js.map +1 -0
  74. package/package.json +55 -3
@@ -0,0 +1,349 @@
1
+ import { spawn } from "node:child_process";
2
+ import { mkdir, writeFile } from "node:fs/promises";
3
+ import { createServer } from "node:http";
4
+ import path from "node:path";
5
+ export function createVisualizationDocument(analysis, openMode = "ide") {
6
+ const payload = JSON.stringify({
7
+ rootDirectory: analysis.rootDirectory,
8
+ nodes: analysis.graph.getNodes(),
9
+ edges: analysis.graph.getEdges(),
10
+ openMode,
11
+ }).replaceAll("<", "\\u003c");
12
+ return `<!doctype html>
13
+ <html lang="en">
14
+ <head>
15
+ <meta charset="utf-8">
16
+ <meta name="viewport" content="width=device-width, initial-scale=1">
17
+ <title>Codegraph</title>
18
+ <style>
19
+ :root { color-scheme: dark; font-family: Inter, ui-sans-serif, system-ui, sans-serif; }
20
+ * { box-sizing: border-box; }
21
+ body { margin: 0; overflow: hidden; background: #0d0e17; color: #f5f6fb; }
22
+ canvas { display: block; width: 100vw; height: 100vh; cursor: grab; }
23
+ canvas.dragging { cursor: grabbing; }
24
+ .panel { position: fixed; z-index: 2; background: rgba(18,20,32,.92); border: 1px solid #30344b; border-radius: 12px; box-shadow: 0 16px 48px #0008; backdrop-filter: blur(12px); }
25
+ #controls { top: 16px; left: 16px; width: min(340px, calc(100vw - 32px)); padding: 16px; }
26
+ h1 { font-size: 18px; margin: 0 0 4px; }
27
+ .subtitle, .hint { color: #9da4bd; font-size: 12px; }
28
+ input[type=search] { width: 100%; margin: 14px 0 10px; padding: 10px 12px; color: inherit; background: #0d0e17; border: 1px solid #3b405b; border-radius: 8px; outline: none; }
29
+ input[type=search]:focus { border-color: #74b9ff; }
30
+ .ask-row { display: flex; gap: 7px; margin: 12px 0 8px; }
31
+ #question { flex: 1; min-width: 0; padding: 9px 10px; color: inherit; background: #0d0e17; border: 1px solid #3b405b; border-radius: 8px; outline: none; }
32
+ #question:focus { border-color: #74b9ff; }
33
+ #ask { margin: 0; }
34
+ #answer { display: none; margin: 0 0 12px; padding: 9px 10px; border-radius: 8px; background: #191c2c; color: #cbd0e2; font-size: 12px; line-height: 1.4; max-height: 130px; overflow: auto; }
35
+ #answer.visible { display: block; }
36
+ #answer button { display: block; width: 100%; margin: 4px 0 0; padding: 4px 6px; text-align: left; border: 0; background: transparent; color: #7fc8ff; }
37
+ #answer .result { display: flex; align-items: center; gap: 6px; }
38
+ #answer .result button { flex: 1; width: auto; }
39
+ a.source-link { color: #7fc8ff; text-decoration: none; }
40
+ a.source-link:hover { text-decoration: underline; }
41
+ #answer a.source-link { flex: none; font-size: 11px; white-space: nowrap; }
42
+ .filters { display: flex; flex-wrap: wrap; gap: 10px 14px; font-size: 12px; }
43
+ .legend-title { margin: 12px 0 7px; color: #9da4bd; font-size: 11px; text-transform: uppercase; letter-spacing: .08em; }
44
+ .node-legend { display: grid; grid-template-columns: repeat(3, max-content); gap: 7px 14px; color: #d8dbea; font-size: 12px; }
45
+ .node-legend span { display: flex; align-items: center; gap: 6px; }
46
+ label { display: flex; align-items: center; gap: 6px; }
47
+ .swatch { width: 9px; height: 9px; border-radius: 50%; }
48
+ button { margin-top: 12px; padding: 7px 10px; border: 1px solid #3b405b; border-radius: 7px; background: #252941; color: inherit; cursor: pointer; }
49
+ button:hover { background: #303651; }
50
+ #details { right: 16px; top: 16px; width: min(360px, calc(100vw - 32px)); padding: 16px; display: none; }
51
+ #details.visible { display: block; }
52
+ #details h2 { margin: 0 24px 8px 0; font-size: 16px; overflow-wrap: anywhere; }
53
+ #details dl { margin: 0; display: grid; grid-template-columns: 92px 1fr; gap: 6px 10px; font-size: 12px; }
54
+ #details dt { color: #9da4bd; }
55
+ #details dd { margin: 0; overflow-wrap: anywhere; }
56
+ #close { position: absolute; right: 10px; top: 4px; border: 0; background: none; font-size: 20px; }
57
+ #status { position: fixed; left: 16px; bottom: 14px; color: #a9aec2; font-size: 11px; pointer-events: none; }
58
+ @media (max-width: 720px) { #details { top: auto; bottom: 40px; } }
59
+ </style>
60
+ </head>
61
+ <body>
62
+ <canvas id="graph" aria-label="Interactive code graph"></canvas>
63
+ <section id="controls" class="panel">
64
+ <h1>Codegraph</h1>
65
+ <div id="summary" class="subtitle"></div>
66
+ <div class="ask-row"><input id="question" placeholder="Ask: What calls analyzeProject?"><button id="ask">Ask</button></div>
67
+ <div id="answer" aria-live="polite"></div>
68
+ <input id="search" type="search" placeholder="Search files and symbols…" autocomplete="off">
69
+ <div class="legend-title">Node colors</div>
70
+ <div class="node-legend" aria-label="Node color legend">
71
+ <span><i class="swatch" style="background:#4cc9f0"></i>File</span>
72
+ <span><i class="swatch" style="background:#ff6b6b"></i>Function</span>
73
+ <span><i class="swatch" style="background:#ffd166"></i>Class</span>
74
+ <span><i class="swatch" style="background:#70e000"></i>Interface</span>
75
+ <span><i class="swatch" style="background:#c77dff"></i>Method</span>
76
+ </div>
77
+ <div class="legend-title">Relationships</div>
78
+ <div class="filters">
79
+ <label><input type="checkbox" data-edge="DEFINES" checked><span class="swatch" style="background:#73809f"></span>Defines</label>
80
+ <label><input type="checkbox" data-edge="IMPORTS" checked><span class="swatch" style="background:#f6bd60"></span>Imports</label>
81
+ <label><input type="checkbox" data-edge="CALLS" checked><span class="swatch" style="background:#f28482"></span>Calls</label>
82
+ </div>
83
+ <button id="fit">Fit graph</button> <button id="reset">Show all</button>
84
+ <div class="hint" style="margin-top:10px">Hover a node to reveal its label · scroll to zoom · drag to pan · click to focus</div>
85
+ </section>
86
+ <aside id="details" class="panel"><button id="close" aria-label="Close">×</button><div id="detail-content"></div></aside>
87
+ <div id="status"></div>
88
+ <script id="codegraph-data" type="application/json">${payload}</script>
89
+ <script>
90
+ (() => {
91
+ const data = JSON.parse(document.getElementById('codegraph-data').textContent);
92
+ const canvas = document.getElementById('graph');
93
+ const context = canvas.getContext('2d');
94
+ const nodes = data.nodes.map((entry, index) => ({
95
+ ...entry, index, x: Math.cos(index * 2.39996) * Math.sqrt(index + 1) * 12,
96
+ y: Math.sin(index * 2.39996) * Math.sqrt(index + 1) * 12, vx: 0, vy: 0, degree: 0
97
+ }));
98
+ const byId = new Map(nodes.map(node => [node.id, node]));
99
+ const edges = data.edges.map(edge => ({ ...edge, source: byId.get(edge.from), target: byId.get(edge.to) })).filter(edge => edge.source && edge.target);
100
+ edges.forEach(edge => { edge.source.degree++; edge.target.degree++; });
101
+ const edgeColors = { DEFINES: '#73809f', IMPORTS: '#f6bd60', CALLS: '#f28482' };
102
+ const kindColors = { file: '#4cc9f0', function: '#ff6b6b', class: '#ffd166', interface: '#70e000', method: '#c77dff' };
103
+ const enabledEdges = new Set(Object.keys(edgeColors));
104
+ let width = 0, height = 0, scale = 1, offsetX = 0, offsetY = 0;
105
+ let dragging = false, moved = false, lastX = 0, lastY = 0, selected = null, hovered = null;
106
+ let visibleNodes = new Set(nodes), simulationTicks = 0;
107
+
108
+ const label = node => node.data.nodeType === 'file' ? node.data.filePath : node.data.qualifiedName;
109
+ const file = node => node.data.nodeType === 'file' ? node.data.filePath : node.data.location.file;
110
+ const color = node => kindColors[node.data.nodeType === 'file' ? 'file' : node.data.declarationKind] || '#8ecae6';
111
+ const radius = node => Math.min(11, 3.3 + Math.sqrt(node.degree) * 1.15);
112
+ const activeEdges = () => edges.filter(edge => enabledEdges.has(edge.type) && visibleNodes.has(edge.source) && visibleNodes.has(edge.target));
113
+ const displayLabel = node => node.data.nodeType === 'file' ? node.data.filePath.split('/').pop() : node.data.name;
114
+ const showLabel = node => node === hovered;
115
+
116
+ function resize() {
117
+ const ratio = window.devicePixelRatio || 1;
118
+ width = window.innerWidth; height = window.innerHeight;
119
+ canvas.width = Math.round(width * ratio); canvas.height = Math.round(height * ratio);
120
+ context.setTransform(ratio, 0, 0, ratio, 0, 0);
121
+ }
122
+
123
+ function simulate() {
124
+ if (simulationTicks++ > 500) return;
125
+ const active = [...visibleNodes];
126
+ const cellSize = 80; const cells = new Map();
127
+ active.forEach(node => { const key=Math.floor(node.x/cellSize)+','+Math.floor(node.y/cellSize); const bucket=cells.get(key)||[]; bucket.push(node); cells.set(key,bucket); });
128
+ active.forEach(a => {
129
+ const cellX=Math.floor(a.x/cellSize), cellY=Math.floor(a.y/cellSize);
130
+ for (let x=cellX-1; x<=cellX+1; x++) for (let y=cellY-1; y<=cellY+1; y++) for (const b of cells.get(x+','+y)||[]) {
131
+ if (b.index <= a.index) continue;
132
+ const dx = b.x - a.x; const dy = b.y - a.y;
133
+ const distance2 = Math.max(100, dx * dx + dy * dy); const force = Math.min(1.2, 900 / distance2);
134
+ a.vx -= dx * force / Math.sqrt(distance2); a.vy -= dy * force / Math.sqrt(distance2);
135
+ b.vx += dx * force / Math.sqrt(distance2); b.vy += dy * force / Math.sqrt(distance2);
136
+ }
137
+ });
138
+ activeEdges().forEach(edge => {
139
+ const dx = edge.target.x - edge.source.x; const dy = edge.target.y - edge.source.y;
140
+ const distance = Math.max(1, Math.hypot(dx, dy)); const target = edge.type === 'DEFINES' ? 45 : 90;
141
+ const force = Math.max(-2, Math.min(2, (distance - target) * .008));
142
+ const forceX = dx / distance * force, forceY = dy / distance * force;
143
+ edge.source.vx += forceX; edge.source.vy += forceY;
144
+ edge.target.vx -= forceX; edge.target.vy -= forceY;
145
+ });
146
+ active.forEach(node => { node.vx -= node.x * .00035; node.vy -= node.y * .00035; node.vx *= .84; node.vy *= .84; node.x += node.vx; node.y += node.vy; });
147
+ }
148
+
149
+ function draw() {
150
+ simulate(); context.clearRect(0, 0, width, height); context.save();
151
+ context.translate(width / 2 + offsetX, height / 2 + offsetY); context.scale(scale, scale);
152
+ activeEdges().forEach(edge => {
153
+ context.beginPath(); context.moveTo(edge.source.x, edge.source.y); context.lineTo(edge.target.x, edge.target.y);
154
+ const edgeColor=edgeColors[edge.type] + (selected && edge.source !== selected && edge.target !== selected ? '35' : '78');
155
+ context.strokeStyle = edgeColor; context.lineWidth = 1 / Math.sqrt(scale); context.stroke();
156
+ const angle=Math.atan2(edge.target.y-edge.source.y,edge.target.x-edge.source.x); const tip=radius(edge.target)+2/scale;
157
+ const tx=edge.target.x-Math.cos(angle)*tip, ty=edge.target.y-Math.sin(angle)*tip, arrow=3.5/Math.sqrt(scale);
158
+ context.beginPath(); context.moveTo(tx,ty); context.lineTo(tx-Math.cos(angle-.55)*arrow,ty-Math.sin(angle-.55)*arrow); context.lineTo(tx-Math.cos(angle+.55)*arrow,ty-Math.sin(angle+.55)*arrow); context.closePath(); context.fillStyle=edgeColor; context.fill();
159
+ });
160
+ visibleNodes.forEach(node => {
161
+ const r = radius(node); context.beginPath(); context.arc(node.x, node.y, r, 0, Math.PI * 2);
162
+ context.fillStyle = color(node); context.globalAlpha = selected && node !== selected && !isNeighbor(selected, node) ? .3 : 1; context.fill();
163
+ if (node === selected || node === hovered) { context.strokeStyle = '#fff'; context.lineWidth = 2 / scale; context.stroke(); }
164
+ });
165
+ visibleNodes.forEach(node => {
166
+ if(!showLabel(node))return;
167
+ const text=displayLabel(node), fontSize=12/scale, padding=3/scale, x=node.x+radius(node)+5/scale, y=node.y;
168
+ context.font='600 '+fontSize+'px ui-sans-serif, system-ui, sans-serif'; context.textBaseline='middle';
169
+ const textWidth=context.measureText(text).width;
170
+ context.globalAlpha=1; context.fillStyle='#10121ddd';
171
+ context.fillRect(x-padding,y-fontSize*.72,textWidth+padding*2,fontSize*1.44);
172
+ context.fillStyle='#f5f6fb'; context.fillText(text,x,y);
173
+ });
174
+ context.globalAlpha = 1; context.restore();
175
+ requestAnimationFrame(draw);
176
+ }
177
+
178
+ function isNeighbor(a, b) { return edges.some(edge => enabledEdges.has(edge.type) && ((edge.source === a && edge.target === b) || (edge.source === b && edge.target === a))); }
179
+ function screenNode(event) {
180
+ const x = (event.clientX - width / 2 - offsetX) / scale; const y = (event.clientY - height / 2 - offsetY) / scale;
181
+ let result = null, best = 16 / scale;
182
+ visibleNodes.forEach(node => { const distance = Math.hypot(node.x - x, node.y - y); if (distance < Math.max(radius(node), best)) { result = node; best = distance; } });
183
+ return result;
184
+ }
185
+ function showDetails(node) {
186
+ const incident = edges.filter(edge => edge.source === node || edge.target === node);
187
+ const counts = incident.reduce((result, edge) => ({ ...result, [edge.type]: (result[edge.type] || 0) + 1 }), {});
188
+ const location = node.data.nodeType === 'symbol' ? node.data.location.file + ':' + node.data.location.line + ':' + node.data.location.column : node.data.filePath;
189
+ document.getElementById('detail-content').innerHTML = '<h2>' + escapeHtml(label(node)) + '</h2><dl>' +
190
+ '<dt>Type</dt><dd>' + escapeHtml(node.data.nodeType === 'file' ? 'file' : node.data.declarationKind) + '</dd>' +
191
+ '<dt>Location</dt><dd><a class="source-link" href="' + editorHref(node) + '">' + escapeHtml(location) + '</a></dd><dt>Connections</dt><dd>' + incident.length + '</dd>' +
192
+ '<dt>Relationships</dt><dd>' + escapeHtml(Object.entries(counts).map(([key,value]) => key + ': ' + value).join(' · ') || 'None') + '</dd></dl>';
193
+ document.getElementById('details').classList.add('visible');
194
+ }
195
+ function editorHref(node) {
196
+ if(data.openMode==='server')return '/open?node='+encodeURIComponent(node.id);
197
+ const relativePath=file(node), suffix=node.data.nodeType==='symbol'?':'+node.data.location.line+':'+node.data.location.column:'';
198
+ const absolutePath=(data.rootDirectory.replace(/\\\/$/,'')+'/'+relativePath).split('/').map((part,index)=>index===0?'':encodeURIComponent(part)).join('/');
199
+ return 'vscode://file'+absolutePath+suffix;
200
+ }
201
+ function focusNode(node, related) {
202
+ selected=node; visibleNodes=new Set([node,...related]); showDetails(node); fit(); restart();
203
+ }
204
+ function normalizedWords(value) { return value.toLowerCase().replace(/[^a-z0-9_.$/\\-]+/g,' ').trim().split(/\\s+/).filter(word => word.length > 1); }
205
+ function bestQuestionNode(question) {
206
+ const ignored=new Set(['what','which','where','who','does','call','calls','called','uses','use','used','imports','import','imported','defines','define','defined','depends','dependents','dependencies','on','by','is','are','the','a','an','of','for','from','show','find','me']);
207
+ const terms=normalizedWords(question).filter(word => !ignored.has(word));
208
+ let best=null, bestScore=0;
209
+ nodes.forEach(node => { const name=label(node).toLowerCase(), location=file(node).toLowerCase(); let score=0; terms.forEach(term => { if(name===term)score+=12; else if(name.endsWith('.'+term)||name.endsWith('/'+term))score+=8; else if(name.includes(term))score+=4; if(location.includes(term))score+=2; }); if(score>bestScore){best=node;bestScore=score;} });
210
+ return best;
211
+ }
212
+ function askQuestion() {
213
+ const question=document.getElementById('question').value.trim(), answer=document.getElementById('answer');
214
+ if(!question){answer.textContent='Ask about a file or symbol, for example: “What calls analyzeProject?”';answer.classList.add('visible');return;}
215
+ const node=bestQuestionNode(question);
216
+ if(!node){answer.textContent='I could not match that question to an indexed file or symbol.';answer.classList.add('visible');return;}
217
+ const lower=question.toLowerCase(); let direction='both', relationship=null;
218
+ const impactIntent=/affected|impact|break|if i change|when i change|changing/.test(lower);
219
+ if(/what (does|do)|dependencies|depends on|calls from/.test(lower))direction='outgoing';
220
+ if(/what calls|who calls|called by|dependents|what imports|imported by|where .*defined/.test(lower))direction='incoming';
221
+ if(/import/.test(lower))relationship='IMPORTS'; else if(/call|use|depend/.test(lower))relationship='CALLS'; else if(/defin|contain/.test(lower))relationship='DEFINES';
222
+ if(impactIntent){direction='incoming';relationship='CALLS';}
223
+ if(relationship) { enabledEdges.clear(); enabledEdges.add(relationship); document.querySelectorAll('[data-edge]').forEach(input => { input.checked=input.dataset.edge===relationship; }); }
224
+ const matches=edges.filter(edge => (!relationship||edge.type===relationship) && ((direction!=='incoming'&&edge.source===node)||(direction!=='outgoing'&&edge.target===node)));
225
+ const related=matches.map(edge => edge.source===node?edge.target:edge.source).filter((entry,index,array)=>array.indexOf(entry)===index);
226
+ focusNode(node,related);
227
+ const heading=impactIntent?'Potentially affected callers':(direction==='incoming'?'Incoming':direction==='outgoing'?'Outgoing':'Connected')+(relationship?' '+relationship.toLowerCase():' relationships');
228
+ answer.innerHTML='<strong>'+escapeHtml(label(node))+'</strong><br>'+escapeHtml(heading)+': '+related.length+(related.length?related.slice(0,8).map(item=>'<div class="result"><button data-node="'+item.index+'">'+escapeHtml(label(item))+'</button><a class="source-link" href="'+editorHref(item)+'">Open file ↗</a></div>').join(''):'')+'<div class="result"><a class="source-link" href="'+editorHref(node)+'">Open matched source ↗</a></div>';
229
+ answer.querySelectorAll('[data-node]').forEach(button=>button.addEventListener('click',()=>{const target=nodes[Number(button.dataset.node)];if(target)focusNode(target,nodes.filter(item=>isNeighbor(target,item)));}));
230
+ answer.classList.add('visible');
231
+ }
232
+ function escapeHtml(value) { const element = document.createElement('span'); element.textContent = String(value); return element.innerHTML; }
233
+ function fit() {
234
+ const active = [...visibleNodes]; if (!active.length) return;
235
+ const xs = active.map(node => node.x), ys = active.map(node => node.y);
236
+ const graphWidth = Math.max(100, Math.max(...xs) - Math.min(...xs)); const graphHeight = Math.max(100, Math.max(...ys) - Math.min(...ys));
237
+ scale = Math.max(.08, Math.min(2.5, Math.min((width - 80) / graphWidth, (height - 80) / graphHeight) * .82));
238
+ offsetX = -((Math.min(...xs) + Math.max(...xs)) / 2) * scale; offsetY = -((Math.min(...ys) + Math.max(...ys)) / 2) * scale;
239
+ }
240
+ function updateStatus() { document.getElementById('status').textContent = visibleNodes.size + ' nodes · ' + activeEdges().length + ' relationships · local visualization'; }
241
+ function restart() { simulationTicks = 0; updateStatus(); }
242
+
243
+ canvas.addEventListener('mousedown', event => { dragging = true; moved = false; lastX = event.clientX; lastY = event.clientY; canvas.classList.add('dragging'); });
244
+ window.addEventListener('mousemove', event => { hovered = screenNode(event); if (!dragging) return; const dx = event.clientX-lastX, dy=event.clientY-lastY; if (Math.abs(dx)+Math.abs(dy)>2) moved=true; offsetX+=dx; offsetY+=dy; lastX=event.clientX; lastY=event.clientY; });
245
+ window.addEventListener('mouseup', event => { if (dragging && !moved) { selected = screenNode(event); if (selected) focusNode(selected,nodes.filter(node => isNeighbor(selected,node))); } dragging=false; canvas.classList.remove('dragging'); });
246
+ canvas.addEventListener('wheel', event => { event.preventDefault(); const previous=scale; scale=Math.max(.05,Math.min(8,scale*Math.exp(-event.deltaY*.001))); offsetX=event.clientX-width/2-(event.clientX-width/2-offsetX)*scale/previous; offsetY=event.clientY-height/2-(event.clientY-height/2-offsetY)*scale/previous; }, {passive:false});
247
+ document.querySelectorAll('[data-edge]').forEach(input => input.addEventListener('change', () => { input.checked ? enabledEdges.add(input.dataset.edge) : enabledEdges.delete(input.dataset.edge); restart(); }));
248
+ document.getElementById('search').addEventListener('input', event => { const query=event.target.value.trim().toLowerCase(); if (!query) { visibleNodes=new Set(nodes); selected=null; } else { const matches=nodes.filter(node => label(node).toLowerCase().includes(query) || file(node).toLowerCase().includes(query)); visibleNodes=new Set(matches); selected=matches.length===1?matches[0]:null; } fit(); restart(); });
249
+ document.getElementById('fit').addEventListener('click', fit);
250
+ document.getElementById('ask').addEventListener('click', askQuestion);
251
+ document.getElementById('question').addEventListener('keydown', event => { if(event.key==='Enter')askQuestion(); });
252
+ document.addEventListener('click', async event => { const link=event.target.closest('a.source-link'); if(!link||!link.getAttribute('href').startsWith('/open?'))return; event.preventDefault(); const response=await fetch(link.href); document.getElementById('status').textContent=response.ok?'Opened source in IDE':'Could not open source: '+await response.text(); });
253
+ document.getElementById('reset').addEventListener('click', () => { visibleNodes=new Set(nodes); selected=null; document.getElementById('search').value=''; document.getElementById('details').classList.remove('visible'); fit(); restart(); });
254
+ document.getElementById('close').addEventListener('click', () => document.getElementById('details').classList.remove('visible'));
255
+ document.getElementById('summary').textContent = data.rootDirectory + ' · ' + nodes.length + ' nodes · ' + edges.length + ' relationships';
256
+ window.addEventListener('resize', () => { resize(); fit(); }); resize(); setTimeout(fit, 500); updateStatus(); draw();
257
+ })();
258
+ </script>
259
+ </body>
260
+ </html>`;
261
+ }
262
+ export async function writeVisualizationDocument(analysis, outputPath) {
263
+ const absoluteOutputPath = path.resolve(outputPath);
264
+ await mkdir(path.dirname(absoluteOutputPath), { recursive: true });
265
+ await writeFile(absoluteOutputPath, createVisualizationDocument(analysis), "utf8");
266
+ return absoluteOutputPath;
267
+ }
268
+ export async function startVisualizationServer(analysis, options = {}) {
269
+ const host = options.host ?? "127.0.0.1";
270
+ const document = createVisualizationDocument(analysis, "server");
271
+ const server = createServer(async (request, response) => {
272
+ const requestUrl = new URL(request.url ?? "/", `http://${host}`);
273
+ if (requestUrl.pathname === "/open") {
274
+ const nodeId = requestUrl.searchParams.get("node");
275
+ const node = nodeId === null ? undefined : analysis.graph.getNode(nodeId);
276
+ if (node === undefined) {
277
+ response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
278
+ response.end("Indexed node not found\n");
279
+ return;
280
+ }
281
+ const relativePath = node.data.nodeType === "file"
282
+ ? node.data.filePath
283
+ : node.data.location.file;
284
+ const absolutePath = path.resolve(analysis.rootDirectory, relativePath);
285
+ const relativeToRoot = path.relative(analysis.rootDirectory, absolutePath);
286
+ if (relativeToRoot.startsWith("..") || path.isAbsolute(relativeToRoot)) {
287
+ response.writeHead(400, { "Content-Type": "text/plain; charset=utf-8" });
288
+ response.end("Indexed path is outside the project root\n");
289
+ return;
290
+ }
291
+ const suffix = node.data.nodeType === "symbol"
292
+ ? `:${node.data.location.line}:${node.data.location.column}`
293
+ : "";
294
+ const uri = `vscode://file${encodeEditorPath(absolutePath)}${suffix}`;
295
+ try {
296
+ await (options.openSource ?? openSourceUri)(uri);
297
+ response.writeHead(204, { "Cache-Control": "no-store" });
298
+ response.end();
299
+ }
300
+ catch (error) {
301
+ response.writeHead(500, { "Content-Type": "text/plain; charset=utf-8" });
302
+ response.end(`Unable to open IDE: ${error instanceof Error ? error.message : String(error)}\n`);
303
+ }
304
+ return;
305
+ }
306
+ if (requestUrl.pathname !== "/" && requestUrl.pathname !== "/index.html") {
307
+ response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
308
+ response.end("Not found\n");
309
+ return;
310
+ }
311
+ response.writeHead(200, {
312
+ "Content-Type": "text/html; charset=utf-8",
313
+ "Cache-Control": "no-store",
314
+ "Content-Security-Policy": "default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data:; connect-src 'self'",
315
+ });
316
+ response.end(document);
317
+ });
318
+ await new Promise((resolve, reject) => {
319
+ server.once("error", reject);
320
+ server.listen(options.port ?? 0, host, () => { server.off("error", reject); resolve(); });
321
+ });
322
+ const address = server.address();
323
+ if (address === null || typeof address === "string")
324
+ throw new Error("Unable to determine visualization server address");
325
+ const url = `http://${host}:${address.port}/`;
326
+ if (options.openBrowser !== false)
327
+ openBrowser(url);
328
+ return { server, url, close: () => new Promise((resolve, reject) => server.close(error => error ? reject(error) : resolve())) };
329
+ }
330
+ function encodeEditorPath(filePath) {
331
+ return filePath.split(path.sep).map((part, index) => index === 0 ? "" : encodeURIComponent(part)).join("/");
332
+ }
333
+ async function openSourceUri(uri) {
334
+ const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
335
+ const args = process.platform === "win32" ? ["/c", "start", "", uri] : [uri];
336
+ await new Promise((resolve, reject) => {
337
+ const child = spawn(command, args, { stdio: "ignore" });
338
+ child.once("error", reject);
339
+ child.once("exit", (code) => code === 0 ? resolve() : reject(new Error(`${command} exited with code ${String(code)}`)));
340
+ });
341
+ }
342
+ function openBrowser(url) {
343
+ const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
344
+ const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
345
+ const child = spawn(command, args, { detached: true, stdio: "ignore" });
346
+ child.on("error", () => { });
347
+ child.unref();
348
+ }
349
+ //# sourceMappingURL=viewer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewer.js","sourceRoot":"","sources":["../../src/visualization/viewer.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,58 @@
1
1
  {
2
2
  "name": "repo-codegraph",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.2.1",
4
+ "description": "A TypeScript code-intelligence graph for imports, calls, queries, and impact analysis",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/RuchiPurohit/codegraph.git"
8
+ },
9
+ "homepage": "https://github.com/RuchiPurohit/codegraph#readme",
10
+ "bugs": {
11
+ "url": "https://github.com/RuchiPurohit/codegraph/issues"
12
+ },
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "type": "module",
17
+ "main": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
19
+ "files": [
20
+ "dist",
21
+ "README.md"
22
+ ],
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "import": "./dist/index.js"
27
+ },
28
+ "./mcp": {
29
+ "types": "./dist/mcp/server.d.ts",
30
+ "import": "./dist/mcp/server.js"
31
+ }
32
+ },
33
+ "bin": {
34
+ "codegraph": "./dist/cli.js",
35
+ "codegraph-mcp": "./dist/mcp/stdio.js"
36
+ },
37
+ "scripts": {
38
+ "build": "tsc -p tsconfig.build.json",
39
+ "check": "tsc --noEmit",
40
+ "codegraph": "node --import tsx src/cli.ts",
41
+ "codegraph-mcp": "node --import tsx src/mcp/stdio.ts",
42
+ "example": "node --import tsx examples/basic.ts",
43
+ "inspect-ast": "node --import tsx examples/inspectAst.ts",
44
+ "test": "node --import tsx --test test/*.test.ts"
45
+ },
46
+ "engines": {
47
+ "node": ">=20"
48
+ },
49
+ "devDependencies": {
50
+ "@types/node": "^20.19.0",
51
+ "tsx": "^4.20.0",
52
+ "typescript": "^5.9.0"
53
+ },
54
+ "dependencies": {
55
+ "@modelcontextprotocol/sdk": "^1.32.1",
56
+ "zod": "^4.6.7"
57
+ }
6
58
  }