@phoenixaihub/graphrag 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 (48) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +17 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +12 -0
  3. package/.github/pull_request_template.md +5 -0
  4. package/.github/workflows/ci.yml +22 -0
  5. package/LICENSE +21 -0
  6. package/README.md +103 -0
  7. package/dist/cli.d.ts +3 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.js +47 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/commands/index-repo.d.ts +8 -0
  12. package/dist/commands/index-repo.d.ts.map +1 -0
  13. package/dist/commands/index-repo.js +79 -0
  14. package/dist/commands/index-repo.js.map +1 -0
  15. package/dist/commands/query.d.ts +7 -0
  16. package/dist/commands/query.d.ts.map +1 -0
  17. package/dist/commands/query.js +32 -0
  18. package/dist/commands/query.js.map +1 -0
  19. package/dist/commands/serve.d.ts +7 -0
  20. package/dist/commands/serve.d.ts.map +1 -0
  21. package/dist/commands/serve.js +65 -0
  22. package/dist/commands/serve.js.map +1 -0
  23. package/dist/db.d.ts +46 -0
  24. package/dist/db.d.ts.map +1 -0
  25. package/dist/db.js +117 -0
  26. package/dist/db.js.map +1 -0
  27. package/dist/embeddings.d.ts +4 -0
  28. package/dist/embeddings.d.ts.map +1 -0
  29. package/dist/embeddings.js +35 -0
  30. package/dist/embeddings.js.map +1 -0
  31. package/dist/index.d.ts +6 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +6 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/parser.d.ts +12 -0
  36. package/dist/parser.d.ts.map +1 -0
  37. package/dist/parser.js +152 -0
  38. package/dist/parser.js.map +1 -0
  39. package/package.json +54 -0
  40. package/src/cli.ts +52 -0
  41. package/src/commands/index-repo.ts +96 -0
  42. package/src/commands/query.ts +43 -0
  43. package/src/commands/serve.ts +90 -0
  44. package/src/db.ts +168 -0
  45. package/src/embeddings.ts +39 -0
  46. package/src/index.ts +5 -0
  47. package/src/parser.ts +162 -0
  48. package/tsconfig.json +19 -0
package/dist/db.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare function getEmbedding(text: string): Promise<number[]>;
2
+ export declare function getEmbeddings(texts: string[]): Promise<number[][]>;
3
+ export declare function cosineSimilarity(a: number[], b: number[]): number;
4
+ //# sourceMappingURL=embeddings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embeddings.d.ts","sourceRoot":"","sources":["../src/embeddings.ts"],"names":[],"mappings":"AAWA,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAOlE;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAQxE;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAQjE"}
@@ -0,0 +1,35 @@
1
+ import OpenAI from 'openai';
2
+ let client = null;
3
+ function getClient() {
4
+ if (!client) {
5
+ client = new OpenAI();
6
+ }
7
+ return client;
8
+ }
9
+ export async function getEmbedding(text) {
10
+ const openai = getClient();
11
+ const resp = await openai.embeddings.create({
12
+ model: 'text-embedding-3-small',
13
+ input: text.substring(0, 8000),
14
+ });
15
+ return resp.data[0].embedding;
16
+ }
17
+ export async function getEmbeddings(texts) {
18
+ const openai = getClient();
19
+ const truncated = texts.map(t => t.substring(0, 8000));
20
+ const resp = await openai.embeddings.create({
21
+ model: 'text-embedding-3-small',
22
+ input: truncated,
23
+ });
24
+ return resp.data.map(d => d.embedding);
25
+ }
26
+ export function cosineSimilarity(a, b) {
27
+ let dot = 0, normA = 0, normB = 0;
28
+ for (let i = 0; i < a.length; i++) {
29
+ dot += a[i] * b[i];
30
+ normA += a[i] * a[i];
31
+ normB += b[i] * b[i];
32
+ }
33
+ return dot / (Math.sqrt(normA) * Math.sqrt(normB));
34
+ }
35
+ //# sourceMappingURL=embeddings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embeddings.js","sourceRoot":"","sources":["../src/embeddings.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,IAAI,MAAM,GAAkB,IAAI,CAAC;AAEjC,SAAS,SAAS;IAChB,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;IACxB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAY;IAC7C,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAC1C,KAAK,EAAE,wBAAwB;QAC/B,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC;KAC/B,CAAC,CAAC;IACH,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAChC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAe;IACjD,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;IACvD,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAC1C,KAAK,EAAE,wBAAwB;QAC/B,KAAK,EAAE,SAAS;KACjB,CAAC,CAAC;IACH,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,CAAW,EAAE,CAAW;IACvD,IAAI,GAAG,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC;IAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACnB,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACrB,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACvB,CAAC;IACD,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;AACrD,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { indexRepo } from './commands/index-repo.js';
2
+ export { queryCode } from './commands/query.js';
3
+ export { serve } from './commands/serve.js';
4
+ export { GraphDB } from './db.js';
5
+ export { parseFile } from './parser.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { indexRepo } from './commands/index-repo.js';
2
+ export { queryCode } from './commands/query.js';
3
+ export { serve } from './commands/serve.js';
4
+ export { GraphDB } from './db.js';
5
+ export { parseFile } from './parser.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { Entity, Edge } from './db.js';
2
+ interface ParseResult {
3
+ entities: Omit<Entity, 'id'>[];
4
+ edges: Array<{
5
+ sourceName: string;
6
+ targetName: string;
7
+ relation: Edge['relation'];
8
+ }>;
9
+ }
10
+ export declare function parseFile(filePath: string, source?: string): ParseResult;
11
+ export {};
12
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAI5C,UAAU,WAAW;IACnB,QAAQ,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC;IAC/B,KAAK,EAAE,KAAK,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;KAAE,CAAC,CAAC;CACtF;AAyED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW,CA+ExE"}
package/dist/parser.js ADDED
@@ -0,0 +1,152 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ const FUNCTION_PATTERNS = {
4
+ typescript: [
5
+ /^(?:export\s+)?(?:async\s+)?function\s+(\w+)/gm,
6
+ /^(?:export\s+)?const\s+(\w+)\s*=\s*(?:async\s+)?\(/gm,
7
+ /^(?:export\s+)?const\s+(\w+)\s*=\s*(?:async\s+)?(?:\([^)]*\)|[^=])\s*=>/gm,
8
+ ],
9
+ python: [
10
+ /^def\s+(\w+)\s*\(/gm,
11
+ /^async\s+def\s+(\w+)\s*\(/gm,
12
+ ],
13
+ };
14
+ const CLASS_PATTERNS = {
15
+ typescript: [/^(?:export\s+)?(?:abstract\s+)?class\s+(\w+)/gm],
16
+ python: [/^class\s+(\w+)/gm],
17
+ };
18
+ const IMPORT_PATTERNS = {
19
+ typescript: [
20
+ /import\s+(?:{([^}]+)}|(\w+))\s+from\s+['"]([^'"]+)['"]/gm,
21
+ /import\s+\*\s+as\s+(\w+)\s+from\s+['"]([^'"]+)['"]/gm,
22
+ ],
23
+ python: [
24
+ /^from\s+(\S+)\s+import\s+(.+)$/gm,
25
+ /^import\s+(\S+)/gm,
26
+ ],
27
+ };
28
+ function detectLanguage(filePath) {
29
+ const ext = path.extname(filePath).toLowerCase();
30
+ if (['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'].includes(ext))
31
+ return 'typescript';
32
+ if (ext === '.py')
33
+ return 'python';
34
+ return 'unknown';
35
+ }
36
+ function getLineNumber(source, index) {
37
+ return source.substring(0, index).split('\n').length;
38
+ }
39
+ function extractBlock(lines, startLine, language) {
40
+ // Simple brace/indent block extraction
41
+ const start = startLine - 1;
42
+ if (language === 'python') {
43
+ let end = start + 1;
44
+ const baseIndent = lines[start]?.search(/\S/) ?? 0;
45
+ while (end < lines.length) {
46
+ const line = lines[end];
47
+ if (line.trim() === '') {
48
+ end++;
49
+ continue;
50
+ }
51
+ const indent = line.search(/\S/);
52
+ if (indent <= baseIndent)
53
+ break;
54
+ end++;
55
+ }
56
+ return { endLine: end, code: lines.slice(start, end).join('\n') };
57
+ }
58
+ // Brace-based
59
+ let braces = 0;
60
+ let found = false;
61
+ let end = start;
62
+ for (let i = start; i < lines.length; i++) {
63
+ for (const ch of lines[i]) {
64
+ if (ch === '{') {
65
+ braces++;
66
+ found = true;
67
+ }
68
+ if (ch === '}')
69
+ braces--;
70
+ }
71
+ end = i + 1;
72
+ if (found && braces <= 0)
73
+ break;
74
+ }
75
+ if (!found)
76
+ end = Math.min(start + 1, lines.length);
77
+ return { endLine: end, code: lines.slice(start, end).join('\n') };
78
+ }
79
+ export function parseFile(filePath, source) {
80
+ const content = source ?? fs.readFileSync(filePath, 'utf-8');
81
+ const language = detectLanguage(filePath);
82
+ if (language === 'unknown')
83
+ return { entities: [], edges: [] };
84
+ const lines = content.split('\n');
85
+ const entities = [];
86
+ const edgesList = [];
87
+ // File entity
88
+ entities.push({
89
+ filePath, name: path.basename(filePath), kind: 'file',
90
+ startLine: 1, endLine: lines.length, code: content.substring(0, 500),
91
+ language,
92
+ });
93
+ const langKey = language;
94
+ // Functions
95
+ for (const pattern of (FUNCTION_PATTERNS[langKey] ?? [])) {
96
+ const regex = new RegExp(pattern.source, pattern.flags);
97
+ let match;
98
+ while ((match = regex.exec(content)) !== null) {
99
+ const name = match[1];
100
+ const startLine = getLineNumber(content, match.index);
101
+ const block = extractBlock(lines, startLine, language);
102
+ entities.push({
103
+ filePath, name, kind: 'function',
104
+ startLine, endLine: block.endLine, code: block.code,
105
+ language,
106
+ });
107
+ edgesList.push({ sourceName: path.basename(filePath), targetName: name, relation: 'contains' });
108
+ }
109
+ }
110
+ // Classes
111
+ for (const pattern of (CLASS_PATTERNS[langKey] ?? [])) {
112
+ const regex = new RegExp(pattern.source, pattern.flags);
113
+ let match;
114
+ while ((match = regex.exec(content)) !== null) {
115
+ const name = match[1];
116
+ const startLine = getLineNumber(content, match.index);
117
+ const block = extractBlock(lines, startLine, language);
118
+ entities.push({
119
+ filePath, name, kind: 'class',
120
+ startLine, endLine: block.endLine, code: block.code,
121
+ language,
122
+ });
123
+ edgesList.push({ sourceName: path.basename(filePath), targetName: name, relation: 'contains' });
124
+ }
125
+ }
126
+ // Imports
127
+ for (const pattern of (IMPORT_PATTERNS[langKey] ?? [])) {
128
+ const regex = new RegExp(pattern.source, pattern.flags);
129
+ let match;
130
+ while ((match = regex.exec(content)) !== null) {
131
+ const startLine = getLineNumber(content, match.index);
132
+ const importSource = language === 'typescript' ? (match[3] ?? match[2] ?? match[1]) : match[1];
133
+ const importedNames = language === 'typescript'
134
+ ? (match[1] ?? match[2] ?? '').split(',').map(s => s.trim()).filter(Boolean)
135
+ : (match[2] ?? match[1] ?? '').split(',').map(s => s.trim()).filter(Boolean);
136
+ for (const name of importedNames) {
137
+ entities.push({
138
+ filePath, name: name.replace(/\s+as\s+\w+/, ''), kind: 'import',
139
+ startLine, endLine: startLine, code: match[0],
140
+ language,
141
+ });
142
+ edgesList.push({
143
+ sourceName: path.basename(filePath),
144
+ targetName: name.replace(/\s+as\s+\w+/, ''),
145
+ relation: 'imports',
146
+ });
147
+ }
148
+ }
149
+ }
150
+ return { entities, edges: edgesList };
151
+ }
152
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@phoenixaihub/graphrag",
3
+ "version": "0.1.0",
4
+ "description": "Graph RAG for private codebases — index code into a knowledge graph with semantic search and MCP server. Self-hosted, no code leaves your machine.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "bin": {
9
+ "graphrag": "./dist/cli.js"
10
+ },
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "dev": "tsc --watch",
14
+ "lint": "tsc --noEmit",
15
+ "test": "node --test dist/**/*.test.js",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "keywords": [
22
+ "rag",
23
+ "graph",
24
+ "codebase",
25
+ "knowledge-graph",
26
+ "mcp",
27
+ "tree-sitter",
28
+ "semantic-search",
29
+ "embeddings",
30
+ "code-intelligence"
31
+ ],
32
+ "author": "Phoenix AI Hub",
33
+ "license": "MIT",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "https://github.com/phoenix-assistant/graph-rag-private-codebases.git"
37
+ },
38
+ "engines": {
39
+ "node": ">=18"
40
+ },
41
+ "dependencies": {
42
+ "better-sqlite3": "^11.7.0",
43
+ "commander": "^13.1.0",
44
+ "glob": "^11.0.1",
45
+ "openai": "^4.78.0",
46
+ "@anthropic-ai/sdk": "^0.39.0",
47
+ "@modelcontextprotocol/sdk": "^1.12.1"
48
+ },
49
+ "devDependencies": {
50
+ "@types/better-sqlite3": "^7.6.12",
51
+ "@types/node": "^22.10.0",
52
+ "typescript": "^5.7.0"
53
+ }
54
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { indexRepo } from './commands/index-repo.js';
4
+ import { queryCode } from './commands/query.js';
5
+ import { serve } from './commands/serve.js';
6
+
7
+ const program = new Command();
8
+
9
+ program
10
+ .name('graphrag')
11
+ .description('Graph RAG for private codebases — self-hosted code intelligence')
12
+ .version('0.1.0');
13
+
14
+ program
15
+ .command('index <repo>')
16
+ .description('Index a codebase into a knowledge graph')
17
+ .option('--extensions <exts>', 'File extensions to index (comma-separated)', 'ts,tsx,js,jsx,py')
18
+ .option('--db <path>', 'Database path', '.graphrag/index.db')
19
+ .option('--embed', 'Generate embeddings (requires OPENAI_API_KEY)', false)
20
+ .action(async (repo: string, opts) => {
21
+ await indexRepo(repo, {
22
+ extensions: opts.extensions.split(','),
23
+ dbPath: opts.db,
24
+ embed: opts.embed,
25
+ });
26
+ });
27
+
28
+ program
29
+ .command('query <question>')
30
+ .description('Semantic search over indexed codebase with graph context')
31
+ .option('--db <path>', 'Database path', '.graphrag/index.db')
32
+ .option('--limit <n>', 'Max results', '5')
33
+ .action(async (question: string, opts) => {
34
+ await queryCode(question, {
35
+ dbPath: opts.db,
36
+ limit: parseInt(opts.limit, 10),
37
+ });
38
+ });
39
+
40
+ program
41
+ .command('serve')
42
+ .description('Start MCP server for code intelligence')
43
+ .option('--db <path>', 'Database path', '.graphrag/index.db')
44
+ .option('--port <n>', 'Port number', '3700')
45
+ .action(async (opts) => {
46
+ await serve({
47
+ dbPath: opts.db,
48
+ port: parseInt(opts.port, 10),
49
+ });
50
+ });
51
+
52
+ program.parse();
@@ -0,0 +1,96 @@
1
+ import path from 'path';
2
+ import { glob } from 'glob';
3
+ import { GraphDB } from '../db.js';
4
+ import { parseFile } from '../parser.js';
5
+ import { getEmbeddings } from '../embeddings.js';
6
+
7
+ interface IndexOptions {
8
+ extensions: string[];
9
+ dbPath: string;
10
+ embed: boolean;
11
+ }
12
+
13
+ export async function indexRepo(repoPath: string, opts: IndexOptions): Promise<void> {
14
+ const absRepo = path.resolve(repoPath);
15
+ const dbPath = path.isAbsolute(opts.dbPath) ? opts.dbPath : path.join(absRepo, opts.dbPath);
16
+ const db = new GraphDB(dbPath);
17
+
18
+ const patterns = opts.extensions.map(ext => `**/*.${ext}`);
19
+ const files = await glob(patterns, {
20
+ cwd: absRepo,
21
+ ignore: ['**/node_modules/**', '**/dist/**', '**/.git/**', '**/venv/**', '**/__pycache__/**'],
22
+ absolute: true,
23
+ });
24
+
25
+ console.log(`Indexing ${files.length} files from ${absRepo}`);
26
+
27
+ const entityNameToId = new Map<string, number>();
28
+ let totalEntities = 0;
29
+ let totalEdges = 0;
30
+ const pendingEdges: Array<{ sourceName: string; targetName: string; relation: string }> = [];
31
+ const chunksToEmbed: Array<{ entityId: number; content: string }> = [];
32
+
33
+ for (const file of files) {
34
+ const relPath = path.relative(absRepo, file);
35
+ db.clearFile(relPath);
36
+
37
+ const result = parseFile(file);
38
+
39
+ for (const entity of result.entities) {
40
+ const e = { ...entity, filePath: relPath };
41
+ const id = db.insertEntity(e);
42
+ entityNameToId.set(e.name, id);
43
+ totalEntities++;
44
+
45
+ // Create chunk for non-import entities
46
+ if (e.kind !== 'import') {
47
+ chunksToEmbed.push({ entityId: id, content: `${e.kind} ${e.name}\n${e.code}` });
48
+ }
49
+ }
50
+
51
+ pendingEdges.push(...result.edges);
52
+ }
53
+
54
+ // Resolve and insert edges
55
+ for (const edge of pendingEdges) {
56
+ const sourceId = entityNameToId.get(edge.sourceName);
57
+ const targetId = entityNameToId.get(edge.targetName);
58
+ if (sourceId && targetId) {
59
+ db.insertEdge({ sourceId, targetId, relation: edge.relation as any });
60
+ totalEdges++;
61
+ }
62
+ }
63
+
64
+ // Generate embeddings in batches
65
+ if (opts.embed && process.env.OPENAI_API_KEY) {
66
+ console.log(`Generating embeddings for ${chunksToEmbed.length} chunks...`);
67
+ const batchSize = 100;
68
+ for (let i = 0; i < chunksToEmbed.length; i += batchSize) {
69
+ const batch = chunksToEmbed.slice(i, i + batchSize);
70
+ const embeddings = await getEmbeddings(batch.map(c => c.content));
71
+ for (let j = 0; j < batch.length; j++) {
72
+ db.insertChunk({
73
+ entityId: batch[j].entityId,
74
+ content: batch[j].content,
75
+ embedding: new Float64Array(embeddings[j]),
76
+ });
77
+ }
78
+ console.log(` Embedded ${Math.min(i + batchSize, chunksToEmbed.length)}/${chunksToEmbed.length}`);
79
+ }
80
+ } else {
81
+ // Store chunks without embeddings
82
+ for (const chunk of chunksToEmbed) {
83
+ db.insertChunk({ entityId: chunk.entityId, content: chunk.content });
84
+ }
85
+ }
86
+
87
+ const stats = db.getStats();
88
+ console.log(`\nDone! Indexed:`);
89
+ console.log(` Files: ${stats.files}`);
90
+ console.log(` Entities: ${stats.entities}`);
91
+ console.log(` Edges: ${stats.edges}`);
92
+ console.log(` Chunks: ${stats.chunks}`);
93
+ console.log(`\nDatabase: ${dbPath}`);
94
+
95
+ db.close();
96
+ }
@@ -0,0 +1,43 @@
1
+ import path from 'path';
2
+ import { GraphDB } from '../db.js';
3
+
4
+ interface QueryOptions {
5
+ dbPath: string;
6
+ limit: number;
7
+ }
8
+
9
+ export async function queryCode(question: string, opts: QueryOptions): Promise<void> {
10
+ const dbPath = path.resolve(opts.dbPath);
11
+ const db = new GraphDB(dbPath);
12
+
13
+ const results = db.searchEntities(question, opts.limit);
14
+
15
+ if (results.length === 0) {
16
+ console.log('No results found.');
17
+ db.close();
18
+ return;
19
+ }
20
+
21
+ console.log(`Found ${results.length} results:\n`);
22
+
23
+ for (const entity of results) {
24
+ const id = (entity as any).id as number;
25
+ console.log(`━━━ ${entity.kind}: ${entity.name} ━━━`);
26
+ console.log(`File: ${entity.filePath}:${entity.startLine}-${entity.endLine}`);
27
+
28
+ // Get related entities for context
29
+ const related = db.getRelated(id);
30
+ if (related.length > 0) {
31
+ console.log(`Relations:`);
32
+ for (const r of related.slice(0, 5)) {
33
+ console.log(` ${r.relation} → ${r.entity.name} (${r.entity.kind})`);
34
+ }
35
+ }
36
+
37
+ console.log(`\n${entity.code.substring(0, 300)}`);
38
+ if (entity.code.length > 300) console.log(' ...');
39
+ console.log('');
40
+ }
41
+
42
+ db.close();
43
+ }
@@ -0,0 +1,90 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { z } from 'zod';
4
+ import path from 'path';
5
+ import { GraphDB } from '../db.js';
6
+
7
+ interface ServeOptions {
8
+ dbPath: string;
9
+ port: number;
10
+ }
11
+
12
+ export async function serve(opts: ServeOptions): Promise<void> {
13
+ const dbPath = path.resolve(opts.dbPath);
14
+ const db = new GraphDB(dbPath);
15
+
16
+ const server = new McpServer({
17
+ name: 'graphrag',
18
+ version: '0.1.0',
19
+ });
20
+
21
+ server.tool(
22
+ 'search_codebase',
23
+ 'Search the indexed codebase for functions, classes, and code patterns',
24
+ { query: z.string().describe('Search query'), limit: z.number().optional().describe('Max results (default 5)') },
25
+ async ({ query, limit }) => {
26
+ const results = db.searchEntities(query, limit ?? 5);
27
+ return {
28
+ content: [{
29
+ type: 'text' as const,
30
+ text: JSON.stringify(results.map(e => ({
31
+ name: e.name,
32
+ kind: e.kind,
33
+ file: `${e.filePath}:${e.startLine}-${e.endLine}`,
34
+ code: e.code.substring(0, 500),
35
+ })), null, 2),
36
+ }],
37
+ };
38
+ }
39
+ );
40
+
41
+ server.tool(
42
+ 'get_function_context',
43
+ 'Get a function/class with its full graph context (callers, callees, imports)',
44
+ { name: z.string().describe('Function or class name') },
45
+ async ({ name }) => {
46
+ const entities = db.findEntitiesByName(name);
47
+ if (entities.length === 0) {
48
+ return { content: [{ type: 'text' as const, text: `No entity found: ${name}` }] };
49
+ }
50
+ const entity = entities[0];
51
+ const id = (entity as any).id as number;
52
+ const related = db.getRelated(id);
53
+ return {
54
+ content: [{
55
+ type: 'text' as const,
56
+ text: JSON.stringify({
57
+ entity: { name: entity.name, kind: entity.kind, file: entity.filePath, code: entity.code },
58
+ related: related.map(r => ({
59
+ name: r.entity.name, kind: r.entity.kind, relation: r.relation,
60
+ file: r.entity.filePath,
61
+ })),
62
+ }, null, 2),
63
+ }],
64
+ };
65
+ }
66
+ );
67
+
68
+ server.tool(
69
+ 'find_usages',
70
+ 'Find all usages of a function, class, or variable',
71
+ { name: z.string().describe('Entity name to find usages of') },
72
+ async ({ name }) => {
73
+ const usages = db.getUsages(name);
74
+ return {
75
+ content: [{
76
+ type: 'text' as const,
77
+ text: JSON.stringify(usages.map(e => ({
78
+ name: e.name, kind: e.kind,
79
+ file: `${e.filePath}:${e.startLine}-${e.endLine}`,
80
+ code: e.code.substring(0, 200),
81
+ })), null, 2),
82
+ }],
83
+ };
84
+ }
85
+ );
86
+
87
+ const transport = new StdioServerTransport();
88
+ await server.connect(transport);
89
+ console.error('GraphRAG MCP server running on stdio');
90
+ }