@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.
- package/.github/ISSUE_TEMPLATE/bug_report.md +17 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +12 -0
- package/.github/pull_request_template.md +5 -0
- package/.github/workflows/ci.yml +22 -0
- package/LICENSE +21 -0
- package/README.md +103 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +47 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/index-repo.d.ts +8 -0
- package/dist/commands/index-repo.d.ts.map +1 -0
- package/dist/commands/index-repo.js +79 -0
- package/dist/commands/index-repo.js.map +1 -0
- package/dist/commands/query.d.ts +7 -0
- package/dist/commands/query.d.ts.map +1 -0
- package/dist/commands/query.js +32 -0
- package/dist/commands/query.js.map +1 -0
- package/dist/commands/serve.d.ts +7 -0
- package/dist/commands/serve.d.ts.map +1 -0
- package/dist/commands/serve.js +65 -0
- package/dist/commands/serve.js.map +1 -0
- package/dist/db.d.ts +46 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +117 -0
- package/dist/db.js.map +1 -0
- package/dist/embeddings.d.ts +4 -0
- package/dist/embeddings.d.ts.map +1 -0
- package/dist/embeddings.js +35 -0
- package/dist/embeddings.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/parser.d.ts +12 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +152 -0
- package/dist/parser.js.map +1 -0
- package/package.json +54 -0
- package/src/cli.ts +52 -0
- package/src/commands/index-repo.ts +96 -0
- package/src/commands/query.ts +43 -0
- package/src/commands/serve.ts +90 -0
- package/src/db.ts +168 -0
- package/src/embeddings.ts +39 -0
- package/src/index.ts +5 -0
- package/src/parser.ts +162 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
node-version: [18, 20, 22]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-node@v4
|
|
18
|
+
with:
|
|
19
|
+
node-version: ${{ matrix.node-version }}
|
|
20
|
+
- run: npm ci
|
|
21
|
+
- run: npm run build
|
|
22
|
+
- run: npm run lint
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Phoenix AI Hub
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# @phoenixaihub/graphrag
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@phoenixaihub/graphrag)
|
|
4
|
+
[](https://github.com/phoenix-assistant/graph-rag-private-codebases/actions/workflows/ci.yml)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
**Graph RAG for private codebases** — index your code into a knowledge graph with semantic search and an MCP server. Self-hosted, no code leaves your machine.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- 🔍 **Index** TypeScript, JavaScript, and Python codebases into a knowledge graph
|
|
12
|
+
- 🧠 **Semantic search** over code with graph-aware context expansion
|
|
13
|
+
- 🔗 **Graph relationships** — functions, classes, imports, exports, call chains
|
|
14
|
+
- 🤖 **MCP server** — plug into Claude, Cursor, or any MCP-compatible client
|
|
15
|
+
- 🏠 **Self-hosted** — SQLite-backed, runs locally, zero cloud dependencies
|
|
16
|
+
- ⚡ **Optional embeddings** via OpenAI `text-embedding-3-small`
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install -g @phoenixaihub/graphrag
|
|
22
|
+
|
|
23
|
+
# Index a repo
|
|
24
|
+
graphrag index ./my-project
|
|
25
|
+
|
|
26
|
+
# Search code
|
|
27
|
+
graphrag query "authentication middleware"
|
|
28
|
+
|
|
29
|
+
# Start MCP server
|
|
30
|
+
graphrag serve --db ./my-project/.graphrag/index.db
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## MCP Configuration
|
|
34
|
+
|
|
35
|
+
Add to your Claude Desktop / Cursor config:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"mcpServers": {
|
|
40
|
+
"graphrag": {
|
|
41
|
+
"command": "graphrag",
|
|
42
|
+
"args": ["serve", "--db", "/path/to/project/.graphrag/index.db"]
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Available MCP Tools
|
|
49
|
+
|
|
50
|
+
| Tool | Description |
|
|
51
|
+
|------|-------------|
|
|
52
|
+
| `search_codebase` | Semantic search across indexed code entities |
|
|
53
|
+
| `get_function_context` | Get a function/class with full graph context (callers, callees, imports) |
|
|
54
|
+
| `find_usages` | Find all usages of a function, class, or variable |
|
|
55
|
+
|
|
56
|
+
## With Embeddings
|
|
57
|
+
|
|
58
|
+
Set `OPENAI_API_KEY` and use `--embed`:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
export OPENAI_API_KEY=sk-...
|
|
62
|
+
graphrag index ./my-project --embed
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## How It Works
|
|
66
|
+
|
|
67
|
+
1. **Parse** — Extracts functions, classes, imports/exports using regex-based parsing
|
|
68
|
+
2. **Graph** — Builds entity relationship graph (contains, imports, calls, extends)
|
|
69
|
+
3. **Store** — Persists to SQLite with full-text search indexes
|
|
70
|
+
4. **Search** — Text search + graph traversal for context-rich results
|
|
71
|
+
5. **Serve** — MCP server exposes tools for AI-assisted code exploration
|
|
72
|
+
|
|
73
|
+
## Comparison
|
|
74
|
+
|
|
75
|
+
| Feature | GraphRAG | Sourcegraph | Codeium |
|
|
76
|
+
|---------|----------|-------------|---------|
|
|
77
|
+
| Self-hosted | ✅ Local SQLite | ❌ Server required | ❌ Cloud |
|
|
78
|
+
| Privacy | ✅ No code leaves machine | ⚠️ Depends on deployment | ❌ Cloud-processed |
|
|
79
|
+
| MCP support | ✅ Built-in | ❌ | ❌ |
|
|
80
|
+
| Setup time | < 1 min | Hours | Minutes |
|
|
81
|
+
| Cost | Free | $$$$ | Free tier limited |
|
|
82
|
+
| Graph relationships | ✅ | ✅ | ❌ |
|
|
83
|
+
| Languages | TS/JS/Python | 30+ | 70+ |
|
|
84
|
+
|
|
85
|
+
## Programmatic API
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { GraphDB, parseFile } from '@phoenixaihub/graphrag';
|
|
89
|
+
|
|
90
|
+
const db = new GraphDB('.graphrag/index.db');
|
|
91
|
+
const result = parseFile('./src/main.ts');
|
|
92
|
+
|
|
93
|
+
for (const entity of result.entities) {
|
|
94
|
+
db.insertEntity(entity);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const results = db.searchEntities('handler', 5);
|
|
98
|
+
console.log(results);
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
MIT
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
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
|
+
const program = new Command();
|
|
7
|
+
program
|
|
8
|
+
.name('graphrag')
|
|
9
|
+
.description('Graph RAG for private codebases — self-hosted code intelligence')
|
|
10
|
+
.version('0.1.0');
|
|
11
|
+
program
|
|
12
|
+
.command('index <repo>')
|
|
13
|
+
.description('Index a codebase into a knowledge graph')
|
|
14
|
+
.option('--extensions <exts>', 'File extensions to index (comma-separated)', 'ts,tsx,js,jsx,py')
|
|
15
|
+
.option('--db <path>', 'Database path', '.graphrag/index.db')
|
|
16
|
+
.option('--embed', 'Generate embeddings (requires OPENAI_API_KEY)', false)
|
|
17
|
+
.action(async (repo, opts) => {
|
|
18
|
+
await indexRepo(repo, {
|
|
19
|
+
extensions: opts.extensions.split(','),
|
|
20
|
+
dbPath: opts.db,
|
|
21
|
+
embed: opts.embed,
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
program
|
|
25
|
+
.command('query <question>')
|
|
26
|
+
.description('Semantic search over indexed codebase with graph context')
|
|
27
|
+
.option('--db <path>', 'Database path', '.graphrag/index.db')
|
|
28
|
+
.option('--limit <n>', 'Max results', '5')
|
|
29
|
+
.action(async (question, opts) => {
|
|
30
|
+
await queryCode(question, {
|
|
31
|
+
dbPath: opts.db,
|
|
32
|
+
limit: parseInt(opts.limit, 10),
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
program
|
|
36
|
+
.command('serve')
|
|
37
|
+
.description('Start MCP server for code intelligence')
|
|
38
|
+
.option('--db <path>', 'Database path', '.graphrag/index.db')
|
|
39
|
+
.option('--port <n>', 'Port number', '3700')
|
|
40
|
+
.action(async (opts) => {
|
|
41
|
+
await serve({
|
|
42
|
+
dbPath: opts.db,
|
|
43
|
+
port: parseInt(opts.port, 10),
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
program.parse();
|
|
47
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAE5C,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,UAAU,CAAC;KAChB,WAAW,CAAC,iEAAiE,CAAC;KAC9E,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,cAAc,CAAC;KACvB,WAAW,CAAC,yCAAyC,CAAC;KACtD,MAAM,CAAC,qBAAqB,EAAE,4CAA4C,EAAE,kBAAkB,CAAC;KAC/F,MAAM,CAAC,aAAa,EAAE,eAAe,EAAE,oBAAoB,CAAC;KAC5D,MAAM,CAAC,SAAS,EAAE,+CAA+C,EAAE,KAAK,CAAC;KACzE,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,IAAI,EAAE,EAAE;IACnC,MAAM,SAAS,CAAC,IAAI,EAAE;QACpB,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC;QACtC,MAAM,EAAE,IAAI,CAAC,EAAE;QACf,KAAK,EAAE,IAAI,CAAC,KAAK;KAClB,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,kBAAkB,CAAC;KAC3B,WAAW,CAAC,0DAA0D,CAAC;KACvE,MAAM,CAAC,aAAa,EAAE,eAAe,EAAE,oBAAoB,CAAC;KAC5D,MAAM,CAAC,aAAa,EAAE,aAAa,EAAE,GAAG,CAAC;KACzC,MAAM,CAAC,KAAK,EAAE,QAAgB,EAAE,IAAI,EAAE,EAAE;IACvC,MAAM,SAAS,CAAC,QAAQ,EAAE;QACxB,MAAM,EAAE,IAAI,CAAC,EAAE;QACf,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;KAChC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,wCAAwC,CAAC;KACrD,MAAM,CAAC,aAAa,EAAE,eAAe,EAAE,oBAAoB,CAAC;KAC5D,MAAM,CAAC,YAAY,EAAE,aAAa,EAAE,MAAM,CAAC;KAC3C,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,MAAM,KAAK,CAAC;QACV,MAAM,EAAE,IAAI,CAAC,EAAE;QACf,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;KAC9B,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-repo.d.ts","sourceRoot":"","sources":["../../src/commands/index-repo.ts"],"names":[],"mappings":"AAMA,UAAU,YAAY;IACpB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAmFnF"}
|
|
@@ -0,0 +1,79 @@
|
|
|
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
|
+
export async function indexRepo(repoPath, opts) {
|
|
7
|
+
const absRepo = path.resolve(repoPath);
|
|
8
|
+
const dbPath = path.isAbsolute(opts.dbPath) ? opts.dbPath : path.join(absRepo, opts.dbPath);
|
|
9
|
+
const db = new GraphDB(dbPath);
|
|
10
|
+
const patterns = opts.extensions.map(ext => `**/*.${ext}`);
|
|
11
|
+
const files = await glob(patterns, {
|
|
12
|
+
cwd: absRepo,
|
|
13
|
+
ignore: ['**/node_modules/**', '**/dist/**', '**/.git/**', '**/venv/**', '**/__pycache__/**'],
|
|
14
|
+
absolute: true,
|
|
15
|
+
});
|
|
16
|
+
console.log(`Indexing ${files.length} files from ${absRepo}`);
|
|
17
|
+
const entityNameToId = new Map();
|
|
18
|
+
let totalEntities = 0;
|
|
19
|
+
let totalEdges = 0;
|
|
20
|
+
const pendingEdges = [];
|
|
21
|
+
const chunksToEmbed = [];
|
|
22
|
+
for (const file of files) {
|
|
23
|
+
const relPath = path.relative(absRepo, file);
|
|
24
|
+
db.clearFile(relPath);
|
|
25
|
+
const result = parseFile(file);
|
|
26
|
+
for (const entity of result.entities) {
|
|
27
|
+
const e = { ...entity, filePath: relPath };
|
|
28
|
+
const id = db.insertEntity(e);
|
|
29
|
+
entityNameToId.set(e.name, id);
|
|
30
|
+
totalEntities++;
|
|
31
|
+
// Create chunk for non-import entities
|
|
32
|
+
if (e.kind !== 'import') {
|
|
33
|
+
chunksToEmbed.push({ entityId: id, content: `${e.kind} ${e.name}\n${e.code}` });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
pendingEdges.push(...result.edges);
|
|
37
|
+
}
|
|
38
|
+
// Resolve and insert edges
|
|
39
|
+
for (const edge of pendingEdges) {
|
|
40
|
+
const sourceId = entityNameToId.get(edge.sourceName);
|
|
41
|
+
const targetId = entityNameToId.get(edge.targetName);
|
|
42
|
+
if (sourceId && targetId) {
|
|
43
|
+
db.insertEdge({ sourceId, targetId, relation: edge.relation });
|
|
44
|
+
totalEdges++;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Generate embeddings in batches
|
|
48
|
+
if (opts.embed && process.env.OPENAI_API_KEY) {
|
|
49
|
+
console.log(`Generating embeddings for ${chunksToEmbed.length} chunks...`);
|
|
50
|
+
const batchSize = 100;
|
|
51
|
+
for (let i = 0; i < chunksToEmbed.length; i += batchSize) {
|
|
52
|
+
const batch = chunksToEmbed.slice(i, i + batchSize);
|
|
53
|
+
const embeddings = await getEmbeddings(batch.map(c => c.content));
|
|
54
|
+
for (let j = 0; j < batch.length; j++) {
|
|
55
|
+
db.insertChunk({
|
|
56
|
+
entityId: batch[j].entityId,
|
|
57
|
+
content: batch[j].content,
|
|
58
|
+
embedding: new Float64Array(embeddings[j]),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
console.log(` Embedded ${Math.min(i + batchSize, chunksToEmbed.length)}/${chunksToEmbed.length}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
// Store chunks without embeddings
|
|
66
|
+
for (const chunk of chunksToEmbed) {
|
|
67
|
+
db.insertChunk({ entityId: chunk.entityId, content: chunk.content });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const stats = db.getStats();
|
|
71
|
+
console.log(`\nDone! Indexed:`);
|
|
72
|
+
console.log(` Files: ${stats.files}`);
|
|
73
|
+
console.log(` Entities: ${stats.entities}`);
|
|
74
|
+
console.log(` Edges: ${stats.edges}`);
|
|
75
|
+
console.log(` Chunks: ${stats.chunks}`);
|
|
76
|
+
console.log(`\nDatabase: ${dbPath}`);
|
|
77
|
+
db.close();
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=index-repo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-repo.js","sourceRoot":"","sources":["../../src/commands/index-repo.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/commands/query.ts"],"names":[],"mappings":"AAGA,UAAU,YAAY;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAkCnF"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { GraphDB } from '../db.js';
|
|
3
|
+
export async function queryCode(question, opts) {
|
|
4
|
+
const dbPath = path.resolve(opts.dbPath);
|
|
5
|
+
const db = new GraphDB(dbPath);
|
|
6
|
+
const results = db.searchEntities(question, opts.limit);
|
|
7
|
+
if (results.length === 0) {
|
|
8
|
+
console.log('No results found.');
|
|
9
|
+
db.close();
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
console.log(`Found ${results.length} results:\n`);
|
|
13
|
+
for (const entity of results) {
|
|
14
|
+
const id = entity.id;
|
|
15
|
+
console.log(`━━━ ${entity.kind}: ${entity.name} ━━━`);
|
|
16
|
+
console.log(`File: ${entity.filePath}:${entity.startLine}-${entity.endLine}`);
|
|
17
|
+
// Get related entities for context
|
|
18
|
+
const related = db.getRelated(id);
|
|
19
|
+
if (related.length > 0) {
|
|
20
|
+
console.log(`Relations:`);
|
|
21
|
+
for (const r of related.slice(0, 5)) {
|
|
22
|
+
console.log(` ${r.relation} → ${r.entity.name} (${r.entity.kind})`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
console.log(`\n${entity.code.substring(0, 300)}`);
|
|
26
|
+
if (entity.code.length > 300)
|
|
27
|
+
console.log(' ...');
|
|
28
|
+
console.log('');
|
|
29
|
+
}
|
|
30
|
+
db.close();
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/commands/query.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAOnC,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,QAAgB,EAAE,IAAkB;IAClE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,EAAE,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAE/B,MAAM,OAAO,GAAG,EAAE,CAAC,cAAc,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IAExD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;QACjC,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,SAAS,OAAO,CAAC,MAAM,aAAa,CAAC,CAAC;IAElD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,EAAE,GAAI,MAAc,CAAC,EAAY,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,OAAO,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,CAAC;QACtD,OAAO,CAAC,GAAG,CAAC,SAAS,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;QAE9E,mCAAmC;QACnC,MAAM,OAAO,GAAG,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QAClC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YAC1B,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;gBACpC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,QAAQ,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC;YACvE,CAAC;QACH,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QAClD,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG;YAAE,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC;IAED,EAAE,CAAC,KAAK,EAAE,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAMA,UAAU,YAAY;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,KAAK,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA8E7D"}
|
|
@@ -0,0 +1,65 @@
|
|
|
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
|
+
export async function serve(opts) {
|
|
7
|
+
const dbPath = path.resolve(opts.dbPath);
|
|
8
|
+
const db = new GraphDB(dbPath);
|
|
9
|
+
const server = new McpServer({
|
|
10
|
+
name: 'graphrag',
|
|
11
|
+
version: '0.1.0',
|
|
12
|
+
});
|
|
13
|
+
server.tool('search_codebase', 'Search the indexed codebase for functions, classes, and code patterns', { query: z.string().describe('Search query'), limit: z.number().optional().describe('Max results (default 5)') }, async ({ query, limit }) => {
|
|
14
|
+
const results = db.searchEntities(query, limit ?? 5);
|
|
15
|
+
return {
|
|
16
|
+
content: [{
|
|
17
|
+
type: 'text',
|
|
18
|
+
text: JSON.stringify(results.map(e => ({
|
|
19
|
+
name: e.name,
|
|
20
|
+
kind: e.kind,
|
|
21
|
+
file: `${e.filePath}:${e.startLine}-${e.endLine}`,
|
|
22
|
+
code: e.code.substring(0, 500),
|
|
23
|
+
})), null, 2),
|
|
24
|
+
}],
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
server.tool('get_function_context', 'Get a function/class with its full graph context (callers, callees, imports)', { name: z.string().describe('Function or class name') }, async ({ name }) => {
|
|
28
|
+
const entities = db.findEntitiesByName(name);
|
|
29
|
+
if (entities.length === 0) {
|
|
30
|
+
return { content: [{ type: 'text', text: `No entity found: ${name}` }] };
|
|
31
|
+
}
|
|
32
|
+
const entity = entities[0];
|
|
33
|
+
const id = entity.id;
|
|
34
|
+
const related = db.getRelated(id);
|
|
35
|
+
return {
|
|
36
|
+
content: [{
|
|
37
|
+
type: 'text',
|
|
38
|
+
text: JSON.stringify({
|
|
39
|
+
entity: { name: entity.name, kind: entity.kind, file: entity.filePath, code: entity.code },
|
|
40
|
+
related: related.map(r => ({
|
|
41
|
+
name: r.entity.name, kind: r.entity.kind, relation: r.relation,
|
|
42
|
+
file: r.entity.filePath,
|
|
43
|
+
})),
|
|
44
|
+
}, null, 2),
|
|
45
|
+
}],
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
server.tool('find_usages', 'Find all usages of a function, class, or variable', { name: z.string().describe('Entity name to find usages of') }, async ({ name }) => {
|
|
49
|
+
const usages = db.getUsages(name);
|
|
50
|
+
return {
|
|
51
|
+
content: [{
|
|
52
|
+
type: 'text',
|
|
53
|
+
text: JSON.stringify(usages.map(e => ({
|
|
54
|
+
name: e.name, kind: e.kind,
|
|
55
|
+
file: `${e.filePath}:${e.startLine}-${e.endLine}`,
|
|
56
|
+
code: e.code.substring(0, 200),
|
|
57
|
+
})), null, 2),
|
|
58
|
+
}],
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
const transport = new StdioServerTransport();
|
|
62
|
+
await server.connect(transport);
|
|
63
|
+
console.error('GraphRAG MCP server running on stdio');
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=serve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"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"}
|
package/dist/db.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export interface Entity {
|
|
2
|
+
id?: number;
|
|
3
|
+
filePath: string;
|
|
4
|
+
name: string;
|
|
5
|
+
kind: 'file' | 'function' | 'class' | 'method' | 'variable' | 'import' | 'export';
|
|
6
|
+
startLine: number;
|
|
7
|
+
endLine: number;
|
|
8
|
+
code: string;
|
|
9
|
+
language: string;
|
|
10
|
+
}
|
|
11
|
+
export interface Edge {
|
|
12
|
+
id?: number;
|
|
13
|
+
sourceId: number;
|
|
14
|
+
targetId: number;
|
|
15
|
+
relation: 'contains' | 'imports' | 'exports' | 'calls' | 'extends' | 'implements';
|
|
16
|
+
}
|
|
17
|
+
export interface Chunk {
|
|
18
|
+
id?: number;
|
|
19
|
+
entityId: number;
|
|
20
|
+
content: string;
|
|
21
|
+
embedding?: Float64Array | null;
|
|
22
|
+
}
|
|
23
|
+
export declare class GraphDB {
|
|
24
|
+
private db;
|
|
25
|
+
constructor(dbPath: string);
|
|
26
|
+
insertEntity(e: Entity): number;
|
|
27
|
+
insertEdge(e: Edge): number;
|
|
28
|
+
insertChunk(c: Chunk): number;
|
|
29
|
+
getEntity(id: number): Entity | undefined;
|
|
30
|
+
findEntitiesByName(name: string): Entity[];
|
|
31
|
+
searchEntities(query: string, limit?: number): Entity[];
|
|
32
|
+
getRelated(entityId: number, direction?: 'outgoing' | 'incoming' | 'both'): Array<{
|
|
33
|
+
entity: Entity;
|
|
34
|
+
relation: string;
|
|
35
|
+
}>;
|
|
36
|
+
getUsages(name: string): Entity[];
|
|
37
|
+
clearFile(filePath: string): void;
|
|
38
|
+
getStats(): {
|
|
39
|
+
entities: number;
|
|
40
|
+
edges: number;
|
|
41
|
+
chunks: number;
|
|
42
|
+
files: number;
|
|
43
|
+
};
|
|
44
|
+
close(): void;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=db.d.ts.map
|
package/dist/db.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,MAAM;IACrB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAClF,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,IAAI;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,YAAY,CAAC;CACnF;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;CACjC;AAoCD,qBAAa,OAAO;IAClB,OAAO,CAAC,EAAE,CAAoB;gBAElB,MAAM,EAAE,MAAM;IAQ1B,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM;IAQ/B,UAAU,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM;IAQ3B,WAAW,CAAC,CAAC,EAAE,KAAK,GAAG,MAAM;IAS7B,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIzC,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAM1C,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,MAAM,EAAE;IAWlD,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,GAAE,UAAU,GAAG,UAAU,GAAG,MAAe,GAAG,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAiB/H,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IASjC,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAUjC,QAAQ,IAAI;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAQ9E,KAAK,IAAI,IAAI;CAGd"}
|
package/dist/db.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
const SCHEMA = `
|
|
5
|
+
CREATE TABLE IF NOT EXISTS entities (
|
|
6
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
7
|
+
file_path TEXT NOT NULL,
|
|
8
|
+
name TEXT NOT NULL,
|
|
9
|
+
kind TEXT NOT NULL,
|
|
10
|
+
start_line INTEGER NOT NULL,
|
|
11
|
+
end_line INTEGER NOT NULL,
|
|
12
|
+
code TEXT NOT NULL,
|
|
13
|
+
language TEXT NOT NULL
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
CREATE TABLE IF NOT EXISTS edges (
|
|
17
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
18
|
+
source_id INTEGER NOT NULL REFERENCES entities(id),
|
|
19
|
+
target_id INTEGER NOT NULL REFERENCES entities(id),
|
|
20
|
+
relation TEXT NOT NULL
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
CREATE TABLE IF NOT EXISTS chunks (
|
|
24
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
25
|
+
entity_id INTEGER NOT NULL REFERENCES entities(id),
|
|
26
|
+
content TEXT NOT NULL,
|
|
27
|
+
embedding BLOB
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
CREATE INDEX IF NOT EXISTS idx_entities_name ON entities(name);
|
|
31
|
+
CREATE INDEX IF NOT EXISTS idx_entities_kind ON entities(kind);
|
|
32
|
+
CREATE INDEX IF NOT EXISTS idx_entities_file ON entities(file_path);
|
|
33
|
+
CREATE INDEX IF NOT EXISTS idx_edges_source ON edges(source_id);
|
|
34
|
+
CREATE INDEX IF NOT EXISTS idx_edges_target ON edges(target_id);
|
|
35
|
+
CREATE INDEX IF NOT EXISTS idx_chunks_entity ON chunks(entity_id);
|
|
36
|
+
`;
|
|
37
|
+
export class GraphDB {
|
|
38
|
+
db;
|
|
39
|
+
constructor(dbPath) {
|
|
40
|
+
const dir = path.dirname(dbPath);
|
|
41
|
+
if (!fs.existsSync(dir))
|
|
42
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
43
|
+
this.db = new Database(dbPath);
|
|
44
|
+
this.db.pragma('journal_mode = WAL');
|
|
45
|
+
this.db.exec(SCHEMA);
|
|
46
|
+
}
|
|
47
|
+
insertEntity(e) {
|
|
48
|
+
const stmt = this.db.prepare('INSERT INTO entities (file_path, name, kind, start_line, end_line, code, language) VALUES (?, ?, ?, ?, ?, ?, ?)');
|
|
49
|
+
const result = stmt.run(e.filePath, e.name, e.kind, e.startLine, e.endLine, e.code, e.language);
|
|
50
|
+
return Number(result.lastInsertRowid);
|
|
51
|
+
}
|
|
52
|
+
insertEdge(e) {
|
|
53
|
+
const stmt = this.db.prepare('INSERT INTO edges (source_id, target_id, relation) VALUES (?, ?, ?)');
|
|
54
|
+
const result = stmt.run(e.sourceId, e.targetId, e.relation);
|
|
55
|
+
return Number(result.lastInsertRowid);
|
|
56
|
+
}
|
|
57
|
+
insertChunk(c) {
|
|
58
|
+
const stmt = this.db.prepare('INSERT INTO chunks (entity_id, content, embedding) VALUES (?, ?, ?)');
|
|
59
|
+
const embeddingBlob = c.embedding ? Buffer.from(c.embedding.buffer) : null;
|
|
60
|
+
const result = stmt.run(c.entityId, c.content, embeddingBlob);
|
|
61
|
+
return Number(result.lastInsertRowid);
|
|
62
|
+
}
|
|
63
|
+
getEntity(id) {
|
|
64
|
+
return this.db.prepare('SELECT * FROM entities WHERE id = ?').get(id);
|
|
65
|
+
}
|
|
66
|
+
findEntitiesByName(name) {
|
|
67
|
+
return this.db.prepare('SELECT * FROM entities WHERE name LIKE ?').all(`%${name}%`);
|
|
68
|
+
}
|
|
69
|
+
searchEntities(query, limit = 5) {
|
|
70
|
+
// Text-based search across name and code
|
|
71
|
+
const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
72
|
+
if (terms.length === 0)
|
|
73
|
+
return [];
|
|
74
|
+
const conditions = terms.map(() => '(LOWER(name) LIKE ? OR LOWER(code) LIKE ?)').join(' AND ');
|
|
75
|
+
const params = terms.flatMap(t => [`%${t}%`, `%${t}%`]);
|
|
76
|
+
return this.db.prepare(`SELECT * FROM entities WHERE ${conditions} ORDER BY kind, name LIMIT ?`).all(...params, limit);
|
|
77
|
+
}
|
|
78
|
+
getRelated(entityId, direction = 'both') {
|
|
79
|
+
const results = [];
|
|
80
|
+
if (direction === 'outgoing' || direction === 'both') {
|
|
81
|
+
const rows = this.db.prepare('SELECT e.*, ed.relation FROM entities e JOIN edges ed ON e.id = ed.target_id WHERE ed.source_id = ?').all(entityId);
|
|
82
|
+
results.push(...rows.map(r => ({ entity: r, relation: r.relation })));
|
|
83
|
+
}
|
|
84
|
+
if (direction === 'incoming' || direction === 'both') {
|
|
85
|
+
const rows = this.db.prepare('SELECT e.*, ed.relation FROM entities e JOIN edges ed ON e.id = ed.source_id WHERE ed.target_id = ?').all(entityId);
|
|
86
|
+
results.push(...rows.map(r => ({ entity: r, relation: r.relation })));
|
|
87
|
+
}
|
|
88
|
+
return results;
|
|
89
|
+
}
|
|
90
|
+
getUsages(name) {
|
|
91
|
+
return this.db.prepare(`SELECT DISTINCT e2.* FROM entities e1
|
|
92
|
+
JOIN edges ed ON e1.id = ed.target_id
|
|
93
|
+
JOIN entities e2 ON e2.id = ed.source_id
|
|
94
|
+
WHERE e1.name = ? AND ed.relation IN ('calls', 'imports')`).all(name);
|
|
95
|
+
}
|
|
96
|
+
clearFile(filePath) {
|
|
97
|
+
const entities = this.db.prepare('SELECT id FROM entities WHERE file_path = ?').all(filePath);
|
|
98
|
+
const ids = entities.map(e => e.id);
|
|
99
|
+
if (ids.length === 0)
|
|
100
|
+
return;
|
|
101
|
+
const placeholders = ids.map(() => '?').join(',');
|
|
102
|
+
this.db.prepare(`DELETE FROM chunks WHERE entity_id IN (${placeholders})`).run(...ids);
|
|
103
|
+
this.db.prepare(`DELETE FROM edges WHERE source_id IN (${placeholders}) OR target_id IN (${placeholders})`).run(...ids, ...ids);
|
|
104
|
+
this.db.prepare(`DELETE FROM entities WHERE file_path = ?`).run(filePath);
|
|
105
|
+
}
|
|
106
|
+
getStats() {
|
|
107
|
+
const entities = this.db.prepare('SELECT COUNT(*) as c FROM entities').get().c;
|
|
108
|
+
const edges = this.db.prepare('SELECT COUNT(*) as c FROM edges').get().c;
|
|
109
|
+
const chunks = this.db.prepare('SELECT COUNT(*) as c FROM chunks').get().c;
|
|
110
|
+
const files = this.db.prepare('SELECT COUNT(DISTINCT file_path) as c FROM entities').get().c;
|
|
111
|
+
return { entities, edges, chunks, files };
|
|
112
|
+
}
|
|
113
|
+
close() {
|
|
114
|
+
this.db.close();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=db.js.map
|