rho-graph 0.1.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.
Files changed (118) hide show
  1. package/README.md +277 -0
  2. package/bin/rho-graph.js +2 -0
  3. package/dist/cli/commands/index-cmd.d.ts +2 -0
  4. package/dist/cli/commands/index-cmd.js +45 -0
  5. package/dist/cli/commands/index-cmd.js.map +1 -0
  6. package/dist/cli/commands/init.d.ts +3 -0
  7. package/dist/cli/commands/init.js +55 -0
  8. package/dist/cli/commands/init.js.map +1 -0
  9. package/dist/cli/commands/install-hook.d.ts +3 -0
  10. package/dist/cli/commands/install-hook.js +37 -0
  11. package/dist/cli/commands/install-hook.js.map +1 -0
  12. package/dist/cli/commands/install-mcp.d.ts +3 -0
  13. package/dist/cli/commands/install-mcp.js +32 -0
  14. package/dist/cli/commands/install-mcp.js.map +1 -0
  15. package/dist/cli/commands/query.d.ts +2 -0
  16. package/dist/cli/commands/query.js +92 -0
  17. package/dist/cli/commands/query.js.map +1 -0
  18. package/dist/cli/commands/setup.d.ts +2 -0
  19. package/dist/cli/commands/setup.js +15 -0
  20. package/dist/cli/commands/setup.js.map +1 -0
  21. package/dist/cli/commands/status.d.ts +2 -0
  22. package/dist/cli/commands/status.js +40 -0
  23. package/dist/cli/commands/status.js.map +1 -0
  24. package/dist/cli/commands/visualize.d.ts +2 -0
  25. package/dist/cli/commands/visualize.js +45 -0
  26. package/dist/cli/commands/visualize.js.map +1 -0
  27. package/dist/cli/index.d.ts +1 -0
  28. package/dist/cli/index.js +32 -0
  29. package/dist/cli/index.js.map +1 -0
  30. package/dist/config.d.ts +22 -0
  31. package/dist/config.js +95 -0
  32. package/dist/config.js.map +1 -0
  33. package/dist/db/connection.d.ts +13 -0
  34. package/dist/db/connection.js +25 -0
  35. package/dist/db/connection.js.map +1 -0
  36. package/dist/db/queries.d.ts +106 -0
  37. package/dist/db/queries.js +247 -0
  38. package/dist/db/queries.js.map +1 -0
  39. package/dist/db/schema.d.ts +2 -0
  40. package/dist/db/schema.js +22 -0
  41. package/dist/db/schema.js.map +1 -0
  42. package/dist/docker/neo4j.d.ts +5 -0
  43. package/dist/docker/neo4j.js +85 -0
  44. package/dist/docker/neo4j.js.map +1 -0
  45. package/dist/indexer/batch-writer.d.ts +35 -0
  46. package/dist/indexer/batch-writer.js +202 -0
  47. package/dist/indexer/batch-writer.js.map +1 -0
  48. package/dist/indexer/extractor.d.ts +35 -0
  49. package/dist/indexer/extractor.js +141 -0
  50. package/dist/indexer/extractor.js.map +1 -0
  51. package/dist/indexer/graph-writer.d.ts +12 -0
  52. package/dist/indexer/graph-writer.js +75 -0
  53. package/dist/indexer/graph-writer.js.map +1 -0
  54. package/dist/indexer/import-resolver.d.ts +8 -0
  55. package/dist/indexer/import-resolver.js +80 -0
  56. package/dist/indexer/import-resolver.js.map +1 -0
  57. package/dist/indexer/index.d.ts +21 -0
  58. package/dist/indexer/index.js +262 -0
  59. package/dist/indexer/index.js.map +1 -0
  60. package/dist/indexer/language-map.json +101 -0
  61. package/dist/indexer/parallel-pipeline.d.ts +41 -0
  62. package/dist/indexer/parallel-pipeline.js +82 -0
  63. package/dist/indexer/parallel-pipeline.js.map +1 -0
  64. package/dist/indexer/parser.d.ts +9 -0
  65. package/dist/indexer/parser.js +85 -0
  66. package/dist/indexer/parser.js.map +1 -0
  67. package/dist/indexer/staleness.d.ts +12 -0
  68. package/dist/indexer/staleness.js +60 -0
  69. package/dist/indexer/staleness.js.map +1 -0
  70. package/dist/mcp/index.d.ts +1 -0
  71. package/dist/mcp/index.js +38 -0
  72. package/dist/mcp/index.js.map +1 -0
  73. package/dist/mcp/staleness-check.d.ts +7 -0
  74. package/dist/mcp/staleness-check.js +31 -0
  75. package/dist/mcp/staleness-check.js.map +1 -0
  76. package/dist/mcp/tools/get-callees.d.ts +3 -0
  77. package/dist/mcp/tools/get-callees.js +34 -0
  78. package/dist/mcp/tools/get-callees.js.map +1 -0
  79. package/dist/mcp/tools/get-callers.d.ts +3 -0
  80. package/dist/mcp/tools/get-callers.js +34 -0
  81. package/dist/mcp/tools/get-callers.js.map +1 -0
  82. package/dist/mcp/tools/get-class.d.ts +3 -0
  83. package/dist/mcp/tools/get-class.js +42 -0
  84. package/dist/mcp/tools/get-class.js.map +1 -0
  85. package/dist/mcp/tools/get-dependencies.d.ts +3 -0
  86. package/dist/mcp/tools/get-dependencies.js +26 -0
  87. package/dist/mcp/tools/get-dependencies.js.map +1 -0
  88. package/dist/mcp/tools/get-dependents.d.ts +3 -0
  89. package/dist/mcp/tools/get-dependents.js +26 -0
  90. package/dist/mcp/tools/get-dependents.js.map +1 -0
  91. package/dist/mcp/tools/get-file-structure.d.ts +3 -0
  92. package/dist/mcp/tools/get-file-structure.js +33 -0
  93. package/dist/mcp/tools/get-file-structure.js.map +1 -0
  94. package/dist/mcp/tools/get-function.d.ts +3 -0
  95. package/dist/mcp/tools/get-function.js +34 -0
  96. package/dist/mcp/tools/get-function.js.map +1 -0
  97. package/dist/mcp/tools/get-repo-structure.d.ts +3 -0
  98. package/dist/mcp/tools/get-repo-structure.js +39 -0
  99. package/dist/mcp/tools/get-repo-structure.js.map +1 -0
  100. package/dist/mcp/tools/reindex.d.ts +4 -0
  101. package/dist/mcp/tools/reindex.js +27 -0
  102. package/dist/mcp/tools/reindex.js.map +1 -0
  103. package/dist/mcp/tools/search-code.d.ts +3 -0
  104. package/dist/mcp/tools/search-code.js +43 -0
  105. package/dist/mcp/tools/search-code.js.map +1 -0
  106. package/dist/visualize/public/graph.js +445 -0
  107. package/dist/visualize/public/index.html +88 -0
  108. package/dist/visualize/queries.d.ts +14 -0
  109. package/dist/visualize/queries.js +84 -0
  110. package/dist/visualize/queries.js.map +1 -0
  111. package/dist/visualize/server.d.ts +19 -0
  112. package/dist/visualize/server.js +293 -0
  113. package/dist/visualize/server.js.map +1 -0
  114. package/docker-compose.yml +16 -0
  115. package/package.json +69 -0
  116. package/src/indexer/language-map.json +128 -0
  117. package/src/visualize/public/graph.js +445 -0
  118. package/src/visualize/public/index.html +88 -0
@@ -0,0 +1,84 @@
1
+ // src/visualize/queries.ts
2
+ export const INITIAL_LIMIT = 500;
3
+ export const EXPAND_FILE_LIMIT = 100;
4
+ export const EXPAND_FUNCTION_LIMIT = 50;
5
+ export const SEARCH_LIMIT = 25;
6
+ export function repoOverview(repoName) {
7
+ return {
8
+ cypher: `
9
+ MATCH (r:Repository)
10
+ WHERE $repoName IS NULL OR r.name = $repoName
11
+ MATCH (r)-[contains:CONTAINS_FILE]->(f:File)
12
+ OPTIONAL MATCH (f)-[imp:IMPORTS]->(other:File)
13
+ WHERE (r)-[:CONTAINS_FILE]->(other)
14
+ RETURN r, contains, f, imp, other
15
+ LIMIT toInteger($limit)
16
+ `,
17
+ params: { repoName: repoName ?? null, limit: INITIAL_LIMIT },
18
+ };
19
+ }
20
+ export function filterByFile(relativePath) {
21
+ return {
22
+ cypher: `
23
+ MATCH (f:File {relativePath: $relativePath})
24
+ OPTIONAL MATCH (f)-[contains:CONTAINS]->(sym)
25
+ WHERE sym:Function OR sym:Class
26
+ OPTIONAL MATCH (sym)-[hasMethod:HAS_METHOD]->(method:Function)
27
+ RETURN f, contains, sym, hasMethod, method
28
+ LIMIT toInteger($limit)
29
+ `,
30
+ params: { relativePath, limit: INITIAL_LIMIT },
31
+ };
32
+ }
33
+ export function filterByFunction(name) {
34
+ return {
35
+ cypher: `
36
+ MATCH (fn:Function {name: $name})
37
+ OPTIONAL MATCH (file:File)-[contains:CONTAINS]->(fn)
38
+ OPTIONAL MATCH (fn)-[outCall:CALLS]->(callee:Function)
39
+ OPTIONAL MATCH (caller:Function)-[inCall:CALLS]->(fn)
40
+ RETURN fn, file, contains, outCall, callee, inCall, caller
41
+ LIMIT toInteger($limit)
42
+ `,
43
+ params: { name, limit: INITIAL_LIMIT },
44
+ };
45
+ }
46
+ export function expandFile(filePath) {
47
+ return {
48
+ cypher: `
49
+ MATCH (f:File {path: $filePath})
50
+ MATCH (f)-[contains:CONTAINS]->(sym)
51
+ WHERE sym:Function OR sym:Class
52
+ OPTIONAL MATCH (sym)-[hasMethod:HAS_METHOD]->(method:Function)
53
+ RETURN f, contains, sym, hasMethod, method
54
+ LIMIT toInteger($limit)
55
+ `,
56
+ params: { filePath, limit: EXPAND_FILE_LIMIT },
57
+ };
58
+ }
59
+ export function expandFunction(name, filePath) {
60
+ return {
61
+ cypher: `
62
+ MATCH (fn:Function {name: $name, filePath: $filePath})
63
+ OPTIONAL MATCH (fn)-[outCall:CALLS]->(callee:Function)
64
+ OPTIONAL MATCH (caller:Function)-[inCall:CALLS]->(fn)
65
+ RETURN fn, outCall, callee, inCall, caller
66
+ LIMIT toInteger($limit)
67
+ `,
68
+ params: { name, filePath, limit: EXPAND_FUNCTION_LIMIT },
69
+ };
70
+ }
71
+ export function searchByName(prefix) {
72
+ return {
73
+ cypher: `
74
+ CALL db.index.fulltext.queryNodes('code_search', $q) YIELD node, score
75
+ WITH node, score
76
+ WHERE node:Function OR node:Class
77
+ RETURN node
78
+ ORDER BY score DESC
79
+ LIMIT toInteger($limit)
80
+ `,
81
+ params: { q: `${prefix}*`, limit: SEARCH_LIMIT },
82
+ };
83
+ }
84
+ //# sourceMappingURL=queries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queries.js","sourceRoot":"","sources":["../../src/visualize/queries.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAO3B,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC;AACjC,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AACrC,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,CAAC;AACxC,MAAM,CAAC,MAAM,YAAY,GAAG,EAAE,CAAC;AAE/B,MAAM,UAAU,YAAY,CAAC,QAAiB;IAC5C,OAAO;QACL,MAAM,EAAE;;;;;;;;KAQP;QACD,MAAM,EAAE,EAAE,QAAQ,EAAE,QAAQ,IAAI,IAAI,EAAE,KAAK,EAAE,aAAa,EAAE;KAC7D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,YAAoB;IAC/C,OAAO;QACL,MAAM,EAAE;;;;;;;KAOP;QACD,MAAM,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE;KAC/C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO;QACL,MAAM,EAAE;;;;;;;KAOP;QACD,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,aAAa,EAAE;KACvC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,OAAO;QACL,MAAM,EAAE;;;;;;;KAOP;QACD,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,iBAAiB,EAAE;KAC/C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,QAAgB;IAC3D,OAAO;QACL,MAAM,EAAE;;;;;;KAMP;QACD,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,qBAAqB,EAAE;KACzD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,OAAO;QACL,MAAM,EAAE;;;;;;;KAOP;QACD,MAAM,EAAE,EAAE,CAAC,EAAE,GAAG,MAAM,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE;KACjD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { Neo4jConfig } from "../config.js";
2
+ interface VisualizationOptions {
3
+ neo4jConfig: Neo4jConfig;
4
+ port: number;
5
+ filter: {
6
+ repo?: string;
7
+ file?: string;
8
+ function?: string;
9
+ };
10
+ }
11
+ export declare function startVisualizationServer(opts: VisualizationOptions): Promise<void>;
12
+ /**
13
+ * Test-only variant of startVisualizationServer:
14
+ * - returns the Server instance so tests can close it
15
+ * - does not call openBrowser
16
+ * - throws (does not process.exit) on connection failure
17
+ */
18
+ export declare function startVisualizationServerForTest(opts: VisualizationOptions): Promise<import("node:http").Server>;
19
+ export {};
@@ -0,0 +1,293 @@
1
+ // src/visualize/server.ts
2
+ import { createServer } from "node:http";
3
+ import { readFileSync } from "node:fs";
4
+ import { join, dirname, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { spawn } from "node:child_process";
7
+ import neo4j from "neo4j-driver";
8
+ import { repoOverview, filterByFile, filterByFunction, expandFile, expandFunction, searchByName, } from "./queries.js";
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+ async function runCypher(driver, q) {
11
+ const session = driver.session();
12
+ try {
13
+ const result = await session.run(q.cypher, q.params, { timeout: 10000 });
14
+ const nodes = new Map();
15
+ const edges = [];
16
+ for (const record of result.records) {
17
+ for (const key of record.keys) {
18
+ const val = record.get(key);
19
+ if (val && typeof val === "object" && "identity" in val && "labels" in val) {
20
+ const id = val.identity.toString();
21
+ if (!nodes.has(id)) {
22
+ nodes.set(id, {
23
+ id,
24
+ label: val.properties.name || val.properties.relativePath || id,
25
+ group: val.labels?.[0] ?? "Unknown",
26
+ properties: val.properties,
27
+ });
28
+ }
29
+ }
30
+ if (val && typeof val === "object" && "type" in val && "start" in val && "end" in val) {
31
+ edges.push({
32
+ from: val.start.toString(),
33
+ to: val.end.toString(),
34
+ label: val.type,
35
+ });
36
+ }
37
+ }
38
+ }
39
+ return { nodes: Array.from(nodes.values()), edges };
40
+ }
41
+ finally {
42
+ await session.close();
43
+ }
44
+ }
45
+ function pickInitialQuery(opts, urlParams) {
46
+ // URL params take precedence over CLI flags so the browser can re-query without restart
47
+ const file = urlParams.get("file") ?? opts.filter.file;
48
+ const fn = urlParams.get("function") ?? opts.filter.function;
49
+ const repo = urlParams.get("repo") ?? opts.filter.repo;
50
+ if (file)
51
+ return filterByFile(file);
52
+ if (fn)
53
+ return filterByFunction(fn);
54
+ return repoOverview(repo);
55
+ }
56
+ async function handleRequest(res, url, driver, opts) {
57
+ const parsedUrl = new URL(url, "http://localhost");
58
+ const pathname = parsedUrl.pathname;
59
+ const params = parsedUrl.searchParams;
60
+ if (pathname === "/api/graph") {
61
+ try {
62
+ const data = await runCypher(driver, pickInitialQuery(opts, params));
63
+ res.writeHead(200, { "Content-Type": "application/json" });
64
+ res.end(JSON.stringify(data));
65
+ }
66
+ catch (err) {
67
+ console.error("[viz] /api/graph error:", err);
68
+ res.writeHead(500, { "Content-Type": "application/json" });
69
+ res.end(JSON.stringify({ error: String(err) }));
70
+ }
71
+ return;
72
+ }
73
+ if (pathname === "/api/expand") {
74
+ const type = params.get("type");
75
+ try {
76
+ let q;
77
+ if (type === "file") {
78
+ const filePath = params.get("filePath");
79
+ if (!filePath) {
80
+ res.writeHead(400, { "Content-Type": "application/json" });
81
+ res.end(JSON.stringify({ error: "missing filePath" }));
82
+ return;
83
+ }
84
+ q = expandFile(filePath);
85
+ }
86
+ else if (type === "function") {
87
+ const name = params.get("name");
88
+ const filePath = params.get("filePath");
89
+ if (!name || !filePath) {
90
+ res.writeHead(400, { "Content-Type": "application/json" });
91
+ res.end(JSON.stringify({ error: "missing name or filePath" }));
92
+ return;
93
+ }
94
+ q = expandFunction(name, filePath);
95
+ }
96
+ else {
97
+ res.writeHead(400, { "Content-Type": "application/json" });
98
+ res.end(JSON.stringify({ error: `unknown expand type: ${type}` }));
99
+ return;
100
+ }
101
+ const data = await runCypher(driver, q);
102
+ res.writeHead(200, { "Content-Type": "application/json" });
103
+ res.end(JSON.stringify(data));
104
+ }
105
+ catch (err) {
106
+ console.error("[viz] /api/expand error:", err);
107
+ res.writeHead(500, { "Content-Type": "application/json" });
108
+ res.end(JSON.stringify({ error: String(err) }));
109
+ }
110
+ return;
111
+ }
112
+ if (pathname === "/api/search") {
113
+ const q = params.get("q");
114
+ if (!q) {
115
+ res.writeHead(400, { "Content-Type": "application/json" });
116
+ res.end(JSON.stringify({ error: "missing q" }));
117
+ return;
118
+ }
119
+ try {
120
+ const data = await runCypher(driver, searchByName(q));
121
+ res.writeHead(200, { "Content-Type": "application/json" });
122
+ res.end(JSON.stringify(data));
123
+ }
124
+ catch (err) {
125
+ console.error("[viz] /api/search error:", err);
126
+ res.writeHead(500, { "Content-Type": "application/json" });
127
+ res.end(JSON.stringify({ error: String(err) }));
128
+ }
129
+ return;
130
+ }
131
+ // Serve static files
132
+ const publicDir = join(__dirname, "public");
133
+ const filePath = join(publicDir, pathname === "/" ? "index.html" : pathname);
134
+ const resolved = resolve(filePath);
135
+ const resolvedPublicDir = resolve(publicDir);
136
+ if (!resolved.startsWith(resolvedPublicDir + "/")) {
137
+ res.writeHead(403, { "Content-Type": "application/json" });
138
+ res.end(JSON.stringify({ error: "Forbidden" }));
139
+ return;
140
+ }
141
+ console.log(`[viz] serving file: ${filePath}`);
142
+ try {
143
+ const content = readFileSync(filePath);
144
+ const ext = filePath.endsWith(".js") ? "application/javascript" : "text/html";
145
+ console.log(`[viz] -> 200 ${ext} (${content.length} bytes)`);
146
+ res.writeHead(200, { "Content-Type": ext });
147
+ res.end(content);
148
+ }
149
+ catch (err) {
150
+ console.error(`[viz] -> 404: ${filePath}`, err instanceof Error ? err.message : err);
151
+ res.writeHead(404);
152
+ res.end("Not found");
153
+ }
154
+ }
155
+ export async function startVisualizationServer(opts) {
156
+ console.log(`[viz] connecting to Neo4j at ${opts.neo4jConfig.uri}...`);
157
+ const driver = neo4j.driver(opts.neo4jConfig.uri, neo4j.auth.basic(opts.neo4jConfig.username, opts.neo4jConfig.password), { connectionTimeout: 5000 });
158
+ // Verify Neo4j is reachable before starting the HTTP server.
159
+ try {
160
+ const pingSession = driver.session();
161
+ await pingSession.run("RETURN 1", {}, { timeout: 5000 });
162
+ await pingSession.close();
163
+ console.log("[viz] Neo4j connection OK");
164
+ }
165
+ catch (err) {
166
+ console.error("[viz] Neo4j connection FAILED:", err);
167
+ console.error(`[viz] Is Neo4j running at ${opts.neo4jConfig.uri}?`);
168
+ await driver.close();
169
+ process.exit(1);
170
+ }
171
+ const server = createServer((req, res) => {
172
+ const url = req.url ?? "/";
173
+ console.log(`[viz] ${req.method} ${url}`);
174
+ if (url === "/health") {
175
+ res.writeHead(200, { "Content-Type": "text/plain" });
176
+ res.end("ok");
177
+ return;
178
+ }
179
+ handleRequest(res, url, driver, opts).catch((err) => {
180
+ console.error("[viz] unhandled error:", err);
181
+ if (!res.headersSent) {
182
+ res.writeHead(500);
183
+ res.end("Internal server error");
184
+ }
185
+ });
186
+ });
187
+ server.on("connection", (socket) => {
188
+ console.log(`[viz] TCP connection from ${socket.remoteAddress}:${socket.remotePort}`);
189
+ });
190
+ server.on("error", (err) => {
191
+ console.error("[viz] server error:", err);
192
+ });
193
+ server.listen(opts.port, "0.0.0.0", () => {
194
+ const url = `http://localhost:${opts.port}`;
195
+ console.log(`Graph visualization running at ${url}`);
196
+ console.log(`[viz] listening on 0.0.0.0:${opts.port}`);
197
+ console.log(`[viz] serving static files from: ${join(__dirname, "public")}`);
198
+ openBrowser(url);
199
+ });
200
+ }
201
+ /**
202
+ * Test-only variant of startVisualizationServer:
203
+ * - returns the Server instance so tests can close it
204
+ * - does not call openBrowser
205
+ * - throws (does not process.exit) on connection failure
206
+ */
207
+ export async function startVisualizationServerForTest(opts) {
208
+ const driver = neo4j.driver(opts.neo4jConfig.uri, neo4j.auth.basic(opts.neo4jConfig.username, opts.neo4jConfig.password), { connectionTimeout: 5000 });
209
+ const pingSession = driver.session();
210
+ try {
211
+ await pingSession.run("RETURN 1", {}, { timeout: 5000 });
212
+ }
213
+ finally {
214
+ await pingSession.close();
215
+ }
216
+ const server = createServer((req, res) => {
217
+ const url = req.url ?? "/";
218
+ if (url === "/health") {
219
+ res.writeHead(200, { "Content-Type": "text/plain" });
220
+ res.end("ok");
221
+ return;
222
+ }
223
+ handleRequest(res, url, driver, opts).catch((err) => {
224
+ console.error("[viz-test] unhandled error:", err);
225
+ if (!res.headersSent) {
226
+ res.writeHead(500);
227
+ res.end("Internal server error");
228
+ }
229
+ });
230
+ });
231
+ return new Promise((resolve, reject) => {
232
+ server.once("error", reject);
233
+ server.listen(opts.port, "127.0.0.1", () => resolve(server));
234
+ });
235
+ }
236
+ /**
237
+ * Launch the user's browser to `url` without ever blocking the Node event loop.
238
+ *
239
+ * Why this exists: the previous implementation used `execFileSync`, which holds
240
+ * the main thread until the child exits. In WSL2 (and other headless Linux
241
+ * environments) `xdg-open` can hang indefinitely searching for a browser, which
242
+ * pins the event loop and starves the HTTP server of request callbacks even
243
+ * though the TCP listener is bound. We MUST spawn detached + ignore stdio +
244
+ * unref so this function returns immediately regardless of what the child does.
245
+ */
246
+ function openBrowser(url) {
247
+ let command;
248
+ let args;
249
+ if (process.platform === "win32") {
250
+ // The empty "" is a placeholder window title — `start` interprets the
251
+ // first quoted arg as a title, and omitting it breaks URLs with spaces.
252
+ command = "cmd";
253
+ args = ["/c", "start", "", url];
254
+ }
255
+ else if (process.platform === "darwin") {
256
+ command = "open";
257
+ args = [url];
258
+ }
259
+ else if (isWSL()) {
260
+ // WSL2: hand off to the Windows default browser via cmd.exe.
261
+ // The empty "" is `start`'s window-title placeholder (see win32 branch).
262
+ // Falls back to a printed URL via the spawn `error` handler if cmd.exe
263
+ // isn't on PATH (e.g. interop.appendWindowsPath=false in /etc/wsl.conf).
264
+ command = "cmd.exe";
265
+ args = ["/c", "start", "", url];
266
+ }
267
+ else {
268
+ command = "xdg-open";
269
+ args = [url];
270
+ }
271
+ try {
272
+ const child = spawn(command, args, { detached: true, stdio: "ignore" });
273
+ child.on("error", () => {
274
+ console.log(`Open your browser to: ${url}`);
275
+ });
276
+ child.unref();
277
+ }
278
+ catch {
279
+ console.log(`Open your browser to: ${url}`);
280
+ }
281
+ }
282
+ function isWSL() {
283
+ if (process.platform !== "linux")
284
+ return false;
285
+ try {
286
+ const version = readFileSync("/proc/version", "utf8");
287
+ return /microsoft/i.test(version);
288
+ }
289
+ catch {
290
+ return false;
291
+ }
292
+ }
293
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/visualize/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ services:
2
+ neo4j:
3
+ image: neo4j:5-community
4
+ container_name: code-graph-rag-neo4j
5
+ ports:
6
+ - "7474:7474"
7
+ - "7687:7687"
8
+ environment:
9
+ NEO4J_AUTH: neo4j/code-graph-rag
10
+ NEO4J_PLUGINS: '[]'
11
+ volumes:
12
+ - code-graph-rag-data:/data
13
+ restart: unless-stopped
14
+
15
+ volumes:
16
+ code-graph-rag-data:
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "rho-graph",
3
+ "version": "0.1.0",
4
+ "description": "Graph-RAG code indexer for AI agents — token-efficient code search via MCP tools backed by Neo4j",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "rho-graph": "bin/rho-graph.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc && cp src/indexer/language-map.json dist/indexer/language-map.json && cp -r src/visualize/public dist/visualize/",
12
+ "dev": "tsc --watch",
13
+ "test": "vitest run",
14
+ "test:watch": "vitest",
15
+ "lint": "tsc --noEmit"
16
+ },
17
+ "keywords": [
18
+ "mcp",
19
+ "graph-rag",
20
+ "code-search",
21
+ "neo4j",
22
+ "tree-sitter",
23
+ "ai",
24
+ "claude",
25
+ "cursor",
26
+ "llm",
27
+ "code-intelligence"
28
+ ],
29
+ "author": "rhofield",
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/rhofield/rho-graph.git"
34
+ },
35
+ "homepage": "https://github.com/rhofield/rho-graph#readme",
36
+ "bugs": {
37
+ "url": "https://github.com/rhofield/rho-graph/issues"
38
+ },
39
+ "dependencies": {
40
+ "@anthropic-ai/sdk": "^0.39.0",
41
+ "@modelcontextprotocol/sdk": "^1.12.1",
42
+ "commander": "^12.1.0",
43
+ "glob": "^11.0.1",
44
+ "neo4j-driver": "^5.27.0",
45
+ "ora": "^8.1.1",
46
+ "tree-sitter-wasms": "^0.1.13",
47
+ "web-tree-sitter": "^0.24.3",
48
+ "zod": "^3.23.0"
49
+ },
50
+ "devDependencies": {
51
+ "@types/node": "^22.10.0",
52
+ "typescript": "^5.7.0",
53
+ "vitest": "^2.1.0"
54
+ },
55
+ "engines": {
56
+ "node": ">=18"
57
+ },
58
+ "files": [
59
+ "dist",
60
+ "bin",
61
+ "src/indexer/language-map.json",
62
+ "src/visualize/public",
63
+ "docker-compose.yml"
64
+ ],
65
+ "directories": {
66
+ "doc": "docs",
67
+ "test": "tests"
68
+ }
69
+ }
@@ -0,0 +1,128 @@
1
+ {
2
+ "typescript": {
3
+ "function": ["function_declaration", "arrow_function", "method_definition"],
4
+ "class": ["class_declaration"],
5
+ "import": ["import_statement"],
6
+ "call": ["call_expression"],
7
+ "name_field": "name",
8
+ "body_field": "body",
9
+ "parameters_field": "parameters"
10
+ },
11
+ "tsx": {
12
+ "function": ["function_declaration", "arrow_function", "method_definition"],
13
+ "class": ["class_declaration"],
14
+ "import": ["import_statement"],
15
+ "call": ["call_expression"],
16
+ "name_field": "name",
17
+ "body_field": "body",
18
+ "parameters_field": "parameters"
19
+ },
20
+ "javascript": {
21
+ "function": ["function_declaration", "arrow_function", "method_definition"],
22
+ "class": ["class_declaration"],
23
+ "import": ["import_statement"],
24
+ "call": ["call_expression"],
25
+ "name_field": "name",
26
+ "body_field": "body",
27
+ "parameters_field": "parameters"
28
+ },
29
+ "python": {
30
+ "function": ["function_definition"],
31
+ "class": ["class_definition"],
32
+ "import": ["import_from_statement", "import_statement"],
33
+ "call": ["call"],
34
+ "name_field": "name",
35
+ "body_field": "body",
36
+ "parameters_field": "parameters"
37
+ },
38
+ "go": {
39
+ "function": ["function_declaration", "method_declaration"],
40
+ "class": ["type_declaration"],
41
+ "import": ["import_declaration", "import_spec"],
42
+ "call": ["call_expression"],
43
+ "name_field": "name",
44
+ "body_field": "body",
45
+ "parameters_field": "parameters"
46
+ },
47
+ "java": {
48
+ "function": ["method_declaration", "constructor_declaration"],
49
+ "class": ["class_declaration", "interface_declaration", "enum_declaration"],
50
+ "import": ["import_declaration"],
51
+ "call": ["method_invocation"],
52
+ "name_field": "name",
53
+ "body_field": "body",
54
+ "parameters_field": "parameters"
55
+ },
56
+ "rust": {
57
+ "function": ["function_item"],
58
+ "class": ["struct_item", "enum_item", "trait_item", "impl_item"],
59
+ "import": ["use_declaration"],
60
+ "call": ["call_expression"],
61
+ "name_field": "name",
62
+ "body_field": "body",
63
+ "parameters_field": "parameters"
64
+ },
65
+ "c": {
66
+ "function": ["function_definition"],
67
+ "class": ["struct_specifier"],
68
+ "import": ["preproc_include"],
69
+ "call": ["call_expression"],
70
+ "name_field": "declarator",
71
+ "body_field": "body",
72
+ "parameters_field": "parameters"
73
+ },
74
+ "cpp": {
75
+ "function": ["function_definition"],
76
+ "class": ["class_specifier", "struct_specifier"],
77
+ "import": ["preproc_include"],
78
+ "call": ["call_expression"],
79
+ "name_field": "declarator",
80
+ "body_field": "body",
81
+ "parameters_field": "parameters"
82
+ },
83
+ "ruby": {
84
+ "function": ["method", "singleton_method"],
85
+ "class": ["class", "module"],
86
+ "import": ["call"],
87
+ "call": ["call", "method_call"],
88
+ "name_field": "name",
89
+ "body_field": "body",
90
+ "parameters_field": "parameters"
91
+ },
92
+ "graphql": {
93
+ "function": ["operation_definition", "fragment_definition"],
94
+ "class": ["object_type_definition", "input_object_type_definition", "interface_type_definition", "enum_type_definition"],
95
+ "import": [],
96
+ "call": [],
97
+ "name_field": "name",
98
+ "body_field": "body",
99
+ "parameters_field": "arguments_definition"
100
+ },
101
+ "yaml": {
102
+ "function": [],
103
+ "class": [],
104
+ "import": [],
105
+ "call": [],
106
+ "name_field": "name",
107
+ "body_field": "body",
108
+ "parameters_field": "parameters"
109
+ },
110
+ "json": {
111
+ "function": [],
112
+ "class": [],
113
+ "import": [],
114
+ "call": [],
115
+ "name_field": "name",
116
+ "body_field": "body",
117
+ "parameters_field": "parameters"
118
+ },
119
+ "markdown": {
120
+ "function": [],
121
+ "class": [],
122
+ "import": [],
123
+ "call": [],
124
+ "name_field": "name",
125
+ "body_field": "body",
126
+ "parameters_field": "parameters"
127
+ }
128
+ }