@mhalder/qdrant-mcp-server 1.3.1 → 1.5.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.
Files changed (118) hide show
  1. package/.codecov.yml +16 -0
  2. package/CHANGELOG.md +25 -0
  3. package/README.md +304 -9
  4. package/build/code/chunker/base.d.ts +19 -0
  5. package/build/code/chunker/base.d.ts.map +1 -0
  6. package/build/code/chunker/base.js +5 -0
  7. package/build/code/chunker/base.js.map +1 -0
  8. package/build/code/chunker/character-chunker.d.ts +22 -0
  9. package/build/code/chunker/character-chunker.d.ts.map +1 -0
  10. package/build/code/chunker/character-chunker.js +111 -0
  11. package/build/code/chunker/character-chunker.js.map +1 -0
  12. package/build/code/chunker/tree-sitter-chunker.d.ts +29 -0
  13. package/build/code/chunker/tree-sitter-chunker.d.ts.map +1 -0
  14. package/build/code/chunker/tree-sitter-chunker.js +213 -0
  15. package/build/code/chunker/tree-sitter-chunker.js.map +1 -0
  16. package/build/code/config.d.ts +11 -0
  17. package/build/code/config.d.ts.map +1 -0
  18. package/build/code/config.js +145 -0
  19. package/build/code/config.js.map +1 -0
  20. package/build/code/indexer.d.ts +42 -0
  21. package/build/code/indexer.d.ts.map +1 -0
  22. package/build/code/indexer.js +508 -0
  23. package/build/code/indexer.js.map +1 -0
  24. package/build/code/metadata.d.ts +32 -0
  25. package/build/code/metadata.d.ts.map +1 -0
  26. package/build/code/metadata.js +128 -0
  27. package/build/code/metadata.js.map +1 -0
  28. package/build/code/scanner.d.ts +35 -0
  29. package/build/code/scanner.d.ts.map +1 -0
  30. package/build/code/scanner.js +108 -0
  31. package/build/code/scanner.js.map +1 -0
  32. package/build/code/sync/merkle.d.ts +45 -0
  33. package/build/code/sync/merkle.d.ts.map +1 -0
  34. package/build/code/sync/merkle.js +116 -0
  35. package/build/code/sync/merkle.js.map +1 -0
  36. package/build/code/sync/snapshot.d.ts +41 -0
  37. package/build/code/sync/snapshot.d.ts.map +1 -0
  38. package/build/code/sync/snapshot.js +91 -0
  39. package/build/code/sync/snapshot.js.map +1 -0
  40. package/build/code/sync/synchronizer.d.ts +53 -0
  41. package/build/code/sync/synchronizer.d.ts.map +1 -0
  42. package/build/code/sync/synchronizer.js +132 -0
  43. package/build/code/sync/synchronizer.js.map +1 -0
  44. package/build/code/types.d.ts +98 -0
  45. package/build/code/types.d.ts.map +1 -0
  46. package/build/code/types.js +5 -0
  47. package/build/code/types.js.map +1 -0
  48. package/build/index.js +321 -6
  49. package/build/index.js.map +1 -1
  50. package/build/prompts/index.d.ts +7 -0
  51. package/build/prompts/index.d.ts.map +1 -0
  52. package/build/prompts/index.js +7 -0
  53. package/build/prompts/index.js.map +1 -0
  54. package/build/prompts/index.test.d.ts +2 -0
  55. package/build/prompts/index.test.d.ts.map +1 -0
  56. package/build/prompts/index.test.js +25 -0
  57. package/build/prompts/index.test.js.map +1 -0
  58. package/build/prompts/loader.d.ts +25 -0
  59. package/build/prompts/loader.d.ts.map +1 -0
  60. package/build/prompts/loader.js +81 -0
  61. package/build/prompts/loader.js.map +1 -0
  62. package/build/prompts/loader.test.d.ts +2 -0
  63. package/build/prompts/loader.test.d.ts.map +1 -0
  64. package/build/prompts/loader.test.js +417 -0
  65. package/build/prompts/loader.test.js.map +1 -0
  66. package/build/prompts/template.d.ts +20 -0
  67. package/build/prompts/template.d.ts.map +1 -0
  68. package/build/prompts/template.js +52 -0
  69. package/build/prompts/template.js.map +1 -0
  70. package/build/prompts/template.test.d.ts +2 -0
  71. package/build/prompts/template.test.d.ts.map +1 -0
  72. package/build/prompts/template.test.js +163 -0
  73. package/build/prompts/template.test.js.map +1 -0
  74. package/build/prompts/types.d.ts +34 -0
  75. package/build/prompts/types.d.ts.map +1 -0
  76. package/build/prompts/types.js +5 -0
  77. package/build/prompts/types.js.map +1 -0
  78. package/examples/code-search/README.md +271 -0
  79. package/package.json +13 -1
  80. package/prompts.example.json +96 -0
  81. package/src/code/chunker/base.ts +22 -0
  82. package/src/code/chunker/character-chunker.ts +131 -0
  83. package/src/code/chunker/tree-sitter-chunker.ts +250 -0
  84. package/src/code/config.ts +156 -0
  85. package/src/code/indexer.ts +613 -0
  86. package/src/code/metadata.ts +153 -0
  87. package/src/code/scanner.ts +124 -0
  88. package/src/code/sync/merkle.ts +136 -0
  89. package/src/code/sync/snapshot.ts +110 -0
  90. package/src/code/sync/synchronizer.ts +154 -0
  91. package/src/code/types.ts +117 -0
  92. package/src/index.ts +382 -5
  93. package/src/prompts/index.test.ts +29 -0
  94. package/src/prompts/index.ts +7 -0
  95. package/src/prompts/loader.test.ts +494 -0
  96. package/src/prompts/loader.ts +90 -0
  97. package/src/prompts/template.test.ts +212 -0
  98. package/src/prompts/template.ts +69 -0
  99. package/src/prompts/types.ts +37 -0
  100. package/tests/code/chunker/character-chunker.test.ts +141 -0
  101. package/tests/code/chunker/tree-sitter-chunker.test.ts +275 -0
  102. package/tests/code/fixtures/sample-py/calculator.py +32 -0
  103. package/tests/code/fixtures/sample-ts/async-operations.ts +120 -0
  104. package/tests/code/fixtures/sample-ts/auth.ts +31 -0
  105. package/tests/code/fixtures/sample-ts/config.ts +52 -0
  106. package/tests/code/fixtures/sample-ts/database.ts +50 -0
  107. package/tests/code/fixtures/sample-ts/index.ts +39 -0
  108. package/tests/code/fixtures/sample-ts/types-advanced.ts +132 -0
  109. package/tests/code/fixtures/sample-ts/utils.ts +105 -0
  110. package/tests/code/fixtures/sample-ts/validator.ts +169 -0
  111. package/tests/code/indexer.test.ts +828 -0
  112. package/tests/code/integration.test.ts +708 -0
  113. package/tests/code/metadata.test.ts +457 -0
  114. package/tests/code/scanner.test.ts +131 -0
  115. package/tests/code/sync/merkle.test.ts +406 -0
  116. package/tests/code/sync/snapshot.test.ts +360 -0
  117. package/tests/code/sync/synchronizer.test.ts +501 -0
  118. package/vitest.config.ts +1 -0
@@ -0,0 +1,128 @@
1
+ /**
2
+ * MetadataExtractor - Extracts metadata from code chunks
3
+ */
4
+ import { createHash } from "node:crypto";
5
+ import { extname } from "node:path";
6
+ import { LANGUAGE_MAP } from "./config.js";
7
+ export class MetadataExtractor {
8
+ /**
9
+ * Extract programming language from file path
10
+ */
11
+ extractLanguage(filePath) {
12
+ const ext = extname(filePath);
13
+ return LANGUAGE_MAP[ext] || "unknown";
14
+ }
15
+ /**
16
+ * Generate deterministic chunk ID based on content and location
17
+ * Format: chunk_{sha256(path:start:end:content)[:16]}
18
+ */
19
+ generateChunkId(chunk) {
20
+ const { metadata, startLine, endLine, content } = chunk;
21
+ const combined = `${metadata.filePath}:${startLine}:${endLine}:${content}`;
22
+ const hash = createHash("sha256").update(combined).digest("hex");
23
+ return `chunk_${hash.substring(0, 16)}`;
24
+ }
25
+ /**
26
+ * Calculate simple code complexity score (optional)
27
+ * Based on: cyclomatic complexity indicators
28
+ */
29
+ calculateComplexity(code) {
30
+ if (!code || code.trim().length === 0) {
31
+ return 0;
32
+ }
33
+ let complexity = 0;
34
+ // Count control flow statements
35
+ const controlFlowPatterns = [
36
+ /\bif\b/g,
37
+ /\belse\b/g,
38
+ /\bfor\b/g,
39
+ /\bwhile\b/g,
40
+ /\bswitch\b/g,
41
+ /\bcase\b/g,
42
+ /\bcatch\b/g,
43
+ /&&/g,
44
+ /\|\|/g,
45
+ /\?[^?]/g, // Ternary operator
46
+ ];
47
+ for (const pattern of controlFlowPatterns) {
48
+ const matches = code.match(pattern);
49
+ if (matches) {
50
+ complexity += matches.length;
51
+ }
52
+ }
53
+ // If code contains function/method/class, add base complexity of 1
54
+ if (complexity > 0 || /\b(function|class|def|fn)\b/.test(code)) {
55
+ complexity = Math.max(1, complexity);
56
+ }
57
+ return complexity;
58
+ }
59
+ /**
60
+ * Detect potential secrets in code (basic pattern matching)
61
+ */
62
+ containsSecrets(code) {
63
+ const secretPatterns = [
64
+ /(?:api[-_]?key|apikey)\s*=\s*['"][^'"]{20,}['"]/i,
65
+ /(?:secret|SECRET)\s*=\s*['"][^'"]{20,}['"]/i,
66
+ /(?:password|PASSWORD)\s*=\s*['"][^'"]{8,}['"]/i,
67
+ /(?:token|TOKEN)\s*=\s*['"][^'"]{20,}['"]/i,
68
+ /-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----/,
69
+ /sk_live_[a-zA-Z0-9]{24,}/, // Stripe secret key
70
+ /ghp_[a-zA-Z0-9]{36,}/, // GitHub personal access token
71
+ /AIza[0-9A-Za-z\\-_]{35}/, // Google API key
72
+ /AKIA[0-9A-Z]{16}/, // AWS access key
73
+ ];
74
+ for (const pattern of secretPatterns) {
75
+ if (pattern.test(code)) {
76
+ return true;
77
+ }
78
+ }
79
+ return false;
80
+ }
81
+ /**
82
+ * Extract imports/exports from code (basic regex-based)
83
+ */
84
+ extractImportsExports(code, language) {
85
+ const imports = [];
86
+ const exports = [];
87
+ if (language === "typescript" || language === "javascript") {
88
+ // Extract imports
89
+ const importMatches = code.matchAll(/import\s+.*?\s+from\s+['"]([^'"]+)['"]/g);
90
+ for (const match of importMatches) {
91
+ imports.push(match[1]);
92
+ }
93
+ // Extract require statements
94
+ const requireMatches = code.matchAll(/require\s*\(\s*['"]([^'"]+)['"]\s*\)/g);
95
+ for (const match of requireMatches) {
96
+ imports.push(match[1]);
97
+ }
98
+ // Extract exports - regular declarations
99
+ const exportMatches = code.matchAll(/export\s+(?:class|function|const|let|var)\s+(\w+)/g);
100
+ for (const match of exportMatches) {
101
+ exports.push(match[1]);
102
+ }
103
+ // Extract export default
104
+ if (/export\s+default\b/.test(code)) {
105
+ exports.push("default");
106
+ }
107
+ // Extract named exports from other modules: export { name } from 'module'
108
+ const reExportMatches = code.matchAll(/export\s+\{\s*(\w+)\s*\}/g);
109
+ for (const match of reExportMatches) {
110
+ exports.push(match[1]);
111
+ }
112
+ }
113
+ else if (language === "python") {
114
+ // Extract imports
115
+ const importMatches = code.matchAll(/(?:from\s+(\S+)\s+)?import\s+([^;\n]+)/g);
116
+ for (const match of importMatches) {
117
+ imports.push(match[1] || match[2]);
118
+ }
119
+ // Extract functions/classes (rough)
120
+ const defMatches = code.matchAll(/^(?:def|class)\s+(\w+)/gm);
121
+ for (const match of defMatches) {
122
+ exports.push(match[1]);
123
+ }
124
+ }
125
+ return { imports, exports };
126
+ }
127
+ }
128
+ //# sourceMappingURL=metadata.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/code/metadata.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * FileScanner - Discovers code files in a directory while respecting ignore patterns
3
+ */
4
+ import type { ScannerConfig } from "./types.js";
5
+ export declare class FileScanner {
6
+ private config;
7
+ private ig;
8
+ private supportedExts;
9
+ constructor(config: ScannerConfig);
10
+ /**
11
+ * Load ignore patterns from .gitignore, .dockerignore, .npmignore, and .contextignore
12
+ */
13
+ loadIgnorePatterns(rootPath: string): Promise<void>;
14
+ /**
15
+ * Scan directory recursively and return all code files
16
+ */
17
+ scanDirectory(rootPath: string): Promise<string[]>;
18
+ /**
19
+ * Check if a file should be ignored based on patterns
20
+ */
21
+ shouldIgnore(filePath: string, rootPath: string): boolean;
22
+ /**
23
+ * Get list of supported file extensions
24
+ */
25
+ getSupportedExtensions(): string[];
26
+ /**
27
+ * Recursively walk directory tree
28
+ */
29
+ private walkDirectory;
30
+ /**
31
+ * Check if a file exists
32
+ */
33
+ private fileExists;
34
+ }
35
+ //# sourceMappingURL=scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/code/scanner.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,qBAAa,WAAW;IAIV,OAAO,CAAC,MAAM;IAH1B,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,aAAa,CAAc;gBAEf,MAAM,EAAE,aAAa;IAIzC;;OAEG;IACG,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BzD;;OAEG;IACG,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IASxD;;OAEG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO;IAKzD;;OAEG;IACH,sBAAsB,IAAI,MAAM,EAAE;IAIlC;;OAEG;YACW,aAAa;IAoC3B;;OAEG;YACW,UAAU;CAQzB"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * FileScanner - Discovers code files in a directory while respecting ignore patterns
3
+ */
4
+ import { promises as fs } from "node:fs";
5
+ import { extname, join, relative, resolve } from "node:path";
6
+ import ignore from "ignore";
7
+ export class FileScanner {
8
+ config;
9
+ ig = ignore();
10
+ supportedExts;
11
+ constructor(config) {
12
+ this.config = config;
13
+ this.supportedExts = new Set(config.supportedExtensions);
14
+ }
15
+ /**
16
+ * Load ignore patterns from .gitignore, .dockerignore, .npmignore, and .contextignore
17
+ */
18
+ async loadIgnorePatterns(rootPath) {
19
+ const ignoreFiles = [".gitignore", ".dockerignore", ".npmignore", ".contextignore"];
20
+ for (const ignoreFile of ignoreFiles) {
21
+ const ignorePath = join(rootPath, ignoreFile);
22
+ if (await this.fileExists(ignorePath)) {
23
+ try {
24
+ const content = await fs.readFile(ignorePath, "utf-8");
25
+ this.ig.add(content);
26
+ }
27
+ catch (_error) {
28
+ // Silently skip if file can't be read
29
+ }
30
+ }
31
+ }
32
+ // Add default patterns from config
33
+ if (this.config.ignorePatterns && this.config.ignorePatterns.length > 0) {
34
+ this.ig.add(this.config.ignorePatterns);
35
+ }
36
+ // Add custom patterns
37
+ if (this.config.customIgnorePatterns && this.config.customIgnorePatterns.length > 0) {
38
+ this.ig.add(this.config.customIgnorePatterns);
39
+ }
40
+ }
41
+ /**
42
+ * Scan directory recursively and return all code files
43
+ */
44
+ async scanDirectory(rootPath) {
45
+ const absoluteRoot = resolve(rootPath);
46
+ const files = [];
47
+ await this.walkDirectory(absoluteRoot, absoluteRoot, files);
48
+ return files;
49
+ }
50
+ /**
51
+ * Check if a file should be ignored based on patterns
52
+ */
53
+ shouldIgnore(filePath, rootPath) {
54
+ const relativePath = relative(rootPath, filePath);
55
+ return this.ig.ignores(relativePath);
56
+ }
57
+ /**
58
+ * Get list of supported file extensions
59
+ */
60
+ getSupportedExtensions() {
61
+ return Array.from(this.supportedExts);
62
+ }
63
+ /**
64
+ * Recursively walk directory tree
65
+ */
66
+ async walkDirectory(currentPath, rootPath, files) {
67
+ try {
68
+ const entries = await fs.readdir(currentPath, { withFileTypes: true });
69
+ for (const entry of entries) {
70
+ const fullPath = join(currentPath, entry.name);
71
+ const relativePath = relative(rootPath, fullPath);
72
+ // Skip ignored paths
73
+ if (this.ig.ignores(relativePath)) {
74
+ continue;
75
+ }
76
+ // Handle symbolic links safely to avoid infinite loops
77
+ if (entry.isSymbolicLink()) {
78
+ continue;
79
+ }
80
+ if (entry.isDirectory()) {
81
+ await this.walkDirectory(fullPath, rootPath, files);
82
+ }
83
+ else if (entry.isFile()) {
84
+ const ext = extname(entry.name);
85
+ if (this.supportedExts.has(ext)) {
86
+ files.push(fullPath);
87
+ }
88
+ }
89
+ }
90
+ }
91
+ catch (_error) {
92
+ // Skip directories that can't be read (permission errors, etc.)
93
+ }
94
+ }
95
+ /**
96
+ * Check if a file exists
97
+ */
98
+ async fileExists(path) {
99
+ try {
100
+ await fs.access(path);
101
+ return true;
102
+ }
103
+ catch {
104
+ return false;
105
+ }
106
+ }
107
+ }
108
+ //# sourceMappingURL=scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/code/scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * MerkleTree - Efficient change detection using Merkle trees
3
+ * Enables incremental updates by comparing file hashes
4
+ */
5
+ export declare class MerkleNode {
6
+ hash: string;
7
+ left?: MerkleNode | undefined;
8
+ right?: MerkleNode | undefined;
9
+ constructor(hash: string, left?: MerkleNode | undefined, right?: MerkleNode | undefined);
10
+ }
11
+ export declare class MerkleTree {
12
+ root: MerkleNode | undefined;
13
+ /**
14
+ * Build Merkle tree from file hashes
15
+ * @param fileHashes Map of file path to content hash
16
+ */
17
+ build(fileHashes: Map<string, string>): void;
18
+ /**
19
+ * Recursively build tree from leaf nodes
20
+ */
21
+ private buildRecursive;
22
+ /**
23
+ * Compare two trees and return file differences
24
+ */
25
+ static compare(oldHashes: Map<string, string>, newHashes: Map<string, string>): {
26
+ added: string[];
27
+ modified: string[];
28
+ deleted: string[];
29
+ };
30
+ /**
31
+ * Get root hash (quick comparison)
32
+ */
33
+ getRootHash(): string | undefined;
34
+ /**
35
+ * Serialize tree for storage
36
+ */
37
+ serialize(): string;
38
+ private serializeNode;
39
+ /**
40
+ * Deserialize tree from storage
41
+ */
42
+ static deserialize(data: string): MerkleTree;
43
+ private deserializeNode;
44
+ }
45
+ //# sourceMappingURL=merkle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merkle.d.ts","sourceRoot":"","sources":["../../../src/code/sync/merkle.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,qBAAa,UAAU;IAEZ,IAAI,EAAE,MAAM;IACZ,IAAI,CAAC,EAAE,UAAU;IACjB,KAAK,CAAC,EAAE,UAAU;gBAFlB,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,UAAU,YAAA,EACjB,KAAK,CAAC,EAAE,UAAU,YAAA;CAE5B;AAED,qBAAa,UAAU;IACrB,IAAI,EAAE,UAAU,GAAG,SAAS,CAAa;IAEzC;;;OAGG;IACH,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAkB5C;;OAEG;IACH,OAAO,CAAC,cAAc;IAmBtB;;OAEG;IACH,MAAM,CAAC,OAAO,CACZ,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE;IAwB7D;;OAEG;IACH,WAAW,IAAI,MAAM,GAAG,SAAS;IAIjC;;OAEG;IACH,SAAS,IAAI,MAAM;IAMnB,OAAO,CAAC,aAAa;IASrB;;OAEG;IACH,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU;IAO5C,OAAO,CAAC,eAAe;CAQxB"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * MerkleTree - Efficient change detection using Merkle trees
3
+ * Enables incremental updates by comparing file hashes
4
+ */
5
+ import { createHash } from "node:crypto";
6
+ export class MerkleNode {
7
+ hash;
8
+ left;
9
+ right;
10
+ constructor(hash, left, right) {
11
+ this.hash = hash;
12
+ this.left = left;
13
+ this.right = right;
14
+ }
15
+ }
16
+ export class MerkleTree {
17
+ root = undefined;
18
+ /**
19
+ * Build Merkle tree from file hashes
20
+ * @param fileHashes Map of file path to content hash
21
+ */
22
+ build(fileHashes) {
23
+ if (fileHashes.size === 0) {
24
+ this.root = undefined;
25
+ return;
26
+ }
27
+ // Sort files for deterministic tree structure
28
+ const leaves = Array.from(fileHashes.entries())
29
+ .sort(([pathA], [pathB]) => pathA.localeCompare(pathB))
30
+ .map(([path, hash]) => {
31
+ const combined = `${path}:${hash}`;
32
+ const leafHash = createHash("sha256").update(combined).digest("hex");
33
+ return new MerkleNode(leafHash);
34
+ });
35
+ this.root = this.buildRecursive(leaves);
36
+ }
37
+ /**
38
+ * Recursively build tree from leaf nodes
39
+ */
40
+ buildRecursive(nodes) {
41
+ if (nodes.length === 1) {
42
+ return nodes[0];
43
+ }
44
+ const parents = [];
45
+ for (let i = 0; i < nodes.length; i += 2) {
46
+ const left = nodes[i];
47
+ const right = nodes[i + 1] || left; // Duplicate if odd number
48
+ const combined = left.hash + right.hash;
49
+ const parentHash = createHash("sha256").update(combined).digest("hex");
50
+ parents.push(new MerkleNode(parentHash, left, right));
51
+ }
52
+ return this.buildRecursive(parents);
53
+ }
54
+ /**
55
+ * Compare two trees and return file differences
56
+ */
57
+ static compare(oldHashes, newHashes) {
58
+ const added = [];
59
+ const modified = [];
60
+ const deleted = [];
61
+ // Find added and modified files
62
+ for (const [path, hash] of newHashes) {
63
+ if (!oldHashes.has(path)) {
64
+ added.push(path);
65
+ }
66
+ else if (oldHashes.get(path) !== hash) {
67
+ modified.push(path);
68
+ }
69
+ }
70
+ // Find deleted files
71
+ for (const [path] of oldHashes) {
72
+ if (!newHashes.has(path)) {
73
+ deleted.push(path);
74
+ }
75
+ }
76
+ return { added, modified, deleted };
77
+ }
78
+ /**
79
+ * Get root hash (quick comparison)
80
+ */
81
+ getRootHash() {
82
+ return this.root?.hash;
83
+ }
84
+ /**
85
+ * Serialize tree for storage
86
+ */
87
+ serialize() {
88
+ return JSON.stringify({
89
+ root: this.serializeNode(this.root),
90
+ });
91
+ }
92
+ serializeNode(node) {
93
+ if (!node)
94
+ return null;
95
+ return {
96
+ hash: node.hash,
97
+ left: this.serializeNode(node.left),
98
+ right: this.serializeNode(node.right),
99
+ };
100
+ }
101
+ /**
102
+ * Deserialize tree from storage
103
+ */
104
+ static deserialize(data) {
105
+ const tree = new MerkleTree();
106
+ const obj = JSON.parse(data);
107
+ tree.root = tree.deserializeNode(obj.root);
108
+ return tree;
109
+ }
110
+ deserializeNode(obj) {
111
+ if (!obj)
112
+ return undefined;
113
+ return new MerkleNode(obj.hash, this.deserializeNode(obj.left), this.deserializeNode(obj.right));
114
+ }
115
+ }
116
+ //# sourceMappingURL=merkle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merkle.js","sourceRoot":"","sources":["../../../src/code/sync/merkle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Snapshot - Persistence layer for Merkle tree snapshots
3
+ * Stores file hashes and tree structure for incremental updates
4
+ */
5
+ import { MerkleTree } from "./merkle.js";
6
+ export interface Snapshot {
7
+ codebasePath: string;
8
+ timestamp: number;
9
+ fileHashes: Record<string, string>;
10
+ merkleTree: string;
11
+ }
12
+ export declare class SnapshotManager {
13
+ private snapshotPath;
14
+ constructor(snapshotPath: string);
15
+ /**
16
+ * Save snapshot to disk
17
+ */
18
+ save(codebasePath: string, fileHashes: Map<string, string>, tree: MerkleTree): Promise<void>;
19
+ /**
20
+ * Load snapshot from disk
21
+ */
22
+ load(): Promise<{
23
+ codebasePath: string;
24
+ fileHashes: Map<string, string>;
25
+ merkleTree: MerkleTree;
26
+ timestamp: number;
27
+ } | null>;
28
+ /**
29
+ * Check if snapshot exists
30
+ */
31
+ exists(): Promise<boolean>;
32
+ /**
33
+ * Delete snapshot
34
+ */
35
+ delete(): Promise<void>;
36
+ /**
37
+ * Validate snapshot (check for corruption)
38
+ */
39
+ validate(): Promise<boolean>;
40
+ }
41
+ //# sourceMappingURL=snapshot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../../src/code/sync/snapshot.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,WAAW,QAAQ;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,eAAe;IACd,OAAO,CAAC,YAAY;gBAAZ,YAAY,EAAE,MAAM;IAExC;;OAEG;IACG,IAAI,CACR,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,IAAI,CAAC;IAkBhB;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,UAAU,EAAE,UAAU,CAAC;QACvB,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,IAAI,CAAC;IAoBT;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAShC;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ7B;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC;CAWnC"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Snapshot - Persistence layer for Merkle tree snapshots
3
+ * Stores file hashes and tree structure for incremental updates
4
+ */
5
+ import { promises as fs } from "node:fs";
6
+ import { dirname } from "node:path";
7
+ import { MerkleTree } from "./merkle.js";
8
+ export class SnapshotManager {
9
+ snapshotPath;
10
+ constructor(snapshotPath) {
11
+ this.snapshotPath = snapshotPath;
12
+ }
13
+ /**
14
+ * Save snapshot to disk
15
+ */
16
+ async save(codebasePath, fileHashes, tree) {
17
+ const snapshot = {
18
+ codebasePath,
19
+ timestamp: Date.now(),
20
+ fileHashes: Object.fromEntries(fileHashes),
21
+ merkleTree: tree.serialize(),
22
+ };
23
+ // Ensure directory exists
24
+ await fs.mkdir(dirname(this.snapshotPath), { recursive: true });
25
+ // Write snapshot atomically (write to temp file, then rename)
26
+ // Use unique temp file name to avoid race conditions
27
+ const tempPath = `${this.snapshotPath}.tmp.${Date.now()}.${Math.random().toString(36).substring(2, 9)}`;
28
+ await fs.writeFile(tempPath, JSON.stringify(snapshot, null, 2), "utf-8");
29
+ await fs.rename(tempPath, this.snapshotPath);
30
+ }
31
+ /**
32
+ * Load snapshot from disk
33
+ */
34
+ async load() {
35
+ try {
36
+ const data = await fs.readFile(this.snapshotPath, "utf-8");
37
+ const snapshot = JSON.parse(data);
38
+ const fileHashes = new Map(Object.entries(snapshot.fileHashes));
39
+ const tree = MerkleTree.deserialize(snapshot.merkleTree);
40
+ return {
41
+ codebasePath: snapshot.codebasePath,
42
+ fileHashes,
43
+ merkleTree: tree,
44
+ timestamp: snapshot.timestamp,
45
+ };
46
+ }
47
+ catch (_error) {
48
+ // Snapshot doesn't exist or is corrupted
49
+ return null;
50
+ }
51
+ }
52
+ /**
53
+ * Check if snapshot exists
54
+ */
55
+ async exists() {
56
+ try {
57
+ await fs.access(this.snapshotPath);
58
+ return true;
59
+ }
60
+ catch {
61
+ return false;
62
+ }
63
+ }
64
+ /**
65
+ * Delete snapshot
66
+ */
67
+ async delete() {
68
+ try {
69
+ await fs.unlink(this.snapshotPath);
70
+ }
71
+ catch {
72
+ // Ignore if doesn't exist
73
+ }
74
+ }
75
+ /**
76
+ * Validate snapshot (check for corruption)
77
+ */
78
+ async validate() {
79
+ try {
80
+ const snapshot = await this.load();
81
+ if (!snapshot)
82
+ return false;
83
+ // Basic validation: check if tree can be deserialized
84
+ return snapshot.merkleTree.getRootHash() !== undefined || snapshot.fileHashes.size === 0;
85
+ }
86
+ catch {
87
+ return false;
88
+ }
89
+ }
90
+ }
91
+ //# sourceMappingURL=snapshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../../src/code/sync/snapshot.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * FileSynchronizer - Manages incremental updates using Merkle trees
3
+ * Detects file changes and updates snapshots
4
+ */
5
+ import type { FileChanges } from "../types.js";
6
+ export declare class FileSynchronizer {
7
+ private codebasePath;
8
+ private snapshotManager;
9
+ private previousHashes;
10
+ private previousTree;
11
+ constructor(codebasePath: string, collectionName: string);
12
+ /**
13
+ * Initialize synchronizer by loading previous snapshot
14
+ */
15
+ initialize(): Promise<boolean>;
16
+ /**
17
+ * Compute hash for a file's content
18
+ */
19
+ private hashFile;
20
+ /**
21
+ * Compute hashes for all files
22
+ */
23
+ computeFileHashes(filePaths: string[]): Promise<Map<string, string>>;
24
+ /**
25
+ * Detect changes since last snapshot
26
+ */
27
+ detectChanges(currentFiles: string[]): Promise<FileChanges>;
28
+ /**
29
+ * Update snapshot with current state
30
+ */
31
+ updateSnapshot(files: string[]): Promise<void>;
32
+ /**
33
+ * Delete snapshot
34
+ */
35
+ deleteSnapshot(): Promise<void>;
36
+ /**
37
+ * Check if snapshot exists
38
+ */
39
+ hasSnapshot(): Promise<boolean>;
40
+ /**
41
+ * Validate snapshot integrity
42
+ */
43
+ validateSnapshot(): Promise<boolean>;
44
+ /**
45
+ * Get snapshot age in milliseconds
46
+ */
47
+ getSnapshotAge(): Promise<number | null>;
48
+ /**
49
+ * Quick check if re-indexing is needed (compare root hashes)
50
+ */
51
+ needsReindex(currentFiles: string[]): Promise<boolean>;
52
+ }
53
+ //# sourceMappingURL=synchronizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"synchronizer.d.ts","sourceRoot":"","sources":["../../../src/code/sync/synchronizer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,qBAAa,gBAAgB;IAMzB,OAAO,CAAC,YAAY;IALtB,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,cAAc,CAAkC;IACxD,OAAO,CAAC,YAAY,CAA2B;gBAGrC,YAAY,EAAE,MAAM,EAC5B,cAAc,EAAE,MAAM;IAQxB;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;IAYpC;;OAEG;YACW,QAAQ;IActB;;OAEG;IACG,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAiB1E;;OAEG;IACG,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAUjE;;OAEG;IACG,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAYpD;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAMrC;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAIrC;;OAEG;IACG,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC;IAI1C;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAO9C;;OAEG;IACG,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;CAS7D"}