@esankhan3/code-search-mcp 0.0.2 → 0.2.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/README.md +343 -248
- package/dist/__tests__/config-resolver.test.js +153 -0
- package/dist/__tests__/daemon-rpc.test.js +100 -0
- package/dist/__tests__/observability.test.js +69 -0
- package/dist/__tests__/projects.test.js +141 -0
- package/dist/backends/daemon-client.js +87 -0
- package/dist/backends/in-process.js +104 -0
- package/dist/backends/index.js +29 -0
- package/dist/cli/index.js +231 -0
- package/dist/core/config.js +362 -49
- package/dist/core/env-config.js +45 -43
- package/dist/daemon/index.js +119 -0
- package/dist/daemon/rpc-server.js +99 -0
- package/dist/daemon/watcher.js +105 -0
- package/dist/index.js +7 -0
- package/dist/observability/logger.js +66 -0
- package/dist/observability/metrics.js +138 -0
- package/dist/projects/registry.js +146 -0
- package/dist/resources/resources.js +3 -3
- package/dist/server.js +6 -2
- package/dist/tools/graph.js +1 -1
- package/dist/tools/index-tools.js +1 -1
- package/dist/tools/profile.js +2 -2
- package/dist/tools/search.js +1 -1
- package/dist/transports/http-transport.js +38 -0
- package/package.json +31 -12
- package/scripts/smoke/run-smoke.sh +167 -0
- package/dist/core/ast-graph-builder.js +0 -800
- package/dist/core/chunker.js +0 -368
- package/dist/core/claude-runner.js +0 -173
- package/dist/core/cross-repo-detector.js +0 -887
- package/dist/core/embedder.js +0 -334
- package/dist/core/file-walker.js +0 -186
- package/dist/core/git-diff.js +0 -160
- package/dist/core/graph-metrics.js +0 -233
- package/dist/core/indexer.js +0 -552
- package/dist/core/project-graph-builder.js +0 -202
- package/dist/core/query-classifier.js +0 -144
- package/dist/core/query-router.js +0 -210
- package/dist/core/rag-evaluator.js +0 -213
- package/dist/core/repo-profiler.js +0 -383
- package/dist/core/reranker.js +0 -218
- package/dist/core/retriever.js +0 -242
- package/dist/core/semantic-edge-detector.js +0 -147
- package/dist/core/service-mesh-inferrer.js +0 -241
- package/dist/core/structural-hasher.js +0 -316
- package/dist/core/tree-sitter-parser.js +0 -539
- package/dist/core/vector-store.js +0 -259
- package/dist/core/workspace-detector.js +0 -481
- /package/dist/{core → backends}/types.js +0 -0
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
class VectorStore {
|
|
2
|
-
db;
|
|
3
|
-
// lancedb.Connection
|
|
4
|
-
table;
|
|
5
|
-
// lancedb.Table
|
|
6
|
-
dbPath;
|
|
7
|
-
initialized = false;
|
|
8
|
-
constructor(dbPath) {
|
|
9
|
-
this.dbPath = dbPath;
|
|
10
|
-
}
|
|
11
|
-
/** Initialize connection, create or open table */
|
|
12
|
-
async init() {
|
|
13
|
-
let lancedb;
|
|
14
|
-
try {
|
|
15
|
-
lancedb = await import("@lancedb/lancedb");
|
|
16
|
-
} catch {
|
|
17
|
-
throw new Error(
|
|
18
|
-
"@lancedb/lancedb is not installed. Install it with: npm install @lancedb/lancedb"
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
this.db = await lancedb.connect(this.dbPath);
|
|
22
|
-
try {
|
|
23
|
-
this.table = await this.db.openTable("chunks");
|
|
24
|
-
await this.ensureFtsIndex();
|
|
25
|
-
this.initialized = true;
|
|
26
|
-
} catch {
|
|
27
|
-
this.initialized = true;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
/** Create or rebuild the full-text search index on contextualizedContent */
|
|
31
|
-
async ensureFtsIndex() {
|
|
32
|
-
if (!this.table) return;
|
|
33
|
-
try {
|
|
34
|
-
const lancedb = await import("@lancedb/lancedb");
|
|
35
|
-
await this.table.createIndex("contextualizedContent", {
|
|
36
|
-
config: lancedb.Index.fts(),
|
|
37
|
-
replace: true
|
|
38
|
-
});
|
|
39
|
-
} catch {
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
/** Insert or replace chunks with embeddings */
|
|
43
|
-
async upsertChunks(chunks) {
|
|
44
|
-
const rows = chunks.map((c) => ({
|
|
45
|
-
id: c.id,
|
|
46
|
-
vector: c.embedding,
|
|
47
|
-
// LanceDB uses 'vector' field for embeddings
|
|
48
|
-
content: c.content,
|
|
49
|
-
contextualizedContent: c.contextualizedContent,
|
|
50
|
-
contextPrefix: c.contextPrefix,
|
|
51
|
-
filePath: c.filePath,
|
|
52
|
-
repoName: c.repoName,
|
|
53
|
-
project: c.project,
|
|
54
|
-
entityType: c.entityType,
|
|
55
|
-
entityName: c.entityName ?? "",
|
|
56
|
-
parentEntity: c.parentEntity ?? "",
|
|
57
|
-
language: c.language,
|
|
58
|
-
startLine: c.startLine,
|
|
59
|
-
endLine: c.endLine,
|
|
60
|
-
tokens: c.tokens
|
|
61
|
-
}));
|
|
62
|
-
if (!this.table) {
|
|
63
|
-
this.table = await this.db.createTable("chunks", rows, { mode: "overwrite" });
|
|
64
|
-
} else {
|
|
65
|
-
const project = chunks[0]?.project;
|
|
66
|
-
if (project) {
|
|
67
|
-
try {
|
|
68
|
-
await this.table.delete(`project = '${project.replace(/'/g, "''")}'`);
|
|
69
|
-
} catch {
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
await this.table.add(rows);
|
|
73
|
-
}
|
|
74
|
-
await this.ensureFtsIndex();
|
|
75
|
-
}
|
|
76
|
-
/** Semantic vector search */
|
|
77
|
-
async vectorSearch(queryEmbedding, opts) {
|
|
78
|
-
if (!this.table) return [];
|
|
79
|
-
let query = this.table.search(queryEmbedding).limit(opts?.limit ?? 20);
|
|
80
|
-
if (opts?.filter) query = query.where(opts.filter);
|
|
81
|
-
const results = await query.toArray();
|
|
82
|
-
return results.map((r) => ({
|
|
83
|
-
chunk: rowToChunk(r),
|
|
84
|
-
score: r._distance != null ? 1 / (1 + r._distance) : 0.5,
|
|
85
|
-
source: "vector"
|
|
86
|
-
}));
|
|
87
|
-
}
|
|
88
|
-
/** Full-text BM25 search (LanceDB built-in FTS) */
|
|
89
|
-
async fullTextSearch(queryText, limit = 20) {
|
|
90
|
-
if (!this.table) return [];
|
|
91
|
-
try {
|
|
92
|
-
const results = await this.table.search(queryText, "fts", "contextualizedContent").limit(limit).toArray();
|
|
93
|
-
return results.map((r) => ({
|
|
94
|
-
chunk: rowToChunk(r),
|
|
95
|
-
score: r._relevance_score ?? 0.5,
|
|
96
|
-
source: "bm25"
|
|
97
|
-
}));
|
|
98
|
-
} catch {
|
|
99
|
-
return [];
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
/** Get specific chunks by their IDs */
|
|
103
|
-
async getByIds(ids) {
|
|
104
|
-
if (!this.table || ids.length === 0) return [];
|
|
105
|
-
const filter = ids.map((id) => `id = '${id}'`).join(" OR ");
|
|
106
|
-
try {
|
|
107
|
-
const results = await this.table.filter(filter).toArray();
|
|
108
|
-
return results.map((r) => rowToChunk(r));
|
|
109
|
-
} catch {
|
|
110
|
-
return [];
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
/** Look up chunks by repo + file + entity name — for direct AST graph expansion.
|
|
114
|
-
* Returns ScoredChunks with a fixed graph-expansion score. */
|
|
115
|
-
async getChunksByEntity(lookups) {
|
|
116
|
-
if (!this.table || lookups.length === 0) return [];
|
|
117
|
-
const esc = (s) => s.replace(/'/g, "''");
|
|
118
|
-
const conditions = lookups.map((l) => {
|
|
119
|
-
const base = `repoName = '${esc(l.repoName)}' AND filePath = '${esc(l.filePath)}'`;
|
|
120
|
-
return l.entityName ? `(${base} AND entityName = '${esc(l.entityName)}')` : `(${base})`;
|
|
121
|
-
});
|
|
122
|
-
try {
|
|
123
|
-
const allResults = [];
|
|
124
|
-
const batchSize = 20;
|
|
125
|
-
for (let i = 0; i < conditions.length; i += batchSize) {
|
|
126
|
-
const batch = conditions.slice(i, i + batchSize).join(" OR ");
|
|
127
|
-
const results = await this.table.filter(batch).limit(batchSize * 2).toArray();
|
|
128
|
-
for (const r of results) {
|
|
129
|
-
allResults.push({
|
|
130
|
-
chunk: rowToChunk(r),
|
|
131
|
-
score: 0.75,
|
|
132
|
-
source: "graph"
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
return allResults;
|
|
137
|
-
} catch {
|
|
138
|
-
return [];
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
/** Get all chunks for a specific file in a repo (for incremental comparison) */
|
|
142
|
-
async getChunksByFile(repoName, filePath) {
|
|
143
|
-
if (!this.table) return [];
|
|
144
|
-
const esc = (s) => s.replace(/'/g, "''");
|
|
145
|
-
try {
|
|
146
|
-
const results = await this.table.filter(`repoName = '${esc(repoName)}' AND filePath = '${esc(filePath)}'`).toArray();
|
|
147
|
-
return results.map((r) => ({
|
|
148
|
-
id: r.id,
|
|
149
|
-
content: r.content,
|
|
150
|
-
entityName: r.entityName || "",
|
|
151
|
-
tokens: r.tokens || 0
|
|
152
|
-
}));
|
|
153
|
-
} catch {
|
|
154
|
-
return [];
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
/** Delete specific chunks by their IDs (for surgical updates) */
|
|
158
|
-
async deleteChunksByIds(ids) {
|
|
159
|
-
if (!this.table || ids.length === 0) return;
|
|
160
|
-
const batchSize = 50;
|
|
161
|
-
for (let i = 0; i < ids.length; i += batchSize) {
|
|
162
|
-
const batch = ids.slice(i, i + batchSize);
|
|
163
|
-
const filter = batch.map((id) => `id = '${id.replace(/'/g, "''")}'`).join(" OR ");
|
|
164
|
-
try {
|
|
165
|
-
await this.table.delete(filter);
|
|
166
|
-
} catch {
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
/** Get all chunk IDs for a project (for incremental diff) */
|
|
171
|
-
async getChunkIds(project) {
|
|
172
|
-
if (!this.table) return [];
|
|
173
|
-
try {
|
|
174
|
-
const escapedProject = project.replace(/'/g, "''");
|
|
175
|
-
const results = await this.table.query().where(`project = '${escapedProject}'`).select(["id"]).toArray();
|
|
176
|
-
return results.map((r) => r.id);
|
|
177
|
-
} catch {
|
|
178
|
-
return [];
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
/** Get index statistics */
|
|
182
|
-
async getStats() {
|
|
183
|
-
if (!this.table) return null;
|
|
184
|
-
try {
|
|
185
|
-
const count = await this.table.countRows();
|
|
186
|
-
return { rowCount: count };
|
|
187
|
-
} catch {
|
|
188
|
-
return null;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
/** Check if the store has data */
|
|
192
|
-
async hasData() {
|
|
193
|
-
const stats = await this.getStats();
|
|
194
|
-
return (stats?.rowCount ?? 0) > 0;
|
|
195
|
-
}
|
|
196
|
-
/** Delete chunks for specific files within a repo (for incremental re-indexing) */
|
|
197
|
-
async deleteFileChunks(project, repoName, filePaths) {
|
|
198
|
-
if (!this.table || filePaths.length === 0) return;
|
|
199
|
-
const escapedProject = project.replace(/'/g, "''");
|
|
200
|
-
const escapedRepo = repoName.replace(/'/g, "''");
|
|
201
|
-
const fileFilter = filePaths.map((f) => `'${f.replace(/'/g, "''")}'`).join(", ");
|
|
202
|
-
try {
|
|
203
|
-
await this.table.delete(
|
|
204
|
-
`project = '${escapedProject}' AND repoName = '${escapedRepo}' AND filePath IN (${fileFilter})`
|
|
205
|
-
);
|
|
206
|
-
} catch {
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
/** Add chunks without deleting existing project data (for incremental updates) */
|
|
210
|
-
async addChunks(chunks) {
|
|
211
|
-
if (chunks.length === 0) return;
|
|
212
|
-
const rows = chunks.map((c) => ({
|
|
213
|
-
id: c.id,
|
|
214
|
-
vector: c.embedding,
|
|
215
|
-
content: c.content,
|
|
216
|
-
contextualizedContent: c.contextualizedContent,
|
|
217
|
-
contextPrefix: c.contextPrefix,
|
|
218
|
-
filePath: c.filePath,
|
|
219
|
-
repoName: c.repoName,
|
|
220
|
-
project: c.project,
|
|
221
|
-
entityType: c.entityType,
|
|
222
|
-
entityName: c.entityName ?? "",
|
|
223
|
-
parentEntity: c.parentEntity ?? "",
|
|
224
|
-
language: c.language,
|
|
225
|
-
startLine: c.startLine,
|
|
226
|
-
endLine: c.endLine,
|
|
227
|
-
tokens: c.tokens
|
|
228
|
-
}));
|
|
229
|
-
if (!this.table) {
|
|
230
|
-
this.table = await this.db.createTable("chunks", rows, { mode: "overwrite" });
|
|
231
|
-
} else {
|
|
232
|
-
await this.table.add(rows);
|
|
233
|
-
}
|
|
234
|
-
await this.ensureFtsIndex();
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
function rowToChunk(row) {
|
|
238
|
-
return {
|
|
239
|
-
id: row.id,
|
|
240
|
-
filePath: row.filePath,
|
|
241
|
-
repoName: row.repoName,
|
|
242
|
-
project: row.project,
|
|
243
|
-
startLine: row.startLine,
|
|
244
|
-
endLine: row.endLine,
|
|
245
|
-
content: row.content,
|
|
246
|
-
contextPrefix: row.contextPrefix,
|
|
247
|
-
contextualizedContent: row.contextualizedContent,
|
|
248
|
-
language: row.language,
|
|
249
|
-
entityType: row.entityType,
|
|
250
|
-
entityName: row.entityName || void 0,
|
|
251
|
-
parentEntity: row.parentEntity || void 0,
|
|
252
|
-
tokens: row.tokens,
|
|
253
|
-
imports: [],
|
|
254
|
-
exports: []
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
export {
|
|
258
|
-
VectorStore
|
|
259
|
-
};
|