@mhalder/qdrant-mcp-server 3.3.3 → 3.3.4
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/workflows/ci.yml +0 -2
- package/.github/workflows/claude-code-review.yml +1 -1
- package/CHANGELOG.md +6 -0
- package/README.md +1 -1
- package/biome.json +3 -2
- package/build/code/chunker/tree-sitter-chunker.d.ts.map +1 -1
- package/build/code/chunker/tree-sitter-chunker.js +2 -12
- package/build/code/chunker/tree-sitter-chunker.js.map +1 -1
- package/build/code/indexer.d.ts.map +1 -1
- package/build/code/indexer.js +12 -18
- package/build/code/indexer.js.map +1 -1
- package/build/code/scanner.js +1 -1
- package/build/code/scanner.js.map +1 -1
- package/build/embeddings/cohere.d.ts +1 -1
- package/build/embeddings/cohere.d.ts.map +1 -1
- package/build/embeddings/cohere.js +2 -2
- package/build/embeddings/cohere.js.map +1 -1
- package/build/embeddings/cohere.test.js +1 -5
- package/build/embeddings/cohere.test.js.map +1 -1
- package/build/embeddings/factory.d.ts +1 -1
- package/build/embeddings/factory.d.ts.map +1 -1
- package/build/embeddings/factory.js +7 -9
- package/build/embeddings/factory.js.map +1 -1
- package/build/embeddings/factory.test.js +3 -3
- package/build/embeddings/factory.test.js.map +1 -1
- package/build/embeddings/ollama.d.ts +1 -1
- package/build/embeddings/ollama.d.ts.map +1 -1
- package/build/embeddings/ollama.js +6 -8
- package/build/embeddings/ollama.js.map +1 -1
- package/build/embeddings/ollama.test.js +2 -6
- package/build/embeddings/ollama.test.js.map +1 -1
- package/build/embeddings/openai.d.ts +1 -1
- package/build/embeddings/openai.d.ts.map +1 -1
- package/build/embeddings/openai.js +4 -7
- package/build/embeddings/openai.js.map +1 -1
- package/build/embeddings/openai.test.js +3 -12
- package/build/embeddings/openai.test.js.map +1 -1
- package/build/embeddings/sparse.test.js +12 -2
- package/build/embeddings/sparse.test.js.map +1 -1
- package/build/embeddings/voyage.d.ts +1 -1
- package/build/embeddings/voyage.d.ts.map +1 -1
- package/build/embeddings/voyage.js +2 -3
- package/build/embeddings/voyage.js.map +1 -1
- package/build/embeddings/voyage.test.js +2 -6
- package/build/embeddings/voyage.test.js.map +1 -1
- package/build/git/chunker.d.ts.map +1 -1
- package/build/git/chunker.js +2 -2
- package/build/git/chunker.js.map +1 -1
- package/build/git/chunker.test.js +1 -1
- package/build/git/chunker.test.js.map +1 -1
- package/build/git/extractor.d.ts.map +1 -1
- package/build/git/extractor.integration.test.js +9 -5
- package/build/git/extractor.integration.test.js.map +1 -1
- package/build/git/extractor.js +1 -1
- package/build/git/extractor.js.map +1 -1
- package/build/git/extractor.test.js +2 -2
- package/build/git/extractor.test.js.map +1 -1
- package/build/git/index.d.ts +4 -4
- package/build/git/index.d.ts.map +1 -1
- package/build/git/index.js +3 -3
- package/build/git/index.js.map +1 -1
- package/build/git/indexer.d.ts.map +1 -1
- package/build/git/indexer.js +9 -21
- package/build/git/indexer.js.map +1 -1
- package/build/git/indexer.test.js +4 -8
- package/build/git/indexer.test.js.map +1 -1
- package/build/git/sync/synchronizer.d.ts.map +1 -1
- package/build/git/sync/synchronizer.js.map +1 -1
- package/build/git/sync/synchronizer.test.js +4 -2
- package/build/git/sync/synchronizer.test.js.map +1 -1
- package/build/index.js +5 -9
- package/build/index.js.map +1 -1
- package/build/index.test.js +3 -3
- package/build/index.test.js.map +1 -1
- package/build/logger.d.ts.map +1 -1
- package/build/logger.js +1 -9
- package/build/logger.js.map +1 -1
- package/build/prompts/register.d.ts.map +1 -1
- package/build/prompts/register.js.map +1 -1
- package/build/qdrant/client.d.ts.map +1 -1
- package/build/qdrant/client.js.map +1 -1
- package/build/qdrant/client.test.js +10 -34
- package/build/qdrant/client.test.js.map +1 -1
- package/build/resources/index.d.ts +1 -1
- package/build/resources/index.d.ts.map +1 -1
- package/build/resources/index.js +1 -1
- package/build/resources/index.js.map +1 -1
- package/build/tools/code.d.ts.map +1 -1
- package/build/tools/code.js +3 -9
- package/build/tools/code.js.map +1 -1
- package/build/tools/collection.d.ts.map +1 -1
- package/build/tools/collection.js +1 -3
- package/build/tools/collection.js.map +1 -1
- package/build/tools/document.d.ts.map +1 -1
- package/build/tools/document.js +1 -1
- package/build/tools/document.js.map +1 -1
- package/build/tools/federated.d.ts.map +1 -1
- package/build/tools/federated.js +15 -6
- package/build/tools/federated.js.map +1 -1
- package/build/tools/federated.test.js +18 -22
- package/build/tools/federated.test.js.map +1 -1
- package/build/tools/git-history.d.ts.map +1 -1
- package/build/tools/git-history.js +3 -7
- package/build/tools/git-history.js.map +1 -1
- package/build/tools/index.d.ts.map +1 -1
- package/build/tools/index.js.map +1 -1
- package/build/tools/logging.d.ts.map +1 -1
- package/build/tools/logging.js +1 -3
- package/build/tools/logging.js.map +1 -1
- package/build/tools/logging.test.js +1 -1
- package/build/tools/logging.test.js.map +1 -1
- package/build/tools/schemas.d.ts.map +1 -1
- package/build/tools/schemas.js +17 -64
- package/build/tools/schemas.js.map +1 -1
- package/build/tools/search.d.ts.map +1 -1
- package/build/tools/search.js +1 -1
- package/build/tools/search.js.map +1 -1
- package/commitlint.config.js +12 -23
- package/package.json +1 -1
- package/scripts/verify-providers.js +12 -32
- package/src/code/chunker/tree-sitter-chunker.ts +9 -35
- package/src/code/indexer.ts +45 -107
- package/src/code/scanner.ts +1 -1
- package/src/embeddings/cohere.test.ts +17 -45
- package/src/embeddings/cohere.ts +10 -17
- package/src/embeddings/factory.test.ts +18 -18
- package/src/embeddings/factory.ts +18 -25
- package/src/embeddings/ollama.test.ts +38 -67
- package/src/embeddings/ollama.ts +15 -27
- package/src/embeddings/openai.test.ts +17 -53
- package/src/embeddings/openai.ts +11 -22
- package/src/embeddings/sparse.test.ts +12 -2
- package/src/embeddings/voyage.test.ts +39 -80
- package/src/embeddings/voyage.ts +9 -13
- package/src/git/chunker.test.ts +1 -1
- package/src/git/chunker.ts +6 -22
- package/src/git/extractor.integration.test.ts +12 -16
- package/src/git/extractor.test.ts +21 -35
- package/src/git/extractor.ts +14 -36
- package/src/git/index.ts +9 -10
- package/src/git/indexer.test.ts +29 -57
- package/src/git/indexer.ts +38 -86
- package/src/git/sync/synchronizer.test.ts +6 -9
- package/src/git/sync/synchronizer.ts +2 -5
- package/src/index.test.ts +7 -9
- package/src/index.ts +34 -80
- package/src/logger.ts +3 -14
- package/src/prompts/register.ts +3 -10
- package/src/qdrant/client.test.ts +63 -169
- package/src/qdrant/client.ts +19 -45
- package/src/resources/index.ts +4 -10
- package/src/tools/code.ts +43 -66
- package/src/tools/collection.ts +19 -38
- package/src/tools/document.ts +10 -19
- package/src/tools/federated.test.ts +34 -57
- package/src/tools/federated.ts +88 -108
- package/src/tools/git-history.ts +32 -60
- package/src/tools/index.ts +1 -4
- package/src/tools/logging.test.ts +10 -10
- package/src/tools/logging.ts +8 -18
- package/src/tools/schemas.ts +23 -78
- package/src/tools/search.ts +77 -94
- package/tests/code/chunker/tree-sitter-chunker.test.ts +6 -19
- package/tests/code/indexer.test.ts +100 -192
- package/tests/code/integration.test.ts +61 -117
- package/tests/code/scanner.test.ts +12 -39
- package/tests/code/sync/snapshot.test.ts +4 -14
- package/tests/code/sync/synchronizer.test.ts +10 -40
|
@@ -33,7 +33,7 @@ class MockQdrantManager implements Partial<QdrantManager> {
|
|
|
33
33
|
name: string,
|
|
34
34
|
vectorSize: number,
|
|
35
35
|
distance: "Cosine" | "Euclid" | "Dot" = "Cosine",
|
|
36
|
-
enableHybrid?: boolean
|
|
36
|
+
enableHybrid?: boolean
|
|
37
37
|
): Promise<void> {
|
|
38
38
|
this.collections.set(name, {
|
|
39
39
|
vectorSize,
|
|
@@ -56,10 +56,7 @@ class MockQdrantManager implements Partial<QdrantManager> {
|
|
|
56
56
|
this.points.set(collectionName, [...filtered, ...points]);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
async addPointsWithSparse(
|
|
60
|
-
collectionName: string,
|
|
61
|
-
points: any[],
|
|
62
|
-
): Promise<void> {
|
|
59
|
+
async addPointsWithSparse(collectionName: string, points: any[]): Promise<void> {
|
|
63
60
|
await this.addPoints(collectionName, points);
|
|
64
61
|
}
|
|
65
62
|
|
|
@@ -67,7 +64,7 @@ class MockQdrantManager implements Partial<QdrantManager> {
|
|
|
67
64
|
collectionName: string,
|
|
68
65
|
_vector: number[],
|
|
69
66
|
limit: number,
|
|
70
|
-
filter?: any
|
|
67
|
+
filter?: any
|
|
71
68
|
): Promise<any[]> {
|
|
72
69
|
const points = this.points.get(collectionName) || [];
|
|
73
70
|
let filtered = points;
|
|
@@ -76,9 +73,7 @@ class MockQdrantManager implements Partial<QdrantManager> {
|
|
|
76
73
|
if (filter?.must) {
|
|
77
74
|
for (const condition of filter.must) {
|
|
78
75
|
if (condition.key === "fileExtension") {
|
|
79
|
-
filtered = filtered.filter((p) =>
|
|
80
|
-
condition.match.any.includes(p.payload.fileExtension),
|
|
81
|
-
);
|
|
76
|
+
filtered = filtered.filter((p) => condition.match.any.includes(p.payload.fileExtension));
|
|
82
77
|
}
|
|
83
78
|
}
|
|
84
79
|
}
|
|
@@ -95,7 +90,7 @@ class MockQdrantManager implements Partial<QdrantManager> {
|
|
|
95
90
|
vector: number[],
|
|
96
91
|
_sparseVector: any,
|
|
97
92
|
limit: number,
|
|
98
|
-
filter?: any
|
|
93
|
+
filter?: any
|
|
99
94
|
): Promise<any[]> {
|
|
100
95
|
// Hybrid search returns similar results with slight boost
|
|
101
96
|
const results = await this.search(collectionName, vector, limit, filter);
|
|
@@ -114,7 +109,7 @@ class MockQdrantManager implements Partial<QdrantManager> {
|
|
|
114
109
|
|
|
115
110
|
async getPoint(
|
|
116
111
|
collectionName: string,
|
|
117
|
-
id: string | number
|
|
112
|
+
id: string | number
|
|
118
113
|
): Promise<{ id: string | number; payload?: Record<string, any> } | null> {
|
|
119
114
|
const points = this.points.get(collectionName) || [];
|
|
120
115
|
const point = points.find((p) => p.id === id);
|
|
@@ -127,16 +122,11 @@ class MockQdrantManager implements Partial<QdrantManager> {
|
|
|
127
122
|
};
|
|
128
123
|
}
|
|
129
124
|
|
|
130
|
-
async deletePointsByFilter(
|
|
131
|
-
collectionName: string,
|
|
132
|
-
filter: Record<string, any>,
|
|
133
|
-
): Promise<void> {
|
|
125
|
+
async deletePointsByFilter(collectionName: string, filter: Record<string, any>): Promise<void> {
|
|
134
126
|
const points = this.points.get(collectionName) || [];
|
|
135
127
|
const pathToDelete = filter?.must?.[0]?.match?.value;
|
|
136
128
|
if (pathToDelete) {
|
|
137
|
-
const filtered = points.filter(
|
|
138
|
-
(p) => p.payload?.relativePath !== pathToDelete,
|
|
139
|
-
);
|
|
129
|
+
const filtered = points.filter((p) => p.payload?.relativePath !== pathToDelete);
|
|
140
130
|
this.points.set(collectionName, filtered);
|
|
141
131
|
}
|
|
142
132
|
}
|
|
@@ -151,20 +141,14 @@ class MockEmbeddingProvider implements EmbeddingProvider {
|
|
|
151
141
|
return "mock-model";
|
|
152
142
|
}
|
|
153
143
|
|
|
154
|
-
async embed(
|
|
155
|
-
text: string,
|
|
156
|
-
): Promise<{ embedding: number[]; dimensions: number }> {
|
|
144
|
+
async embed(text: string): Promise<{ embedding: number[]; dimensions: number }> {
|
|
157
145
|
// Simple hash-based mock embedding
|
|
158
|
-
const hash = text
|
|
159
|
-
.split("")
|
|
160
|
-
.reduce((acc, char) => acc + char.charCodeAt(0), 0);
|
|
146
|
+
const hash = text.split("").reduce((acc, char) => acc + char.charCodeAt(0), 0);
|
|
161
147
|
const base = (hash % 100) / 100;
|
|
162
148
|
return { embedding: new Array(384).fill(base), dimensions: 384 };
|
|
163
149
|
}
|
|
164
150
|
|
|
165
|
-
async embedBatch(
|
|
166
|
-
texts: string[],
|
|
167
|
-
): Promise<Array<{ embedding: number[]; dimensions: number }>> {
|
|
151
|
+
async embedBatch(texts: string[]): Promise<Array<{ embedding: number[]; dimensions: number }>> {
|
|
168
152
|
return Promise.all(texts.map((text) => this.embed(text)));
|
|
169
153
|
}
|
|
170
154
|
}
|
|
@@ -180,7 +164,7 @@ describe("CodeIndexer Integration Tests", () => {
|
|
|
180
164
|
beforeEach(async () => {
|
|
181
165
|
tempDir = join(
|
|
182
166
|
tmpdir(),
|
|
183
|
-
`qdrant-mcp-test-${Date.now()}-${Math.random().toString(36).substring(7)}
|
|
167
|
+
`qdrant-mcp-test-${Date.now()}-${Math.random().toString(36).substring(7)}`
|
|
184
168
|
);
|
|
185
169
|
codebaseDir = join(tempDir, "codebase");
|
|
186
170
|
await fs.mkdir(codebaseDir, { recursive: true });
|
|
@@ -221,7 +205,7 @@ export class AuthService {
|
|
|
221
205
|
return { token: 'jwt-token' };
|
|
222
206
|
}
|
|
223
207
|
}
|
|
224
|
-
|
|
208
|
+
`
|
|
225
209
|
);
|
|
226
210
|
|
|
227
211
|
await createTestFile(
|
|
@@ -233,7 +217,7 @@ export class RegistrationService {
|
|
|
233
217
|
return { id: '123', email: user.email };
|
|
234
218
|
}
|
|
235
219
|
}
|
|
236
|
-
|
|
220
|
+
`
|
|
237
221
|
);
|
|
238
222
|
|
|
239
223
|
await createTestFile(
|
|
@@ -243,7 +227,7 @@ export class RegistrationService {
|
|
|
243
227
|
export function validateEmail(email: string): boolean {
|
|
244
228
|
return /^[^@]+@[^@]+\\.[^@]+$/.test(email);
|
|
245
229
|
}
|
|
246
|
-
|
|
230
|
+
`
|
|
247
231
|
);
|
|
248
232
|
|
|
249
233
|
// Index the codebase
|
|
@@ -255,10 +239,7 @@ export function validateEmail(email: string): boolean {
|
|
|
255
239
|
expect(indexStats.status).toBe("completed");
|
|
256
240
|
|
|
257
241
|
// Search for authentication-related code
|
|
258
|
-
const authResults = await indexer.searchCode(
|
|
259
|
-
codebaseDir,
|
|
260
|
-
"authentication login",
|
|
261
|
-
);
|
|
242
|
+
const authResults = await indexer.searchCode(codebaseDir, "authentication login");
|
|
262
243
|
|
|
263
244
|
expect(authResults.length).toBeGreaterThan(0);
|
|
264
245
|
expect(authResults[0].language).toBe("typescript");
|
|
@@ -278,7 +259,7 @@ export function validateEmail(email: string): boolean {
|
|
|
278
259
|
import express from 'express';
|
|
279
260
|
const app = express();
|
|
280
261
|
app.listen(3000);
|
|
281
|
-
|
|
262
|
+
`
|
|
282
263
|
);
|
|
283
264
|
|
|
284
265
|
await createTestFile(
|
|
@@ -287,7 +268,7 @@ app.listen(3000);
|
|
|
287
268
|
`
|
|
288
269
|
const API_URL = 'http://localhost:3000';
|
|
289
270
|
fetch(API_URL).then(res => res.json());
|
|
290
|
-
|
|
271
|
+
`
|
|
291
272
|
);
|
|
292
273
|
|
|
293
274
|
await createTestFile(
|
|
@@ -296,7 +277,7 @@ fetch(API_URL).then(res => res.json());
|
|
|
296
277
|
`
|
|
297
278
|
def process_data(data):
|
|
298
279
|
return [x * 2 for x in data]
|
|
299
|
-
|
|
280
|
+
`
|
|
300
281
|
);
|
|
301
282
|
|
|
302
283
|
const stats = await indexer.indexCodebase(codebaseDir);
|
|
@@ -322,7 +303,7 @@ console.log('First file loaded successfully');
|
|
|
322
303
|
function init(): string {
|
|
323
304
|
console.log('Initializing system');
|
|
324
305
|
return 'ready';
|
|
325
|
-
}
|
|
306
|
+
}`
|
|
326
307
|
);
|
|
327
308
|
await createTestFile(
|
|
328
309
|
codebaseDir,
|
|
@@ -332,7 +313,7 @@ console.log('Second file loaded successfully');
|
|
|
332
313
|
function start(): string {
|
|
333
314
|
console.log('Starting application');
|
|
334
315
|
return 'started';
|
|
335
|
-
}
|
|
316
|
+
}`
|
|
336
317
|
);
|
|
337
318
|
|
|
338
319
|
const initialStats = await indexer.indexCodebase(codebaseDir);
|
|
@@ -354,7 +335,7 @@ export function process(): string {
|
|
|
354
335
|
return status;
|
|
355
336
|
}
|
|
356
337
|
|
|
357
|
-
export const thirdValue = 3
|
|
338
|
+
export const thirdValue = 3;`
|
|
358
339
|
);
|
|
359
340
|
|
|
360
341
|
// Incremental update
|
|
@@ -373,7 +354,7 @@ export const thirdValue = 3;`,
|
|
|
373
354
|
await createTestFile(
|
|
374
355
|
codebaseDir,
|
|
375
356
|
"config.ts",
|
|
376
|
-
"export const DEBUG_MODE = false;\nconsole.log('Debug mode off');"
|
|
357
|
+
"export const DEBUG_MODE = false;\nconsole.log('Debug mode off');"
|
|
377
358
|
);
|
|
378
359
|
|
|
379
360
|
await indexer.indexCodebase(codebaseDir);
|
|
@@ -382,7 +363,7 @@ export const thirdValue = 3;`,
|
|
|
382
363
|
await createTestFile(
|
|
383
364
|
codebaseDir,
|
|
384
365
|
"config.ts",
|
|
385
|
-
"export const DEBUG_MODE = true;\nconsole.log('Debug mode on');"
|
|
366
|
+
"export const DEBUG_MODE = true;\nconsole.log('Debug mode on');"
|
|
386
367
|
);
|
|
387
368
|
|
|
388
369
|
const updateStats = await indexer.reindexChanges(codebaseDir);
|
|
@@ -395,12 +376,12 @@ export const thirdValue = 3;`,
|
|
|
395
376
|
await createTestFile(
|
|
396
377
|
codebaseDir,
|
|
397
378
|
"temp.ts",
|
|
398
|
-
"export const tempValue = true;\nconsole.log('Temporary file created');\nfunction cleanup() { return null; }"
|
|
379
|
+
"export const tempValue = true;\nconsole.log('Temporary file created');\nfunction cleanup() { return null; }"
|
|
399
380
|
);
|
|
400
381
|
await createTestFile(
|
|
401
382
|
codebaseDir,
|
|
402
383
|
"keep.ts",
|
|
403
|
-
"export const keepValue = true;\nconsole.log('Permanent file stays');\nfunction maintain() { return true; }"
|
|
384
|
+
"export const keepValue = true;\nconsole.log('Permanent file stays');\nfunction maintain() { return true; }"
|
|
404
385
|
);
|
|
405
386
|
|
|
406
387
|
await indexer.indexCodebase(codebaseDir);
|
|
@@ -419,17 +400,17 @@ export const thirdValue = 3;`,
|
|
|
419
400
|
await createTestFile(
|
|
420
401
|
codebaseDir,
|
|
421
402
|
"file1.ts",
|
|
422
|
-
"export const alpha = 1;\nconsole.log('Alpha file');"
|
|
403
|
+
"export const alpha = 1;\nconsole.log('Alpha file');"
|
|
423
404
|
);
|
|
424
405
|
await createTestFile(
|
|
425
406
|
codebaseDir,
|
|
426
407
|
"file2.ts",
|
|
427
|
-
"export const beta = 2;\nconsole.log('Beta file');"
|
|
408
|
+
"export const beta = 2;\nconsole.log('Beta file');"
|
|
428
409
|
);
|
|
429
410
|
await createTestFile(
|
|
430
411
|
codebaseDir,
|
|
431
412
|
"file3.ts",
|
|
432
|
-
"export const gamma = 3;\nconsole.log('Gamma file');"
|
|
413
|
+
"export const gamma = 3;\nconsole.log('Gamma file');"
|
|
433
414
|
);
|
|
434
415
|
|
|
435
416
|
await indexer.indexCodebase(codebaseDir);
|
|
@@ -438,12 +419,12 @@ export const thirdValue = 3;`,
|
|
|
438
419
|
await createTestFile(
|
|
439
420
|
codebaseDir,
|
|
440
421
|
"file1.ts",
|
|
441
|
-
"export const alpha = 100;\nconsole.log('Alpha modified');"
|
|
422
|
+
"export const alpha = 100;\nconsole.log('Alpha modified');"
|
|
442
423
|
); // Modified
|
|
443
424
|
await createTestFile(
|
|
444
425
|
codebaseDir,
|
|
445
426
|
"file4.ts",
|
|
446
|
-
"export const delta = 4;\nconsole.log('Delta file added');"
|
|
427
|
+
"export const delta = 4;\nconsole.log('Delta file added');"
|
|
447
428
|
); // Added
|
|
448
429
|
await fs.unlink(join(codebaseDir, "file3.ts")); // Deleted
|
|
449
430
|
|
|
@@ -457,21 +438,9 @@ export const thirdValue = 3;`,
|
|
|
457
438
|
|
|
458
439
|
describe("Search filtering and options", () => {
|
|
459
440
|
beforeEach(async () => {
|
|
460
|
-
await createTestFile(
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
"export class UserService {}",
|
|
464
|
-
);
|
|
465
|
-
await createTestFile(
|
|
466
|
-
codebaseDir,
|
|
467
|
-
"auth.ts",
|
|
468
|
-
"export class AuthService {}",
|
|
469
|
-
);
|
|
470
|
-
await createTestFile(
|
|
471
|
-
codebaseDir,
|
|
472
|
-
"utils.js",
|
|
473
|
-
"export function helper() {}",
|
|
474
|
-
);
|
|
441
|
+
await createTestFile(codebaseDir, "users.ts", "export class UserService {}");
|
|
442
|
+
await createTestFile(codebaseDir, "auth.ts", "export class AuthService {}");
|
|
443
|
+
await createTestFile(codebaseDir, "utils.js", "export function helper() {}");
|
|
475
444
|
await createTestFile(codebaseDir, "data.py", "class DataProcessor: pass");
|
|
476
445
|
|
|
477
446
|
await indexer.indexCodebase(codebaseDir);
|
|
@@ -506,11 +475,7 @@ export const thirdValue = 3;`,
|
|
|
506
475
|
});
|
|
507
476
|
|
|
508
477
|
it("should support path pattern filtering", async () => {
|
|
509
|
-
await createTestFile(
|
|
510
|
-
codebaseDir,
|
|
511
|
-
"src/api/endpoints.ts",
|
|
512
|
-
"export const API = {}",
|
|
513
|
-
);
|
|
478
|
+
await createTestFile(codebaseDir, "src/api/endpoints.ts", "export const API = {}");
|
|
514
479
|
await indexer.indexCodebase(codebaseDir, { forceReindex: true });
|
|
515
480
|
|
|
516
481
|
const results = await indexer.searchCode(codebaseDir, "export", {
|
|
@@ -524,35 +489,24 @@ export const thirdValue = 3;`,
|
|
|
524
489
|
describe("Hybrid search workflow", () => {
|
|
525
490
|
it("should enable and use hybrid search", async () => {
|
|
526
491
|
const hybridConfig = { ...config, enableHybridSearch: true };
|
|
527
|
-
const hybridIndexer = new CodeIndexer(
|
|
528
|
-
qdrant as any,
|
|
529
|
-
embeddings,
|
|
530
|
-
hybridConfig,
|
|
531
|
-
);
|
|
492
|
+
const hybridIndexer = new CodeIndexer(qdrant as any, embeddings, hybridConfig);
|
|
532
493
|
|
|
533
494
|
await createTestFile(
|
|
534
495
|
codebaseDir,
|
|
535
496
|
"search.ts",
|
|
536
|
-
"function performSearch(query: string) { return results; }"
|
|
497
|
+
"function performSearch(query: string) { return results; }"
|
|
537
498
|
);
|
|
538
499
|
|
|
539
500
|
await hybridIndexer.indexCodebase(codebaseDir);
|
|
540
501
|
|
|
541
|
-
const results = await hybridIndexer.searchCode(
|
|
542
|
-
codebaseDir,
|
|
543
|
-
"search query",
|
|
544
|
-
);
|
|
502
|
+
const results = await hybridIndexer.searchCode(codebaseDir, "search query");
|
|
545
503
|
|
|
546
504
|
expect(results.length).toBeGreaterThan(0);
|
|
547
505
|
});
|
|
548
506
|
|
|
549
507
|
it("should fallback to standard search if hybrid not available", async () => {
|
|
550
508
|
const hybridConfig = { ...config, enableHybridSearch: true };
|
|
551
|
-
const hybridIndexer = new CodeIndexer(
|
|
552
|
-
qdrant as any,
|
|
553
|
-
embeddings,
|
|
554
|
-
hybridConfig,
|
|
555
|
-
);
|
|
509
|
+
const hybridIndexer = new CodeIndexer(qdrant as any, embeddings, hybridConfig);
|
|
556
510
|
|
|
557
511
|
// Index without hybrid
|
|
558
512
|
await createTestFile(
|
|
@@ -563,7 +517,7 @@ console.log('Test value configured successfully');
|
|
|
563
517
|
function validate(): boolean {
|
|
564
518
|
console.log('Validating test value');
|
|
565
519
|
return testValue === true;
|
|
566
|
-
}
|
|
520
|
+
}`
|
|
567
521
|
);
|
|
568
522
|
await indexer.indexCodebase(codebaseDir);
|
|
569
523
|
|
|
@@ -581,7 +535,7 @@ function validate(): boolean {
|
|
|
581
535
|
await createTestFile(
|
|
582
536
|
codebaseDir,
|
|
583
537
|
`module${i}.ts`,
|
|
584
|
-
`export function func${i}() { return ${i}; }
|
|
538
|
+
`export function func${i}() { return ${i}; }`
|
|
585
539
|
);
|
|
586
540
|
}
|
|
587
541
|
|
|
@@ -611,12 +565,12 @@ function validate(): boolean {
|
|
|
611
565
|
await createTestFile(
|
|
612
566
|
codebaseDir,
|
|
613
567
|
"valid.ts",
|
|
614
|
-
"export const validValue = true;\nconsole.log('Valid file');"
|
|
568
|
+
"export const validValue = true;\nconsole.log('Valid file');"
|
|
615
569
|
);
|
|
616
570
|
await createTestFile(
|
|
617
571
|
codebaseDir,
|
|
618
572
|
"secrets.ts",
|
|
619
|
-
'export const apiKey = "sk_test_FAKE_KEY_FOR_TESTING_NOT_REAL_KEY";\nconsole.log("Secrets file");'
|
|
573
|
+
'export const apiKey = "sk_test_FAKE_KEY_FOR_TESTING_NOT_REAL_KEY";\nconsole.log("Secrets file");'
|
|
620
574
|
);
|
|
621
575
|
|
|
622
576
|
const stats = await indexer.indexCodebase(codebaseDir);
|
|
@@ -631,7 +585,7 @@ function validate(): boolean {
|
|
|
631
585
|
await createTestFile(
|
|
632
586
|
codebaseDir,
|
|
633
587
|
"test.ts",
|
|
634
|
-
"export const testData = true;\nconsole.log('Test data loaded');"
|
|
588
|
+
"export const testData = true;\nconsole.log('Test data loaded');"
|
|
635
589
|
);
|
|
636
590
|
|
|
637
591
|
const stats1 = await indexer.indexCodebase(codebaseDir);
|
|
@@ -683,7 +637,7 @@ function validate(): boolean {
|
|
|
683
637
|
await createTestFile(
|
|
684
638
|
codebaseDir,
|
|
685
639
|
`component${i}.ts`,
|
|
686
|
-
`export class Component${i} {\n private value = ${i};\n render() {\n console.log('Rendering component ${i}');\n return this.value;\n }\n}
|
|
640
|
+
`export class Component${i} {\n private value = ${i};\n render() {\n console.log('Rendering component ${i}');\n return this.value;\n }\n}`
|
|
687
641
|
);
|
|
688
642
|
}
|
|
689
643
|
|
|
@@ -710,7 +664,7 @@ function validate(): boolean {
|
|
|
710
664
|
await createTestFile(
|
|
711
665
|
codebaseDir,
|
|
712
666
|
"timestamped.ts",
|
|
713
|
-
"export const timestamp = Date.now();\nconsole.log('Timestamp test');\nfunction getTime() { return timestamp; }"
|
|
667
|
+
"export const timestamp = Date.now();\nconsole.log('Timestamp test');\nfunction getTime() { return timestamp; }"
|
|
714
668
|
);
|
|
715
669
|
|
|
716
670
|
const beforeIndexing = new Date();
|
|
@@ -722,12 +676,8 @@ function validate(): boolean {
|
|
|
722
676
|
expect(status.status).toBe("indexed");
|
|
723
677
|
expect(status.lastUpdated).toBeDefined();
|
|
724
678
|
expect(status.lastUpdated).toBeInstanceOf(Date);
|
|
725
|
-
expect(status.lastUpdated
|
|
726
|
-
|
|
727
|
-
);
|
|
728
|
-
expect(status.lastUpdated!.getTime()).toBeLessThanOrEqual(
|
|
729
|
-
afterIndexing.getTime(),
|
|
730
|
-
);
|
|
679
|
+
expect(status.lastUpdated?.getTime()).toBeGreaterThanOrEqual(beforeIndexing.getTime());
|
|
680
|
+
expect(status.lastUpdated?.getTime()).toBeLessThanOrEqual(afterIndexing.getTime());
|
|
731
681
|
});
|
|
732
682
|
|
|
733
683
|
it("should correctly count chunks excluding metadata point", async () => {
|
|
@@ -736,11 +686,11 @@ function validate(): boolean {
|
|
|
736
686
|
await createTestFile(
|
|
737
687
|
codebaseDir,
|
|
738
688
|
`service${i}.ts`,
|
|
739
|
-
`export class Service${i} {\n async process(input: string): Promise<string> {\n console.log('Processing in service ${i}:', input);\n const result = input.toUpperCase();\n return result;\n }\n async validate(data: any): Promise<boolean> {\n console.log('Validating in service ${i}');\n return data !== null;\n }\n}
|
|
689
|
+
`export class Service${i} {\n async process(input: string): Promise<string> {\n console.log('Processing in service ${i}:', input);\n const result = input.toUpperCase();\n return result;\n }\n async validate(data: any): Promise<boolean> {\n console.log('Validating in service ${i}');\n return data !== null;\n }\n}`
|
|
740
690
|
);
|
|
741
691
|
}
|
|
742
692
|
|
|
743
|
-
const
|
|
693
|
+
const _stats = await indexer.indexCodebase(codebaseDir);
|
|
744
694
|
const status = await indexer.getIndexStatus(codebaseDir);
|
|
745
695
|
|
|
746
696
|
// The chunks count in status should match what was indexed
|
|
@@ -755,7 +705,7 @@ function validate(): boolean {
|
|
|
755
705
|
await createTestFile(
|
|
756
706
|
codebaseDir,
|
|
757
707
|
"reindexable.ts",
|
|
758
|
-
"export const version = 1;\nconsole.log('Version:', version);\nfunction getVersion() { return version; }"
|
|
708
|
+
"export const version = 1;\nconsole.log('Version:', version);\nfunction getVersion() { return version; }"
|
|
759
709
|
);
|
|
760
710
|
|
|
761
711
|
// Initial index
|
|
@@ -777,9 +727,7 @@ function validate(): boolean {
|
|
|
777
727
|
|
|
778
728
|
// Timestamp should be updated
|
|
779
729
|
if (firstTimestamp && status.lastUpdated) {
|
|
780
|
-
expect(status.lastUpdated.getTime()).toBeGreaterThanOrEqual(
|
|
781
|
-
firstTimestamp.getTime(),
|
|
782
|
-
);
|
|
730
|
+
expect(status.lastUpdated.getTime()).toBeGreaterThanOrEqual(firstTimestamp.getTime());
|
|
783
731
|
}
|
|
784
732
|
});
|
|
785
733
|
});
|
|
@@ -790,7 +738,7 @@ function validate(): boolean {
|
|
|
790
738
|
await createTestFile(
|
|
791
739
|
codebaseDir,
|
|
792
740
|
`file${i}.ts`,
|
|
793
|
-
`export const value${i} = ${i};\nconsole.log('File ${i} loaded successfully');\nfunction process${i}() { return value${i} * 2; }
|
|
741
|
+
`export const value${i} = ${i};\nconsole.log('File ${i} loaded successfully');\nfunction process${i}() { return value${i} * 2; }`
|
|
794
742
|
);
|
|
795
743
|
}
|
|
796
744
|
|
|
@@ -811,14 +759,14 @@ function validate(): boolean {
|
|
|
811
759
|
await createTestFile(
|
|
812
760
|
codebaseDir,
|
|
813
761
|
"file1.ts",
|
|
814
|
-
"export const initial = 1;\nconsole.log('Initial file');"
|
|
762
|
+
"export const initial = 1;\nconsole.log('Initial file');"
|
|
815
763
|
);
|
|
816
764
|
await indexer.indexCodebase(codebaseDir);
|
|
817
765
|
|
|
818
766
|
await createTestFile(
|
|
819
767
|
codebaseDir,
|
|
820
768
|
"file2.ts",
|
|
821
|
-
"export const additional = 2;\nconsole.log('Additional file');"
|
|
769
|
+
"export const additional = 2;\nconsole.log('Additional file');"
|
|
822
770
|
);
|
|
823
771
|
|
|
824
772
|
const progressUpdates: string[] = [];
|
|
@@ -835,17 +783,13 @@ function validate(): boolean {
|
|
|
835
783
|
describe("Hybrid search with incremental updates", () => {
|
|
836
784
|
it("should use hybrid search during reindexChanges", async () => {
|
|
837
785
|
const hybridConfig = { ...config, enableHybridSearch: true };
|
|
838
|
-
const hybridIndexer = new CodeIndexer(
|
|
839
|
-
qdrant as any,
|
|
840
|
-
embeddings,
|
|
841
|
-
hybridConfig,
|
|
842
|
-
);
|
|
786
|
+
const hybridIndexer = new CodeIndexer(qdrant as any, embeddings, hybridConfig);
|
|
843
787
|
|
|
844
788
|
// Initial indexing with hybrid search
|
|
845
789
|
await createTestFile(
|
|
846
790
|
codebaseDir,
|
|
847
791
|
"initial.ts",
|
|
848
|
-
"export const initial = 1;\nconsole.log('Initial file');"
|
|
792
|
+
"export const initial = 1;\nconsole.log('Initial file');"
|
|
849
793
|
);
|
|
850
794
|
await hybridIndexer.indexCodebase(codebaseDir);
|
|
851
795
|
|
|
@@ -869,7 +813,7 @@ export class DataProcessor {
|
|
|
869
813
|
process(input: string): string {
|
|
870
814
|
return input.trim();
|
|
871
815
|
}
|
|
872
|
-
}
|
|
816
|
+
}`
|
|
873
817
|
);
|
|
874
818
|
|
|
875
819
|
// Reindex with hybrid search - this should cover lines 540-545
|
|
@@ -886,7 +830,7 @@ export class DataProcessor {
|
|
|
886
830
|
await createTestFile(
|
|
887
831
|
codebaseDir,
|
|
888
832
|
"file1.ts",
|
|
889
|
-
"export const value = 1;\nconsole.log('File 1');"
|
|
833
|
+
"export const value = 1;\nconsole.log('File 1');"
|
|
890
834
|
);
|
|
891
835
|
await indexer.indexCodebase(codebaseDir);
|
|
892
836
|
|
|
@@ -894,7 +838,7 @@ export class DataProcessor {
|
|
|
894
838
|
await createTestFile(
|
|
895
839
|
codebaseDir,
|
|
896
840
|
"file2.ts",
|
|
897
|
-
"export const value2 = 2;\nconsole.log('File 2');"
|
|
841
|
+
"export const value2 = 2;\nconsole.log('File 2');"
|
|
898
842
|
);
|
|
899
843
|
|
|
900
844
|
// This should not throw even if there are processing errors
|
|
@@ -911,7 +855,7 @@ export class DataProcessor {
|
|
|
911
855
|
async function createTestFile(
|
|
912
856
|
baseDir: string,
|
|
913
857
|
relativePath: string,
|
|
914
|
-
content: string
|
|
858
|
+
content: string
|
|
915
859
|
): Promise<void> {
|
|
916
860
|
const fullPath = join(baseDir, relativePath);
|
|
917
861
|
const dir = join(fullPath, "..");
|
|
@@ -41,9 +41,7 @@ describe("FileScanner", () => {
|
|
|
41
41
|
it("should respect supported extensions", async () => {
|
|
42
42
|
const files = await scanner.scanDirectory(join(fixturesDir, "sample-ts"));
|
|
43
43
|
files.forEach((file) => {
|
|
44
|
-
const hasValidExt = config.supportedExtensions.some((ext) =>
|
|
45
|
-
file.endsWith(ext),
|
|
46
|
-
);
|
|
44
|
+
const hasValidExt = config.supportedExtensions.some((ext) => file.endsWith(ext));
|
|
47
45
|
expect(hasValidExt).toBe(true);
|
|
48
46
|
});
|
|
49
47
|
});
|
|
@@ -69,9 +67,7 @@ describe("FileScanner", () => {
|
|
|
69
67
|
});
|
|
70
68
|
|
|
71
69
|
it("should handle missing ignore files gracefully", async () => {
|
|
72
|
-
await expect(
|
|
73
|
-
scanner.loadIgnorePatterns("/nonexistent/path"),
|
|
74
|
-
).resolves.not.toThrow();
|
|
70
|
+
await expect(scanner.loadIgnorePatterns("/nonexistent/path")).resolves.not.toThrow();
|
|
75
71
|
});
|
|
76
72
|
});
|
|
77
73
|
|
|
@@ -92,12 +88,7 @@ describe("FileScanner", () => {
|
|
|
92
88
|
await ignoreScanner.loadIgnorePatterns(join(fixturesDir, "sample-ts"));
|
|
93
89
|
|
|
94
90
|
const rootPath = join(fixturesDir, "sample-ts");
|
|
95
|
-
const ignoredPath = join(
|
|
96
|
-
rootPath,
|
|
97
|
-
"node_modules",
|
|
98
|
-
"some-package",
|
|
99
|
-
"index.js",
|
|
100
|
-
);
|
|
91
|
+
const ignoredPath = join(rootPath, "node_modules", "some-package", "index.js");
|
|
101
92
|
|
|
102
93
|
expect(ignoreScanner.shouldIgnore(ignoredPath, rootPath)).toBe(true);
|
|
103
94
|
});
|
|
@@ -127,21 +118,11 @@ describe("FileScanner", () => {
|
|
|
127
118
|
|
|
128
119
|
const rootPath = join(fixturesDir, "sample-ts");
|
|
129
120
|
|
|
130
|
-
expect(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
).toBe(true);
|
|
136
|
-
expect(
|
|
137
|
-
customScanner.shouldIgnore(
|
|
138
|
-
join(rootPath, "tests", "main.ts"),
|
|
139
|
-
rootPath,
|
|
140
|
-
),
|
|
141
|
-
).toBe(true);
|
|
142
|
-
expect(
|
|
143
|
-
customScanner.shouldIgnore(join(rootPath, "src", "utils.ts"), rootPath),
|
|
144
|
-
).toBe(false);
|
|
121
|
+
expect(customScanner.shouldIgnore(join(rootPath, "src", "utils.test.ts"), rootPath)).toBe(
|
|
122
|
+
true
|
|
123
|
+
);
|
|
124
|
+
expect(customScanner.shouldIgnore(join(rootPath, "tests", "main.ts"), rootPath)).toBe(true);
|
|
125
|
+
expect(customScanner.shouldIgnore(join(rootPath, "src", "utils.ts"), rootPath)).toBe(false);
|
|
145
126
|
});
|
|
146
127
|
});
|
|
147
128
|
|
|
@@ -164,9 +145,7 @@ describe("FileScanner", () => {
|
|
|
164
145
|
};
|
|
165
146
|
const customScanner = new FileScanner(customConfig);
|
|
166
147
|
await customScanner.loadIgnorePatterns(join(fixturesDir, "sample-ts"));
|
|
167
|
-
const files = await customScanner.scanDirectory(
|
|
168
|
-
join(fixturesDir, "sample-ts"),
|
|
169
|
-
);
|
|
148
|
+
const files = await customScanner.scanDirectory(join(fixturesDir, "sample-ts"));
|
|
170
149
|
expect(files.some((f) => f.includes(".test.ts"))).toBe(false);
|
|
171
150
|
});
|
|
172
151
|
|
|
@@ -177,9 +156,7 @@ describe("FileScanner", () => {
|
|
|
177
156
|
};
|
|
178
157
|
const ignoreScanner = new FileScanner(ignoreConfig);
|
|
179
158
|
await ignoreScanner.loadIgnorePatterns(join(fixturesDir, "sample-ts"));
|
|
180
|
-
const files = await ignoreScanner.scanDirectory(
|
|
181
|
-
join(fixturesDir, "sample-ts"),
|
|
182
|
-
);
|
|
159
|
+
const files = await ignoreScanner.scanDirectory(join(fixturesDir, "sample-ts"));
|
|
183
160
|
|
|
184
161
|
// Should not include auth.ts due to ignore pattern
|
|
185
162
|
expect(files.some((f) => f.endsWith("auth.ts"))).toBe(false);
|
|
@@ -187,12 +164,8 @@ describe("FileScanner", () => {
|
|
|
187
164
|
|
|
188
165
|
it("should handle directories with .gitignore", async () => {
|
|
189
166
|
const scannerWithGitignore = new FileScanner(config);
|
|
190
|
-
await scannerWithGitignore.loadIgnorePatterns(
|
|
191
|
-
|
|
192
|
-
);
|
|
193
|
-
const files = await scannerWithGitignore.scanDirectory(
|
|
194
|
-
join(fixturesDir, "sample-ts"),
|
|
195
|
-
);
|
|
167
|
+
await scannerWithGitignore.loadIgnorePatterns(join(fixturesDir, "sample-ts"));
|
|
168
|
+
const files = await scannerWithGitignore.scanDirectory(join(fixturesDir, "sample-ts"));
|
|
196
169
|
|
|
197
170
|
// Files matching .gitignore patterns should be excluded
|
|
198
171
|
expect(Array.isArray(files)).toBe(true);
|
|
@@ -14,7 +14,7 @@ describe("SnapshotManager", () => {
|
|
|
14
14
|
// Create a temporary directory for test snapshots
|
|
15
15
|
tempDir = join(
|
|
16
16
|
tmpdir(),
|
|
17
|
-
`qdrant-mcp-test-${Date.now()}-${Math.random().toString(36).substring(7)}
|
|
17
|
+
`qdrant-mcp-test-${Date.now()}-${Math.random().toString(36).substring(7)}`
|
|
18
18
|
);
|
|
19
19
|
await fs.mkdir(tempDir, { recursive: true });
|
|
20
20
|
|
|
@@ -177,11 +177,7 @@ describe("SnapshotManager", () => {
|
|
|
177
177
|
|
|
178
178
|
it("should handle missing fields in snapshot", async () => {
|
|
179
179
|
const snapshotPath = join(tempDir, `${collectionName}.json`);
|
|
180
|
-
await fs.writeFile(
|
|
181
|
-
snapshotPath,
|
|
182
|
-
JSON.stringify({ codebasePath: "/test" }),
|
|
183
|
-
"utf-8",
|
|
184
|
-
);
|
|
180
|
+
await fs.writeFile(snapshotPath, JSON.stringify({ codebasePath: "/test" }), "utf-8");
|
|
185
181
|
|
|
186
182
|
const loaded = await snapshotManager.load();
|
|
187
183
|
|
|
@@ -376,9 +372,7 @@ describe("SnapshotManager", () => {
|
|
|
376
372
|
const tree = new MerkleTree();
|
|
377
373
|
tree.build(fileHashes);
|
|
378
374
|
|
|
379
|
-
promises.push(
|
|
380
|
-
snapshotManager.save(`/test/codebase${i}`, fileHashes, tree),
|
|
381
|
-
);
|
|
375
|
+
promises.push(snapshotManager.save(`/test/codebase${i}`, fileHashes, tree));
|
|
382
376
|
}
|
|
383
377
|
|
|
384
378
|
await Promise.all(promises);
|
|
@@ -395,11 +389,7 @@ describe("SnapshotManager", () => {
|
|
|
395
389
|
|
|
396
390
|
await snapshotManager.save("/test/codebase", fileHashes, tree);
|
|
397
391
|
|
|
398
|
-
const promises = [
|
|
399
|
-
snapshotManager.load(),
|
|
400
|
-
snapshotManager.load(),
|
|
401
|
-
snapshotManager.load(),
|
|
402
|
-
];
|
|
392
|
+
const promises = [snapshotManager.load(), snapshotManager.load(), snapshotManager.load()];
|
|
403
393
|
|
|
404
394
|
const results = await Promise.all(promises);
|
|
405
395
|
|