mcp-codebase-intelligence 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/LICENSE +21 -0
- package/README.md +166 -0
- package/dist/graph/code-graph.d.ts +119 -0
- package/dist/graph/code-graph.js +279 -0
- package/dist/graph/code-graph.js.map +1 -0
- package/dist/graph/schema.d.ts +2 -0
- package/dist/graph/schema.js +62 -0
- package/dist/graph/schema.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +171 -0
- package/dist/index.js.map +1 -0
- package/dist/indexer/file-watcher.d.ts +13 -0
- package/dist/indexer/file-watcher.js +106 -0
- package/dist/indexer/file-watcher.js.map +1 -0
- package/dist/indexer/lang-go.d.ts +2 -0
- package/dist/indexer/lang-go.js +246 -0
- package/dist/indexer/lang-go.js.map +1 -0
- package/dist/indexer/lang-java.d.ts +2 -0
- package/dist/indexer/lang-java.js +307 -0
- package/dist/indexer/lang-java.js.map +1 -0
- package/dist/indexer/lang-python.d.ts +2 -0
- package/dist/indexer/lang-python.js +232 -0
- package/dist/indexer/lang-python.js.map +1 -0
- package/dist/indexer/lang-rust.d.ts +2 -0
- package/dist/indexer/lang-rust.js +323 -0
- package/dist/indexer/lang-rust.js.map +1 -0
- package/dist/indexer/language-plugin.d.ts +22 -0
- package/dist/indexer/language-plugin.js +22 -0
- package/dist/indexer/language-plugin.js.map +1 -0
- package/dist/indexer/tree-sitter-indexer.d.ts +12 -0
- package/dist/indexer/tree-sitter-indexer.js +618 -0
- package/dist/indexer/tree-sitter-indexer.js.map +1 -0
- package/dist/lsp/lsp-client.d.ts +45 -0
- package/dist/lsp/lsp-client.js +315 -0
- package/dist/lsp/lsp-client.js.map +1 -0
- package/dist/lsp/lsp-manager.d.ts +13 -0
- package/dist/lsp/lsp-manager.js +96 -0
- package/dist/lsp/lsp-manager.js.map +1 -0
- package/dist/tools/analyze-impact.d.ts +38 -0
- package/dist/tools/analyze-impact.js +126 -0
- package/dist/tools/analyze-impact.js.map +1 -0
- package/dist/tools/architecture-diagram.d.ts +33 -0
- package/dist/tools/architecture-diagram.js +216 -0
- package/dist/tools/architecture-diagram.js.map +1 -0
- package/dist/tools/find-implementations.d.ts +33 -0
- package/dist/tools/find-implementations.js +68 -0
- package/dist/tools/find-implementations.js.map +1 -0
- package/dist/tools/find-symbol.d.ts +39 -0
- package/dist/tools/find-symbol.js +52 -0
- package/dist/tools/find-symbol.js.map +1 -0
- package/dist/tools/get-call-graph.d.ts +40 -0
- package/dist/tools/get-call-graph.js +174 -0
- package/dist/tools/get-call-graph.js.map +1 -0
- package/dist/tools/get-dependencies.d.ts +28 -0
- package/dist/tools/get-dependencies.js +68 -0
- package/dist/tools/get-dependencies.js.map +1 -0
- package/dist/tools/get-exports.d.ts +23 -0
- package/dist/tools/get-exports.js +40 -0
- package/dist/tools/get-exports.js.map +1 -0
- package/dist/tools/get-references.d.ts +28 -0
- package/dist/tools/get-references.js +53 -0
- package/dist/tools/get-references.js.map +1 -0
- package/dist/tools/get-stats.d.ts +15 -0
- package/dist/tools/get-stats.js +23 -0
- package/dist/tools/get-stats.js.map +1 -0
- package/dist/tools/get-type-info.d.ts +33 -0
- package/dist/tools/get-type-info.js +69 -0
- package/dist/tools/get-type-info.js.map +1 -0
- package/dist/tools/goto-definition.d.ts +33 -0
- package/dist/tools/goto-definition.js +77 -0
- package/dist/tools/goto-definition.js.map +1 -0
- package/dist/tools/natural-language-query.d.ts +23 -0
- package/dist/tools/natural-language-query.js +426 -0
- package/dist/tools/natural-language-query.js.map +1 -0
- package/dist/tools/reindex.d.ts +15 -0
- package/dist/tools/reindex.js +20 -0
- package/dist/tools/reindex.js.map +1 -0
- package/dist/tools/semantic-diff.d.ts +32 -0
- package/dist/tools/semantic-diff.js +407 -0
- package/dist/tools/semantic-diff.js.map +1 -0
- package/dist/utils/logger.d.ts +6 -0
- package/dist/utils/logger.js +15 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +68 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Gaurav Tiwari
|
|
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,166 @@
|
|
|
1
|
+
# mcp-codebase-intelligence
|
|
2
|
+
|
|
3
|
+
**Give your AI assistant a deep understanding of your codebase.**
|
|
4
|
+
|
|
5
|
+
[](https://github.com/g-tiwari/mcp-codebase-intelligence/actions)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
An MCP server that parses your entire codebase with tree-sitter, builds a semantic graph of symbols, references, and dependencies, and lets AI assistants query it in real time. Works with Claude Code, Cursor, VS Code, and any MCP-compatible client.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Why?
|
|
13
|
+
|
|
14
|
+
AI coding assistants are limited by what fits in their context window. When they need to understand your codebase -- find callers of a function, trace a dependency chain, or assess the impact of a change -- they resort to `grep` and guesswork.
|
|
15
|
+
|
|
16
|
+
**mcp-codebase-intelligence gives them structural understanding instead.**
|
|
17
|
+
|
|
18
|
+
| Without | With |
|
|
19
|
+
|---------|------|
|
|
20
|
+
| AI greps for function name, misses qualified calls | AI queries the symbol graph, finds all 47 callers instantly |
|
|
21
|
+
| AI reads files one by one to trace imports | AI gets the full dependency tree in one call |
|
|
22
|
+
| AI reviews a PR by reading the diff | AI analyzes which 12 downstream modules are affected by the change |
|
|
23
|
+
| AI guesses at project structure | AI generates an architecture diagram from the actual import graph |
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Clone and build
|
|
31
|
+
git clone https://github.com/g-tiwari/mcp-codebase-intelligence.git
|
|
32
|
+
cd mcp-codebase-intelligence
|
|
33
|
+
npm install && npm run build
|
|
34
|
+
|
|
35
|
+
# Add to Claude Code
|
|
36
|
+
claude mcp add codebase-intelligence \
|
|
37
|
+
node /path/to/mcp-codebase-intelligence/dist/index.js \
|
|
38
|
+
-e PROJECT_ROOT=/path/to/your/project
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Or add manually to your MCP config (Claude Code, Cursor, VS Code):
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"mcpServers": {
|
|
46
|
+
"codebase-intelligence": {
|
|
47
|
+
"command": "node",
|
|
48
|
+
"args": ["/path/to/mcp-codebase-intelligence/dist/index.js"],
|
|
49
|
+
"env": {
|
|
50
|
+
"PROJECT_ROOT": "/path/to/your/project"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
That's it. The server indexes your codebase on startup and watches for changes.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 14 Tools
|
|
62
|
+
|
|
63
|
+
### Code Navigation
|
|
64
|
+
| Tool | What it does |
|
|
65
|
+
|------|-------------|
|
|
66
|
+
| `find_symbol` | Search for functions, classes, interfaces, types by name. Fuzzy matching, kind/scope filters. |
|
|
67
|
+
| `get_references` | Find all callers/users of a symbol. Transitive: follow the chain N levels deep. |
|
|
68
|
+
| `get_exports` | Public API surface of any file -- all exported symbols with signatures. |
|
|
69
|
+
| `get_dependencies` | Import graph for a file. Transitive: see the full dependency tree. |
|
|
70
|
+
| `get_call_graph` | Who calls this function? What does it call? Tree or mermaid diagram output. |
|
|
71
|
+
|
|
72
|
+
### Code Intelligence (LSP-powered)
|
|
73
|
+
| Tool | What it does |
|
|
74
|
+
|------|-------------|
|
|
75
|
+
| `goto_definition` | Jump to the definition of any symbol at a given position (TS/JS). |
|
|
76
|
+
| `get_type_info` | Get the inferred type of any expression at a given position (TS/JS). |
|
|
77
|
+
| `find_implementations` | Find all implementations of an interface or abstract method (TS/JS). |
|
|
78
|
+
|
|
79
|
+
### Change Analysis
|
|
80
|
+
| Tool | What it does |
|
|
81
|
+
|------|-------------|
|
|
82
|
+
| `semantic_diff` | Feed it `git diff` output. It identifies affected symbols, finds all downstream dependents, and flags breaking changes and high-impact modifications. |
|
|
83
|
+
| `analyze_change_impact` | Point it at specific lines in a file. It tells you which symbols are affected and who depends on them. |
|
|
84
|
+
|
|
85
|
+
### Architecture & Discovery
|
|
86
|
+
| Tool | What it does |
|
|
87
|
+
|------|-------------|
|
|
88
|
+
| `architecture_diagram` | Auto-generate a mermaid diagram of module dependencies, grouped by directory. |
|
|
89
|
+
| `query_codebase` | Ask natural language questions: "find all API endpoints", "what does the orders module do?", "what depends on the database layer?" |
|
|
90
|
+
|
|
91
|
+
### Admin
|
|
92
|
+
| Tool | What it does |
|
|
93
|
+
|------|-------------|
|
|
94
|
+
| `get_index_stats` | How many files, symbols, references, and imports are indexed. |
|
|
95
|
+
| `reindex` | Trigger a full re-index after major changes. |
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 6 Languages
|
|
100
|
+
|
|
101
|
+
| Language | Extensions | Parser |
|
|
102
|
+
|----------|-----------|--------|
|
|
103
|
+
| TypeScript | `.ts` `.tsx` `.mts` `.cts` | tree-sitter + LSP |
|
|
104
|
+
| JavaScript | `.js` `.jsx` `.mjs` `.cjs` | tree-sitter + LSP |
|
|
105
|
+
| Python | `.py` `.pyi` | tree-sitter |
|
|
106
|
+
| Go | `.go` | tree-sitter |
|
|
107
|
+
| Rust | `.rs` | tree-sitter |
|
|
108
|
+
| Java | `.java` | tree-sitter |
|
|
109
|
+
|
|
110
|
+
All languages get symbol extraction, reference tracking, import/export analysis, and call graphs. TypeScript/JavaScript additionally get LSP-powered go-to-definition, type info, and find-implementations.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## How It Works
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
Source Files ──> tree-sitter AST ──> Symbol Extraction ──> SQLite Graph
|
|
118
|
+
│
|
|
119
|
+
File Watcher ─────────────┤ (incremental updates)
|
|
120
|
+
│
|
|
121
|
+
MCP Tools ◄───────────────┘ (AI queries)
|
|
122
|
+
│
|
|
123
|
+
LSP Servers ──> Type Info (TS/JS)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
1. **Parse** -- tree-sitter builds ASTs for all supported files
|
|
127
|
+
2. **Extract** -- language plugins walk the AST to find symbols, references, imports, inheritance
|
|
128
|
+
3. **Store** -- everything goes into a SQLite database with WAL mode, prepared statements, batch transactions
|
|
129
|
+
4. **Watch** -- chokidar monitors the filesystem; changed files are re-indexed incrementally
|
|
130
|
+
5. **Query** -- MCP tools run recursive SQL queries against the graph (transitive references, dependency chains)
|
|
131
|
+
6. **LSP** -- typescript-language-server provides type-aware intelligence for TS/JS
|
|
132
|
+
|
|
133
|
+
### Performance
|
|
134
|
+
|
|
135
|
+
- **Batch indexing** with single-transaction writes
|
|
136
|
+
- **Prepared statement cache** -- 14 SQL statements prepared once at startup
|
|
137
|
+
- **In-memory hash cache** -- skip DB lookups for unchanged files
|
|
138
|
+
- **Incremental updates** -- only re-index files that actually changed
|
|
139
|
+
|
|
140
|
+
Tested on real-world projects: Zod, Express, gin, ripgrep, gson.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Testing
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
npm test
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
90 tests across 7 test suites covering all 6 language parsers, the graph engine, and semantic diff.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Environment Variables
|
|
155
|
+
|
|
156
|
+
| Variable | Description | Default |
|
|
157
|
+
|----------|-------------|---------|
|
|
158
|
+
| `PROJECT_ROOT` | Path to the codebase to index (required) | `cwd()` |
|
|
159
|
+
| `DB_PATH` | Path to SQLite database file | `$PROJECT_ROOT/.codegraph/index.db` |
|
|
160
|
+
| `LOG_LEVEL` | Logging verbosity: debug, info, warn, error | `info` |
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
export interface SymbolInfo {
|
|
3
|
+
name: string;
|
|
4
|
+
kind: string;
|
|
5
|
+
filePath: string;
|
|
6
|
+
lineStart: number;
|
|
7
|
+
lineEnd: number;
|
|
8
|
+
colStart: number;
|
|
9
|
+
colEnd: number;
|
|
10
|
+
parentSymbolId?: number;
|
|
11
|
+
signature?: string;
|
|
12
|
+
isExported: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface ReferenceInfo {
|
|
15
|
+
fromSymbolId: number;
|
|
16
|
+
toSymbolName: string;
|
|
17
|
+
toSymbolBareName: string;
|
|
18
|
+
toFileId?: number;
|
|
19
|
+
kind: string;
|
|
20
|
+
line: number;
|
|
21
|
+
col: number;
|
|
22
|
+
}
|
|
23
|
+
export interface ImportInfo {
|
|
24
|
+
sourcePath: string;
|
|
25
|
+
importedName: string;
|
|
26
|
+
localName: string;
|
|
27
|
+
isDefault: boolean;
|
|
28
|
+
isNamespace: boolean;
|
|
29
|
+
line: number;
|
|
30
|
+
}
|
|
31
|
+
export declare class CodeGraph {
|
|
32
|
+
private db;
|
|
33
|
+
private stmts;
|
|
34
|
+
private hashCache;
|
|
35
|
+
constructor(db: Database.Database);
|
|
36
|
+
private prepareStatements;
|
|
37
|
+
getDb(): Database.Database;
|
|
38
|
+
fileHash(content: string): string;
|
|
39
|
+
getFileId(filePath: string): number | undefined;
|
|
40
|
+
getFileHash(filePath: string): string | undefined;
|
|
41
|
+
upsertFile(filePath: string, hash: string): number;
|
|
42
|
+
clearFileData(fileId: number): void;
|
|
43
|
+
insertSymbol(fileId: number, symbol: SymbolInfo): number;
|
|
44
|
+
insertReference(ref: ReferenceInfo): void;
|
|
45
|
+
insertImport(fileId: number, imp: ImportInfo): void;
|
|
46
|
+
/**
|
|
47
|
+
* Index a single file. Wraps in a transaction.
|
|
48
|
+
* For batch indexing many files, use indexFileBatch() instead.
|
|
49
|
+
*/
|
|
50
|
+
indexFile(filePath: string, content: string, symbols: SymbolInfo[], references: ReferenceInfo[], imports: ImportInfo[]): void;
|
|
51
|
+
/**
|
|
52
|
+
* Batch-index many files in a single transaction.
|
|
53
|
+
* Much faster for initial indexing of large repos.
|
|
54
|
+
*/
|
|
55
|
+
indexFileBatch(files: Array<{
|
|
56
|
+
filePath: string;
|
|
57
|
+
content: string;
|
|
58
|
+
symbols: SymbolInfo[];
|
|
59
|
+
references: ReferenceInfo[];
|
|
60
|
+
imports: ImportInfo[];
|
|
61
|
+
}>): {
|
|
62
|
+
indexed: number;
|
|
63
|
+
skipped: number;
|
|
64
|
+
};
|
|
65
|
+
private _indexFileInner;
|
|
66
|
+
removeFile(filePath: string): void;
|
|
67
|
+
findSymbols(query: {
|
|
68
|
+
name?: string;
|
|
69
|
+
kind?: string;
|
|
70
|
+
scope?: string;
|
|
71
|
+
limit?: number;
|
|
72
|
+
}): Array<{
|
|
73
|
+
name: string;
|
|
74
|
+
kind: string;
|
|
75
|
+
filePath: string;
|
|
76
|
+
lineStart: number;
|
|
77
|
+
lineEnd: number;
|
|
78
|
+
colStart: number;
|
|
79
|
+
colEnd: number;
|
|
80
|
+
signature: string | null;
|
|
81
|
+
isExported: boolean;
|
|
82
|
+
}>;
|
|
83
|
+
getReferences(symbolName: string, depth?: number): Array<{
|
|
84
|
+
fromSymbol: string;
|
|
85
|
+
fromKind: string;
|
|
86
|
+
fromFile: string;
|
|
87
|
+
fromLine: number;
|
|
88
|
+
toSymbol: string;
|
|
89
|
+
refKind: string;
|
|
90
|
+
refLine: number;
|
|
91
|
+
refCol: number;
|
|
92
|
+
depth: number;
|
|
93
|
+
}>;
|
|
94
|
+
getExports(filePath: string): Array<{
|
|
95
|
+
name: string;
|
|
96
|
+
kind: string;
|
|
97
|
+
lineStart: number;
|
|
98
|
+
signature: string | null;
|
|
99
|
+
}>;
|
|
100
|
+
getImports(filePath: string): Array<{
|
|
101
|
+
sourcePath: string;
|
|
102
|
+
importedName: string;
|
|
103
|
+
localName: string;
|
|
104
|
+
isDefault: boolean;
|
|
105
|
+
isNamespace: boolean;
|
|
106
|
+
line: number;
|
|
107
|
+
}>;
|
|
108
|
+
getDependencyGraph(filePath: string, depth?: number): Array<{
|
|
109
|
+
file: string;
|
|
110
|
+
imports: string;
|
|
111
|
+
depth: number;
|
|
112
|
+
}>;
|
|
113
|
+
getStats(): {
|
|
114
|
+
files: number;
|
|
115
|
+
symbols: number;
|
|
116
|
+
references: number;
|
|
117
|
+
imports: number;
|
|
118
|
+
};
|
|
119
|
+
}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { createHash } from "crypto";
|
|
2
|
+
import { logger } from "../utils/logger.js";
|
|
3
|
+
export class CodeGraph {
|
|
4
|
+
db;
|
|
5
|
+
// Prepared statement cache — avoids re-preparing on every call
|
|
6
|
+
stmts;
|
|
7
|
+
// In-memory hash cache — avoids DB lookup on unchanged files
|
|
8
|
+
hashCache = new Map();
|
|
9
|
+
constructor(db) {
|
|
10
|
+
this.db = db;
|
|
11
|
+
this.stmts = this.prepareStatements();
|
|
12
|
+
}
|
|
13
|
+
prepareStatements() {
|
|
14
|
+
return {
|
|
15
|
+
getFileId: this.db.prepare("SELECT id FROM files WHERE path = ?"),
|
|
16
|
+
getFileHash: this.db.prepare("SELECT hash FROM files WHERE path = ?"),
|
|
17
|
+
updateFile: this.db.prepare("UPDATE files SET hash = ?, indexed_at = datetime('now') WHERE id = ?"),
|
|
18
|
+
insertFile: this.db.prepare("INSERT INTO files (path, hash) VALUES (?, ?)"),
|
|
19
|
+
deleteSymbols: this.db.prepare("DELETE FROM symbols WHERE file_id = ?"),
|
|
20
|
+
deleteImports: this.db.prepare("DELETE FROM imports WHERE file_id = ?"),
|
|
21
|
+
insertSymbol: this.db.prepare(`INSERT INTO symbols (name, kind, file_id, line_start, line_end, col_start, col_end, parent_symbol_id, signature, is_exported)
|
|
22
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
|
|
23
|
+
updateParent: this.db.prepare("UPDATE symbols SET parent_symbol_id = ? WHERE id = ?"),
|
|
24
|
+
insertRef: this.db.prepare(`INSERT INTO references_ (from_symbol_id, to_symbol_name, to_symbol_bare_name, to_file_id, kind, line, col)
|
|
25
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`),
|
|
26
|
+
insertImport: this.db.prepare(`INSERT INTO imports (file_id, source_path, imported_name, local_name, is_default, is_namespace, line)
|
|
27
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`),
|
|
28
|
+
deleteFile: this.db.prepare("DELETE FROM files WHERE id = ?"),
|
|
29
|
+
countFiles: this.db.prepare("SELECT COUNT(*) as c FROM files"),
|
|
30
|
+
countSymbols: this.db.prepare("SELECT COUNT(*) as c FROM symbols"),
|
|
31
|
+
countRefs: this.db.prepare("SELECT COUNT(*) as c FROM references_"),
|
|
32
|
+
countImports: this.db.prepare("SELECT COUNT(*) as c FROM imports"),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
getDb() {
|
|
36
|
+
return this.db;
|
|
37
|
+
}
|
|
38
|
+
fileHash(content) {
|
|
39
|
+
return createHash("sha256").update(content).digest("hex");
|
|
40
|
+
}
|
|
41
|
+
getFileId(filePath) {
|
|
42
|
+
const row = this.stmts.getFileId.get(filePath);
|
|
43
|
+
return row?.id;
|
|
44
|
+
}
|
|
45
|
+
getFileHash(filePath) {
|
|
46
|
+
// Check in-memory cache first
|
|
47
|
+
const cached = this.hashCache.get(filePath);
|
|
48
|
+
if (cached)
|
|
49
|
+
return cached;
|
|
50
|
+
const row = this.stmts.getFileHash.get(filePath);
|
|
51
|
+
if (row)
|
|
52
|
+
this.hashCache.set(filePath, row.hash);
|
|
53
|
+
return row?.hash;
|
|
54
|
+
}
|
|
55
|
+
upsertFile(filePath, hash) {
|
|
56
|
+
const existing = this.getFileId(filePath);
|
|
57
|
+
if (existing) {
|
|
58
|
+
this.stmts.updateFile.run(hash, existing);
|
|
59
|
+
this.hashCache.set(filePath, hash);
|
|
60
|
+
return existing;
|
|
61
|
+
}
|
|
62
|
+
const result = this.stmts.insertFile.run(filePath, hash);
|
|
63
|
+
this.hashCache.set(filePath, hash);
|
|
64
|
+
return result.lastInsertRowid;
|
|
65
|
+
}
|
|
66
|
+
clearFileData(fileId) {
|
|
67
|
+
this.stmts.deleteSymbols.run(fileId);
|
|
68
|
+
this.stmts.deleteImports.run(fileId);
|
|
69
|
+
}
|
|
70
|
+
insertSymbol(fileId, symbol) {
|
|
71
|
+
const result = this.stmts.insertSymbol.run(symbol.name, symbol.kind, fileId, symbol.lineStart, symbol.lineEnd, symbol.colStart, symbol.colEnd, symbol.parentSymbolId ?? null, symbol.signature ?? null, symbol.isExported ? 1 : 0);
|
|
72
|
+
return result.lastInsertRowid;
|
|
73
|
+
}
|
|
74
|
+
insertReference(ref) {
|
|
75
|
+
this.stmts.insertRef.run(ref.fromSymbolId, ref.toSymbolName, ref.toSymbolBareName, ref.toFileId ?? null, ref.kind, ref.line, ref.col);
|
|
76
|
+
}
|
|
77
|
+
insertImport(fileId, imp) {
|
|
78
|
+
this.stmts.insertImport.run(fileId, imp.sourcePath, imp.importedName, imp.localName, imp.isDefault ? 1 : 0, imp.isNamespace ? 1 : 0, imp.line);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Index a single file. Wraps in a transaction.
|
|
82
|
+
* For batch indexing many files, use indexFileBatch() instead.
|
|
83
|
+
*/
|
|
84
|
+
indexFile(filePath, content, symbols, references, imports) {
|
|
85
|
+
const hash = this.fileHash(content);
|
|
86
|
+
const existingHash = this.getFileHash(filePath);
|
|
87
|
+
if (existingHash === hash) {
|
|
88
|
+
logger.debug(`Skipping unchanged file: ${filePath}`);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const tx = this.db.transaction(() => {
|
|
92
|
+
this._indexFileInner(filePath, hash, symbols, references, imports);
|
|
93
|
+
});
|
|
94
|
+
tx();
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Batch-index many files in a single transaction.
|
|
98
|
+
* Much faster for initial indexing of large repos.
|
|
99
|
+
*/
|
|
100
|
+
indexFileBatch(files) {
|
|
101
|
+
let indexed = 0;
|
|
102
|
+
let skipped = 0;
|
|
103
|
+
const tx = this.db.transaction(() => {
|
|
104
|
+
for (const file of files) {
|
|
105
|
+
const hash = this.fileHash(file.content);
|
|
106
|
+
const existingHash = this.getFileHash(file.filePath);
|
|
107
|
+
if (existingHash === hash) {
|
|
108
|
+
skipped++;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
this._indexFileInner(file.filePath, hash, file.symbols, file.references, file.imports);
|
|
112
|
+
indexed++;
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
tx();
|
|
116
|
+
return { indexed, skipped };
|
|
117
|
+
}
|
|
118
|
+
_indexFileInner(filePath, hash, symbols, references, imports) {
|
|
119
|
+
const fileId = this.upsertFile(filePath, hash);
|
|
120
|
+
this.clearFileData(fileId);
|
|
121
|
+
// First pass: insert all symbols without parent references
|
|
122
|
+
const symbolIdMap = new Map();
|
|
123
|
+
for (let i = 0; i < symbols.length; i++) {
|
|
124
|
+
const sym = symbols[i];
|
|
125
|
+
const savedParent = sym.parentSymbolId;
|
|
126
|
+
sym.parentSymbolId = undefined;
|
|
127
|
+
const id = this.insertSymbol(fileId, sym);
|
|
128
|
+
symbolIdMap.set(i, id);
|
|
129
|
+
sym.parentSymbolId = savedParent;
|
|
130
|
+
}
|
|
131
|
+
// Second pass: update parent references now that all symbols have real IDs
|
|
132
|
+
for (let i = 0; i < symbols.length; i++) {
|
|
133
|
+
const sym = symbols[i];
|
|
134
|
+
if (sym.parentSymbolId !== undefined) {
|
|
135
|
+
const realParentId = symbolIdMap.get(sym.parentSymbolId);
|
|
136
|
+
const realId = symbolIdMap.get(i);
|
|
137
|
+
if (realParentId !== undefined && realId !== undefined) {
|
|
138
|
+
this.stmts.updateParent.run(realParentId, realId);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
for (const ref of references) {
|
|
143
|
+
const mappedFromId = symbolIdMap.get(ref.fromSymbolId) ?? ref.fromSymbolId;
|
|
144
|
+
this.insertReference({ ...ref, fromSymbolId: mappedFromId });
|
|
145
|
+
}
|
|
146
|
+
for (const imp of imports) {
|
|
147
|
+
this.insertImport(fileId, imp);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
removeFile(filePath) {
|
|
151
|
+
const fileId = this.getFileId(filePath);
|
|
152
|
+
if (fileId) {
|
|
153
|
+
this.stmts.deleteFile.run(fileId);
|
|
154
|
+
this.hashCache.delete(filePath);
|
|
155
|
+
logger.info(`Removed file from index: ${filePath}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// --- Query methods ---
|
|
159
|
+
findSymbols(query) {
|
|
160
|
+
const conditions = [];
|
|
161
|
+
const params = [];
|
|
162
|
+
if (query.name) {
|
|
163
|
+
conditions.push("s.name LIKE ?");
|
|
164
|
+
params.push(`%${query.name}%`);
|
|
165
|
+
}
|
|
166
|
+
if (query.kind) {
|
|
167
|
+
conditions.push("s.kind = ?");
|
|
168
|
+
params.push(query.kind);
|
|
169
|
+
}
|
|
170
|
+
if (query.scope) {
|
|
171
|
+
conditions.push("f.path LIKE ?");
|
|
172
|
+
params.push(`${query.scope}%`);
|
|
173
|
+
}
|
|
174
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
175
|
+
const limit = query.limit ?? 50;
|
|
176
|
+
const sql = `
|
|
177
|
+
SELECT s.name, s.kind, f.path as filePath, s.line_start as lineStart, s.line_end as lineEnd,
|
|
178
|
+
s.col_start as colStart, s.col_end as colEnd, s.signature, s.is_exported as isExported
|
|
179
|
+
FROM symbols s
|
|
180
|
+
JOIN files f ON s.file_id = f.id
|
|
181
|
+
${where}
|
|
182
|
+
ORDER BY
|
|
183
|
+
CASE WHEN s.name = ? THEN 0 ELSE 1 END,
|
|
184
|
+
s.is_exported DESC,
|
|
185
|
+
f.path
|
|
186
|
+
LIMIT ?
|
|
187
|
+
`;
|
|
188
|
+
params.push(query.name ?? "", limit);
|
|
189
|
+
return this.db.prepare(sql).all(...params);
|
|
190
|
+
}
|
|
191
|
+
getReferences(symbolName, depth = 1) {
|
|
192
|
+
if (depth < 1)
|
|
193
|
+
depth = 1;
|
|
194
|
+
if (depth > 10)
|
|
195
|
+
depth = 10;
|
|
196
|
+
const sql = `
|
|
197
|
+
WITH RECURSIVE ref_chain(from_symbol_name, from_kind, from_file, from_line, to_name, ref_kind, ref_line, ref_col, depth) AS (
|
|
198
|
+
-- Base case: direct references to the target symbol
|
|
199
|
+
-- Match against both full qualified name and bare name
|
|
200
|
+
SELECT s.name, s.kind, f.path, s.line_start, r.to_symbol_name, r.kind, r.line, r.col, 1
|
|
201
|
+
FROM references_ r
|
|
202
|
+
JOIN symbols s ON r.from_symbol_id = s.id
|
|
203
|
+
JOIN files f ON s.file_id = f.id
|
|
204
|
+
WHERE r.to_symbol_name = ?
|
|
205
|
+
OR r.to_symbol_bare_name = ?
|
|
206
|
+
OR r.to_symbol_name LIKE (? || '.%')
|
|
207
|
+
|
|
208
|
+
UNION ALL
|
|
209
|
+
|
|
210
|
+
-- Recursive case: who references the symbols that reference our target
|
|
211
|
+
SELECT s.name, s.kind, f.path, s.line_start, r.to_symbol_name, r.kind, r.line, r.col, rc.depth + 1
|
|
212
|
+
FROM references_ r
|
|
213
|
+
JOIN symbols s ON r.from_symbol_id = s.id
|
|
214
|
+
JOIN files f ON s.file_id = f.id
|
|
215
|
+
JOIN ref_chain rc ON r.to_symbol_name = rc.from_symbol_name
|
|
216
|
+
OR r.to_symbol_bare_name = rc.from_symbol_name
|
|
217
|
+
OR r.to_symbol_name LIKE (rc.from_symbol_name || '.%')
|
|
218
|
+
WHERE rc.depth < ?
|
|
219
|
+
)
|
|
220
|
+
SELECT from_symbol_name as fromSymbol, from_kind as fromKind, from_file as fromFile,
|
|
221
|
+
from_line as fromLine, to_name as toSymbol, ref_kind as refKind,
|
|
222
|
+
ref_line as refLine, ref_col as refCol, depth
|
|
223
|
+
FROM ref_chain
|
|
224
|
+
ORDER BY depth, from_file, ref_line
|
|
225
|
+
LIMIT 200
|
|
226
|
+
`;
|
|
227
|
+
return this.db.prepare(sql).all(symbolName, symbolName, symbolName, depth);
|
|
228
|
+
}
|
|
229
|
+
getExports(filePath) {
|
|
230
|
+
return this.db
|
|
231
|
+
.prepare(`SELECT s.name, s.kind, s.line_start as lineStart, s.signature
|
|
232
|
+
FROM symbols s
|
|
233
|
+
JOIN files f ON s.file_id = f.id
|
|
234
|
+
WHERE f.path = ? AND s.is_exported = 1
|
|
235
|
+
ORDER BY s.line_start`)
|
|
236
|
+
.all(filePath);
|
|
237
|
+
}
|
|
238
|
+
getImports(filePath) {
|
|
239
|
+
return this.db
|
|
240
|
+
.prepare(`SELECT source_path as sourcePath, imported_name as importedName, local_name as localName,
|
|
241
|
+
is_default as isDefault, is_namespace as isNamespace, line
|
|
242
|
+
FROM imports
|
|
243
|
+
JOIN files f ON imports.file_id = f.id
|
|
244
|
+
WHERE f.path = ?
|
|
245
|
+
ORDER BY line`)
|
|
246
|
+
.all(filePath);
|
|
247
|
+
}
|
|
248
|
+
getDependencyGraph(filePath, depth = 2) {
|
|
249
|
+
const sql = `
|
|
250
|
+
WITH RECURSIVE dep_tree(file_path, imports_from, depth) AS (
|
|
251
|
+
SELECT f.path, i.source_path, 1
|
|
252
|
+
FROM imports i
|
|
253
|
+
JOIN files f ON i.file_id = f.id
|
|
254
|
+
WHERE f.path = ?
|
|
255
|
+
|
|
256
|
+
UNION ALL
|
|
257
|
+
|
|
258
|
+
SELECT f.path, i.source_path, dt.depth + 1
|
|
259
|
+
FROM imports i
|
|
260
|
+
JOIN files f ON i.file_id = f.id
|
|
261
|
+
JOIN dep_tree dt ON f.path LIKE ('%' || dt.imports_from || '%')
|
|
262
|
+
WHERE dt.depth < ?
|
|
263
|
+
)
|
|
264
|
+
SELECT file_path as file, imports_from as imports, depth
|
|
265
|
+
FROM dep_tree
|
|
266
|
+
ORDER BY depth, file_path
|
|
267
|
+
LIMIT 100
|
|
268
|
+
`;
|
|
269
|
+
return this.db.prepare(sql).all(filePath, depth);
|
|
270
|
+
}
|
|
271
|
+
getStats() {
|
|
272
|
+
const files = this.stmts.countFiles.get().c;
|
|
273
|
+
const symbols = this.stmts.countSymbols.get().c;
|
|
274
|
+
const references = this.stmts.countRefs.get().c;
|
|
275
|
+
const imports = this.stmts.countImports.get().c;
|
|
276
|
+
return { files, symbols, references, imports };
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
//# sourceMappingURL=code-graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-graph.js","sourceRoot":"","sources":["../../src/graph/code-graph.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
export function initializeDatabase(dbPath) {
|
|
3
|
+
const db = new Database(dbPath);
|
|
4
|
+
db.pragma("journal_mode = WAL");
|
|
5
|
+
db.pragma("foreign_keys = ON");
|
|
6
|
+
db.exec(`
|
|
7
|
+
CREATE TABLE IF NOT EXISTS files (
|
|
8
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
9
|
+
path TEXT NOT NULL UNIQUE,
|
|
10
|
+
hash TEXT NOT NULL,
|
|
11
|
+
indexed_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
CREATE TABLE IF NOT EXISTS symbols (
|
|
15
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
16
|
+
name TEXT NOT NULL,
|
|
17
|
+
kind TEXT NOT NULL,
|
|
18
|
+
file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE,
|
|
19
|
+
line_start INTEGER NOT NULL,
|
|
20
|
+
line_end INTEGER NOT NULL,
|
|
21
|
+
col_start INTEGER NOT NULL,
|
|
22
|
+
col_end INTEGER NOT NULL,
|
|
23
|
+
parent_symbol_id INTEGER REFERENCES symbols(id) ON DELETE SET NULL,
|
|
24
|
+
signature TEXT,
|
|
25
|
+
is_exported INTEGER NOT NULL DEFAULT 0,
|
|
26
|
+
UNIQUE(name, kind, file_id, line_start)
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
CREATE TABLE IF NOT EXISTS references_ (
|
|
30
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
31
|
+
from_symbol_id INTEGER NOT NULL REFERENCES symbols(id) ON DELETE CASCADE,
|
|
32
|
+
to_symbol_name TEXT NOT NULL,
|
|
33
|
+
to_symbol_bare_name TEXT NOT NULL,
|
|
34
|
+
to_file_id INTEGER,
|
|
35
|
+
kind TEXT NOT NULL,
|
|
36
|
+
line INTEGER NOT NULL,
|
|
37
|
+
col INTEGER NOT NULL
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
CREATE TABLE IF NOT EXISTS imports (
|
|
41
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
42
|
+
file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE,
|
|
43
|
+
source_path TEXT NOT NULL,
|
|
44
|
+
imported_name TEXT NOT NULL,
|
|
45
|
+
local_name TEXT NOT NULL,
|
|
46
|
+
is_default INTEGER NOT NULL DEFAULT 0,
|
|
47
|
+
is_namespace INTEGER NOT NULL DEFAULT 0,
|
|
48
|
+
line INTEGER NOT NULL
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
CREATE INDEX IF NOT EXISTS idx_symbols_name ON symbols(name);
|
|
52
|
+
CREATE INDEX IF NOT EXISTS idx_symbols_kind ON symbols(kind);
|
|
53
|
+
CREATE INDEX IF NOT EXISTS idx_symbols_file ON symbols(file_id);
|
|
54
|
+
CREATE INDEX IF NOT EXISTS idx_refs_from ON references_(from_symbol_id);
|
|
55
|
+
CREATE INDEX IF NOT EXISTS idx_refs_to_name ON references_(to_symbol_name);
|
|
56
|
+
CREATE INDEX IF NOT EXISTS idx_refs_to_bare ON references_(to_symbol_bare_name);
|
|
57
|
+
CREATE INDEX IF NOT EXISTS idx_imports_file ON imports(file_id);
|
|
58
|
+
CREATE INDEX IF NOT EXISTS idx_imports_source ON imports(source_path);
|
|
59
|
+
`);
|
|
60
|
+
return db;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/graph/schema.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAEtC,MAAM,UAAU,kBAAkB,CAAC,MAAc;IAC/C,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;IAEhC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAE/B,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDP,CAAC,CAAC;IAEH,OAAO,EAAE,CAAC;AACZ,CAAC"}
|