@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
package/src/git/indexer.ts
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
6
6
|
import { promises as fs } from "node:fs";
|
|
7
7
|
import { resolve } from "node:path";
|
|
8
|
-
import logger from "../logger.js";
|
|
9
8
|
import type { EmbeddingProvider } from "../embeddings/base.js";
|
|
10
9
|
import { BM25SparseVectorGenerator } from "../embeddings/sparse.js";
|
|
10
|
+
import logger from "../logger.js";
|
|
11
11
|
import type { QdrantManager } from "../qdrant/client.js";
|
|
12
12
|
import { CommitChunker } from "./chunker.js";
|
|
13
13
|
import { GIT_INDEXING_METADATA_ID } from "./config.js";
|
|
@@ -31,7 +31,7 @@ export class GitHistoryIndexer {
|
|
|
31
31
|
constructor(
|
|
32
32
|
private qdrant: QdrantManager,
|
|
33
33
|
private embeddings: EmbeddingProvider,
|
|
34
|
-
private config: GitConfig
|
|
34
|
+
private config: GitConfig
|
|
35
35
|
) {}
|
|
36
36
|
|
|
37
37
|
/**
|
|
@@ -54,7 +54,7 @@ export class GitHistoryIndexer {
|
|
|
54
54
|
async indexHistory(
|
|
55
55
|
path: string,
|
|
56
56
|
options?: GitIndexOptions,
|
|
57
|
-
progressCallback?: GitProgressCallback
|
|
57
|
+
progressCallback?: GitProgressCallback
|
|
58
58
|
): Promise<GitIndexStats> {
|
|
59
59
|
const startTime = Date.now();
|
|
60
60
|
const stats: GitIndexStats = {
|
|
@@ -69,10 +69,7 @@ export class GitHistoryIndexer {
|
|
|
69
69
|
const absolutePath = await this.validatePath(path);
|
|
70
70
|
const collectionName = await this.getCollectionName(absolutePath);
|
|
71
71
|
|
|
72
|
-
this.log.info(
|
|
73
|
-
{ path: absolutePath, collectionName },
|
|
74
|
-
"Git indexing started",
|
|
75
|
-
);
|
|
72
|
+
this.log.info({ path: absolutePath, collectionName }, "Git indexing started");
|
|
76
73
|
|
|
77
74
|
try {
|
|
78
75
|
// 1. Validate repository
|
|
@@ -92,8 +89,7 @@ export class GitHistoryIndexer {
|
|
|
92
89
|
}
|
|
93
90
|
|
|
94
91
|
// 2. Create or verify collection
|
|
95
|
-
const collectionExists =
|
|
96
|
-
await this.qdrant.collectionExists(collectionName);
|
|
92
|
+
const collectionExists = await this.qdrant.collectionExists(collectionName);
|
|
97
93
|
|
|
98
94
|
if (options?.forceReindex && collectionExists) {
|
|
99
95
|
await this.qdrant.deleteCollection(collectionName);
|
|
@@ -105,7 +101,7 @@ export class GitHistoryIndexer {
|
|
|
105
101
|
collectionName,
|
|
106
102
|
vectorSize,
|
|
107
103
|
"Cosine",
|
|
108
|
-
this.config.enableHybridSearch
|
|
104
|
+
this.config.enableHybridSearch
|
|
109
105
|
);
|
|
110
106
|
}
|
|
111
107
|
|
|
@@ -165,11 +161,8 @@ export class GitHistoryIndexer {
|
|
|
165
161
|
|
|
166
162
|
stats.commitsIndexed++;
|
|
167
163
|
} catch (error) {
|
|
168
|
-
const errorMessage =
|
|
169
|
-
|
|
170
|
-
stats.errors?.push(
|
|
171
|
-
`Failed to process commit ${commit.shortHash}: ${errorMessage}`,
|
|
172
|
-
);
|
|
164
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
165
|
+
stats.errors?.push(`Failed to process commit ${commit.shortHash}: ${errorMessage}`);
|
|
173
166
|
}
|
|
174
167
|
}
|
|
175
168
|
|
|
@@ -184,10 +177,7 @@ export class GitHistoryIndexer {
|
|
|
184
177
|
|
|
185
178
|
// 5. Generate embeddings and store in batches
|
|
186
179
|
const batchSize = this.config.batchSize;
|
|
187
|
-
this.log.debug(
|
|
188
|
-
{ totalChunks: allChunks.length, batchSize },
|
|
189
|
-
"Starting embedding generation",
|
|
190
|
-
);
|
|
180
|
+
this.log.debug({ totalChunks: allChunks.length, batchSize }, "Starting embedding generation");
|
|
191
181
|
for (let i = 0; i < allChunks.length; i += batchSize) {
|
|
192
182
|
const batch = allChunks.slice(i, i + batchSize);
|
|
193
183
|
|
|
@@ -195,8 +185,7 @@ export class GitHistoryIndexer {
|
|
|
195
185
|
phase: "embedding",
|
|
196
186
|
current: i + batch.length,
|
|
197
187
|
total: allChunks.length,
|
|
198
|
-
percentage:
|
|
199
|
-
40 + Math.round(((i + batch.length) / allChunks.length) * 30),
|
|
188
|
+
percentage: 40 + Math.round(((i + batch.length) / allChunks.length) * 30),
|
|
200
189
|
message: `Generating embeddings ${i + batch.length}/${allChunks.length}`,
|
|
201
190
|
});
|
|
202
191
|
|
|
@@ -204,11 +193,7 @@ export class GitHistoryIndexer {
|
|
|
204
193
|
let lastError: Error | null = null;
|
|
205
194
|
let success = false;
|
|
206
195
|
|
|
207
|
-
for (
|
|
208
|
-
let attempt = 1;
|
|
209
|
-
attempt <= this.config.batchRetryAttempts;
|
|
210
|
-
attempt++
|
|
211
|
-
) {
|
|
196
|
+
for (let attempt = 1; attempt <= this.config.batchRetryAttempts; attempt++) {
|
|
212
197
|
try {
|
|
213
198
|
const texts = batch.map((b) => b.chunk.content);
|
|
214
199
|
const embeddings = await this.embeddings.embedBatch(texts);
|
|
@@ -217,8 +202,7 @@ export class GitHistoryIndexer {
|
|
|
217
202
|
phase: "storing",
|
|
218
203
|
current: i + batch.length,
|
|
219
204
|
total: allChunks.length,
|
|
220
|
-
percentage:
|
|
221
|
-
70 + Math.round(((i + batch.length) / allChunks.length) * 30),
|
|
205
|
+
percentage: 70 + Math.round(((i + batch.length) / allChunks.length) * 30),
|
|
222
206
|
message: `Storing chunks ${i + batch.length}/${allChunks.length}`,
|
|
223
207
|
});
|
|
224
208
|
|
|
@@ -245,14 +229,9 @@ export class GitHistoryIndexer {
|
|
|
245
229
|
const sparseGenerator = new BM25SparseVectorGenerator();
|
|
246
230
|
const hybridPoints = points.map((point, idx) => ({
|
|
247
231
|
...point,
|
|
248
|
-
sparseVector: sparseGenerator.generate(
|
|
249
|
-
batch[idx].chunk.content,
|
|
250
|
-
),
|
|
232
|
+
sparseVector: sparseGenerator.generate(batch[idx].chunk.content),
|
|
251
233
|
}));
|
|
252
|
-
await this.qdrant.addPointsWithSparse(
|
|
253
|
-
collectionName,
|
|
254
|
-
hybridPoints,
|
|
255
|
-
);
|
|
234
|
+
await this.qdrant.addPointsWithSparse(collectionName, hybridPoints);
|
|
256
235
|
} else {
|
|
257
236
|
await this.qdrant.addPoints(collectionName, points);
|
|
258
237
|
}
|
|
@@ -260,11 +239,10 @@ export class GitHistoryIndexer {
|
|
|
260
239
|
success = true;
|
|
261
240
|
break;
|
|
262
241
|
} catch (error) {
|
|
263
|
-
lastError =
|
|
264
|
-
error instanceof Error ? error : new Error(String(error));
|
|
242
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
265
243
|
if (attempt < this.config.batchRetryAttempts) {
|
|
266
244
|
// Exponential backoff: 1s, 2s, 4s...
|
|
267
|
-
const delay =
|
|
245
|
+
const delay = 2 ** (attempt - 1) * 1000;
|
|
268
246
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
269
247
|
}
|
|
270
248
|
}
|
|
@@ -272,7 +250,7 @@ export class GitHistoryIndexer {
|
|
|
272
250
|
|
|
273
251
|
if (!success && lastError) {
|
|
274
252
|
stats.errors?.push(
|
|
275
|
-
`Failed to process batch at index ${i} after ${this.config.batchRetryAttempts} attempts: ${lastError.message}
|
|
253
|
+
`Failed to process batch at index ${i} after ${this.config.batchRetryAttempts} attempts: ${lastError.message}`
|
|
276
254
|
);
|
|
277
255
|
stats.status = "partial";
|
|
278
256
|
}
|
|
@@ -284,8 +262,7 @@ export class GitHistoryIndexer {
|
|
|
284
262
|
const synchronizer = new GitSynchronizer(absolutePath, collectionName);
|
|
285
263
|
await synchronizer.updateSnapshot(latestHash, stats.commitsIndexed);
|
|
286
264
|
} catch (error) {
|
|
287
|
-
const errorMessage =
|
|
288
|
-
error instanceof Error ? error.message : String(error);
|
|
265
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
289
266
|
this.log.error({ err: error }, "Failed to save snapshot");
|
|
290
267
|
stats.errors?.push(`Snapshot save failed: ${errorMessage}`);
|
|
291
268
|
}
|
|
@@ -300,12 +277,11 @@ export class GitHistoryIndexer {
|
|
|
300
277
|
chunksCreated: stats.chunksCreated,
|
|
301
278
|
durationMs: stats.durationMs,
|
|
302
279
|
},
|
|
303
|
-
"Git indexing complete"
|
|
280
|
+
"Git indexing complete"
|
|
304
281
|
);
|
|
305
282
|
return stats;
|
|
306
283
|
} catch (error) {
|
|
307
|
-
const errorMessage =
|
|
308
|
-
error instanceof Error ? error.message : String(error);
|
|
284
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
309
285
|
stats.status = "failed";
|
|
310
286
|
stats.errors?.push(`Indexing failed: ${errorMessage}`);
|
|
311
287
|
stats.durationMs = Date.now() - startTime;
|
|
@@ -319,7 +295,7 @@ export class GitHistoryIndexer {
|
|
|
319
295
|
async searchHistory(
|
|
320
296
|
path: string,
|
|
321
297
|
query: string,
|
|
322
|
-
options?: GitSearchOptions
|
|
298
|
+
options?: GitSearchOptions
|
|
323
299
|
): Promise<GitSearchResult[]> {
|
|
324
300
|
// Validate date range if both dates are provided
|
|
325
301
|
if (options?.dateFrom && options?.dateTo) {
|
|
@@ -327,7 +303,7 @@ export class GitHistoryIndexer {
|
|
|
327
303
|
const toDate = new Date(options.dateTo);
|
|
328
304
|
if (fromDate > toDate) {
|
|
329
305
|
throw new Error(
|
|
330
|
-
`Invalid date range: dateFrom (${options.dateFrom}) must be before dateTo (${options.dateTo})
|
|
306
|
+
`Invalid date range: dateFrom (${options.dateFrom}) must be before dateTo (${options.dateTo})`
|
|
331
307
|
);
|
|
332
308
|
}
|
|
333
309
|
}
|
|
@@ -344,8 +320,7 @@ export class GitHistoryIndexer {
|
|
|
344
320
|
// Check if collection has hybrid search enabled
|
|
345
321
|
const collectionInfo = await this.qdrant.getCollectionInfo(collectionName);
|
|
346
322
|
const useHybrid =
|
|
347
|
-
(options?.useHybrid ?? this.config.enableHybridSearch) &&
|
|
348
|
-
collectionInfo.hybridEnabled;
|
|
323
|
+
(options?.useHybrid ?? this.config.enableHybridSearch) && collectionInfo.hybridEnabled;
|
|
349
324
|
|
|
350
325
|
// Generate query embedding
|
|
351
326
|
const { embedding } = await this.embeddings.embed(query);
|
|
@@ -363,14 +338,14 @@ export class GitHistoryIndexer {
|
|
|
363
338
|
embedding,
|
|
364
339
|
sparseVector,
|
|
365
340
|
options?.limit || this.config.defaultSearchLimit,
|
|
366
|
-
filter
|
|
341
|
+
filter
|
|
367
342
|
);
|
|
368
343
|
} else {
|
|
369
344
|
results = await this.qdrant.search(
|
|
370
345
|
collectionName,
|
|
371
346
|
embedding,
|
|
372
347
|
options?.limit || this.config.defaultSearchLimit,
|
|
373
|
-
filter
|
|
348
|
+
filter
|
|
374
349
|
);
|
|
375
350
|
}
|
|
376
351
|
|
|
@@ -406,19 +381,14 @@ export class GitHistoryIndexer {
|
|
|
406
381
|
}
|
|
407
382
|
|
|
408
383
|
// Check for indexing marker
|
|
409
|
-
const indexingMarker = await this.qdrant.getPoint(
|
|
410
|
-
collectionName,
|
|
411
|
-
GIT_INDEXING_METADATA_ID,
|
|
412
|
-
);
|
|
384
|
+
const indexingMarker = await this.qdrant.getPoint(collectionName, GIT_INDEXING_METADATA_ID);
|
|
413
385
|
const info = await this.qdrant.getCollectionInfo(collectionName);
|
|
414
386
|
|
|
415
387
|
const isComplete = indexingMarker?.payload?.indexingComplete === true;
|
|
416
388
|
const isInProgress = indexingMarker?.payload?.indexingComplete === false;
|
|
417
389
|
|
|
418
390
|
// Subtract 1 from points count if marker exists
|
|
419
|
-
const actualChunksCount = indexingMarker
|
|
420
|
-
? Math.max(0, info.pointsCount - 1)
|
|
421
|
-
: info.pointsCount;
|
|
391
|
+
const actualChunksCount = indexingMarker ? Math.max(0, info.pointsCount - 1) : info.pointsCount;
|
|
422
392
|
|
|
423
393
|
// Load snapshot for additional info
|
|
424
394
|
const synchronizer = new GitSynchronizer(absolutePath, collectionName);
|
|
@@ -439,12 +409,8 @@ export class GitHistoryIndexer {
|
|
|
439
409
|
status: "indexed",
|
|
440
410
|
collectionName,
|
|
441
411
|
chunksCount: actualChunksCount,
|
|
442
|
-
commitsCount: hasSnapshot
|
|
443
|
-
|
|
444
|
-
: undefined,
|
|
445
|
-
lastCommitHash: hasSnapshot
|
|
446
|
-
? (synchronizer.getLastCommitHash() ?? undefined)
|
|
447
|
-
: undefined,
|
|
412
|
+
commitsCount: hasSnapshot ? synchronizer.getCommitsIndexed() : undefined,
|
|
413
|
+
lastCommitHash: hasSnapshot ? (synchronizer.getLastCommitHash() ?? undefined) : undefined,
|
|
448
414
|
lastIndexedAt: hasSnapshot
|
|
449
415
|
? (synchronizer.getLastIndexedAt() ?? undefined)
|
|
450
416
|
: indexingMarker?.payload?.completedAt
|
|
@@ -460,12 +426,8 @@ export class GitHistoryIndexer {
|
|
|
460
426
|
status: "indexed",
|
|
461
427
|
collectionName,
|
|
462
428
|
chunksCount: actualChunksCount,
|
|
463
|
-
commitsCount: hasSnapshot
|
|
464
|
-
|
|
465
|
-
: undefined,
|
|
466
|
-
lastCommitHash: hasSnapshot
|
|
467
|
-
? (synchronizer.getLastCommitHash() ?? undefined)
|
|
468
|
-
: undefined,
|
|
429
|
+
commitsCount: hasSnapshot ? synchronizer.getCommitsIndexed() : undefined,
|
|
430
|
+
lastCommitHash: hasSnapshot ? (synchronizer.getLastCommitHash() ?? undefined) : undefined,
|
|
469
431
|
};
|
|
470
432
|
}
|
|
471
433
|
|
|
@@ -482,7 +444,7 @@ export class GitHistoryIndexer {
|
|
|
482
444
|
*/
|
|
483
445
|
async indexNewCommits(
|
|
484
446
|
path: string,
|
|
485
|
-
progressCallback?: GitProgressCallback
|
|
447
|
+
progressCallback?: GitProgressCallback
|
|
486
448
|
): Promise<GitChangeStats> {
|
|
487
449
|
const startTime = Date.now();
|
|
488
450
|
const stats: GitChangeStats = {
|
|
@@ -497,9 +459,7 @@ export class GitHistoryIndexer {
|
|
|
497
459
|
// Check if collection exists
|
|
498
460
|
const exists = await this.qdrant.collectionExists(collectionName);
|
|
499
461
|
if (!exists) {
|
|
500
|
-
throw new Error(
|
|
501
|
-
`Git history not indexed: ${path}. Use index_git_history first.`,
|
|
502
|
-
);
|
|
462
|
+
throw new Error(`Git history not indexed: ${path}. Use index_git_history first.`);
|
|
503
463
|
}
|
|
504
464
|
|
|
505
465
|
// Initialize synchronizer
|
|
@@ -507,9 +467,7 @@ export class GitHistoryIndexer {
|
|
|
507
467
|
const hasSnapshot = await synchronizer.initialize();
|
|
508
468
|
|
|
509
469
|
if (!hasSnapshot) {
|
|
510
|
-
throw new Error(
|
|
511
|
-
"No previous snapshot found. Use index_git_history for initial indexing.",
|
|
512
|
-
);
|
|
470
|
+
throw new Error("No previous snapshot found. Use index_git_history for initial indexing.");
|
|
513
471
|
}
|
|
514
472
|
|
|
515
473
|
const lastCommitHash = synchronizer.getLastCommitHash();
|
|
@@ -576,8 +534,7 @@ export class GitHistoryIndexer {
|
|
|
576
534
|
phase: "embedding",
|
|
577
535
|
current: i + batch.length,
|
|
578
536
|
total: allChunks.length,
|
|
579
|
-
percentage:
|
|
580
|
-
40 + Math.round(((i + batch.length) / allChunks.length) * 30),
|
|
537
|
+
percentage: 40 + Math.round(((i + batch.length) / allChunks.length) * 30),
|
|
581
538
|
message: `Generating embeddings ${i + batch.length}/${allChunks.length}`,
|
|
582
539
|
});
|
|
583
540
|
|
|
@@ -588,8 +545,7 @@ export class GitHistoryIndexer {
|
|
|
588
545
|
phase: "storing",
|
|
589
546
|
current: i + batch.length,
|
|
590
547
|
total: allChunks.length,
|
|
591
|
-
percentage:
|
|
592
|
-
70 + Math.round(((i + batch.length) / allChunks.length) * 30),
|
|
548
|
+
percentage: 70 + Math.round(((i + batch.length) / allChunks.length) * 30),
|
|
593
549
|
message: `Storing chunks ${i + batch.length}/${allChunks.length}`,
|
|
594
550
|
});
|
|
595
551
|
|
|
@@ -658,16 +614,12 @@ export class GitHistoryIndexer {
|
|
|
658
614
|
/**
|
|
659
615
|
* Store indexing status marker in the collection
|
|
660
616
|
*/
|
|
661
|
-
private async storeIndexingMarker(
|
|
662
|
-
collectionName: string,
|
|
663
|
-
complete: boolean,
|
|
664
|
-
): Promise<void> {
|
|
617
|
+
private async storeIndexingMarker(collectionName: string, complete: boolean): Promise<void> {
|
|
665
618
|
try {
|
|
666
619
|
const vectorSize = this.embeddings.getDimensions();
|
|
667
620
|
const zeroVector = new Array(vectorSize).fill(0);
|
|
668
621
|
|
|
669
|
-
const collectionInfo =
|
|
670
|
-
await this.qdrant.getCollectionInfo(collectionName);
|
|
622
|
+
const collectionInfo = await this.qdrant.getCollectionInfo(collectionName);
|
|
671
623
|
|
|
672
624
|
const payload = {
|
|
673
625
|
_type: "git_indexing_metadata",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
2
|
import { GitSynchronizer } from "./synchronizer.js";
|
|
3
3
|
|
|
4
4
|
// Mock fs module
|
|
@@ -51,9 +51,7 @@ describe("GitSynchronizer", () => {
|
|
|
51
51
|
});
|
|
52
52
|
|
|
53
53
|
it("should return false when snapshot does not exist", async () => {
|
|
54
|
-
vi.mocked(fs.readFile).mockRejectedValue(
|
|
55
|
-
new Error("ENOENT: no such file"),
|
|
56
|
-
);
|
|
54
|
+
vi.mocked(fs.readFile).mockRejectedValue(new Error("ENOENT: no such file"));
|
|
57
55
|
|
|
58
56
|
const result = await synchronizer.initialize();
|
|
59
57
|
|
|
@@ -157,14 +155,13 @@ describe("GitSynchronizer", () => {
|
|
|
157
155
|
|
|
158
156
|
await synchronizer.updateSnapshot("def456", 200);
|
|
159
157
|
|
|
160
|
-
expect(fs.mkdir).toHaveBeenCalledWith(
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
);
|
|
158
|
+
expect(fs.mkdir).toHaveBeenCalledWith(expect.stringContaining("git-snapshots"), {
|
|
159
|
+
recursive: true,
|
|
160
|
+
});
|
|
164
161
|
// Check the file was written with correct data (JSON may be formatted)
|
|
165
162
|
expect(fs.writeFile).toHaveBeenCalledWith(
|
|
166
163
|
expect.stringContaining(".tmp"),
|
|
167
|
-
expect.stringMatching(/lastCommitHash.*def456/s)
|
|
164
|
+
expect.stringMatching(/lastCommitHash.*def456/s)
|
|
168
165
|
);
|
|
169
166
|
expect(fs.rename).toHaveBeenCalled();
|
|
170
167
|
|
|
@@ -13,7 +13,7 @@ export class GitSynchronizer {
|
|
|
13
13
|
|
|
14
14
|
constructor(
|
|
15
15
|
private repoPath: string,
|
|
16
|
-
collectionName: string
|
|
16
|
+
collectionName: string
|
|
17
17
|
) {
|
|
18
18
|
// Store snapshots in ~/.qdrant-mcp/git-snapshots/
|
|
19
19
|
const snapshotDir = join(homedir(), ".qdrant-mcp", "git-snapshots");
|
|
@@ -67,10 +67,7 @@ export class GitSynchronizer {
|
|
|
67
67
|
/**
|
|
68
68
|
* Update snapshot with new indexing state
|
|
69
69
|
*/
|
|
70
|
-
async updateSnapshot(
|
|
71
|
-
lastCommitHash: string,
|
|
72
|
-
commitsIndexed: number,
|
|
73
|
-
): Promise<void> {
|
|
70
|
+
async updateSnapshot(lastCommitHash: string, commitsIndexed: number): Promise<void> {
|
|
74
71
|
this.snapshot = {
|
|
75
72
|
repoPath: this.repoPath,
|
|
76
73
|
lastCommitHash,
|
package/src/index.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe,
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
2
|
|
|
3
3
|
vi.mock("./qdrant/client.js");
|
|
4
4
|
vi.mock("./embeddings/openai.js");
|
|
@@ -31,9 +31,7 @@ describe("MCP Server Tool Schemas", () => {
|
|
|
31
31
|
name: "test-collection",
|
|
32
32
|
distance: "Cosine" as const,
|
|
33
33
|
};
|
|
34
|
-
expect(() =>
|
|
35
|
-
CreateCollectionSchema.parse(validInputWithDistance),
|
|
36
|
-
).not.toThrow();
|
|
34
|
+
expect(() => CreateCollectionSchema.parse(validInputWithDistance)).not.toThrow();
|
|
37
35
|
});
|
|
38
36
|
|
|
39
37
|
it("should reject invalid distance metric", async () => {
|
|
@@ -72,7 +70,7 @@ describe("MCP Server Tool Schemas", () => {
|
|
|
72
70
|
id: z.union([z.string(), z.number()]),
|
|
73
71
|
text: z.string(),
|
|
74
72
|
metadata: z.record(z.string(), z.any()).optional(),
|
|
75
|
-
})
|
|
73
|
+
})
|
|
76
74
|
),
|
|
77
75
|
});
|
|
78
76
|
|
|
@@ -97,7 +95,7 @@ describe("MCP Server Tool Schemas", () => {
|
|
|
97
95
|
id: z.union([z.string(), z.number()]),
|
|
98
96
|
text: z.string(),
|
|
99
97
|
metadata: z.record(z.string(), z.any()).optional(),
|
|
100
|
-
})
|
|
98
|
+
})
|
|
101
99
|
),
|
|
102
100
|
});
|
|
103
101
|
|
|
@@ -124,7 +122,7 @@ describe("MCP Server Tool Schemas", () => {
|
|
|
124
122
|
id: z.union([z.string(), z.number()]),
|
|
125
123
|
text: z.string(),
|
|
126
124
|
metadata: z.record(z.string(), z.any()).optional(),
|
|
127
|
-
})
|
|
125
|
+
})
|
|
128
126
|
),
|
|
129
127
|
});
|
|
130
128
|
|
|
@@ -292,7 +290,7 @@ describe("MCP Server Resource URIs", () => {
|
|
|
292
290
|
const match = collectionUri.match(/^qdrant:\/\/collection\/(.+)$/);
|
|
293
291
|
|
|
294
292
|
expect(match).not.toBeNull();
|
|
295
|
-
expect(match
|
|
293
|
+
expect(match?.[1]).toBe("my-collection");
|
|
296
294
|
});
|
|
297
295
|
|
|
298
296
|
it("should extract collection name from URI", () => {
|
|
@@ -304,7 +302,7 @@ describe("MCP Server Resource URIs", () => {
|
|
|
304
302
|
|
|
305
303
|
testCases.forEach(({ uri, expected }) => {
|
|
306
304
|
const match = uri.match(/^qdrant:\/\/collection\/(.+)$/);
|
|
307
|
-
expect(match
|
|
305
|
+
expect(match?.[1]).toBe(expected);
|
|
308
306
|
});
|
|
309
307
|
});
|
|
310
308
|
|