@zuvia-software-solutions/code-mapper 1.4.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 (213) hide show
  1. package/README.md +215 -0
  2. package/dist/cli/ai-context.d.ts +19 -0
  3. package/dist/cli/ai-context.js +168 -0
  4. package/dist/cli/analyze.d.ts +7 -0
  5. package/dist/cli/analyze.js +325 -0
  6. package/dist/cli/augment.d.ts +7 -0
  7. package/dist/cli/augment.js +27 -0
  8. package/dist/cli/clean.d.ts +5 -0
  9. package/dist/cli/clean.js +56 -0
  10. package/dist/cli/eval-server.d.ts +25 -0
  11. package/dist/cli/eval-server.js +365 -0
  12. package/dist/cli/index.d.ts +6 -0
  13. package/dist/cli/index.js +102 -0
  14. package/dist/cli/lazy-action.d.ts +6 -0
  15. package/dist/cli/lazy-action.js +19 -0
  16. package/dist/cli/list.d.ts +2 -0
  17. package/dist/cli/list.js +27 -0
  18. package/dist/cli/mcp.d.ts +8 -0
  19. package/dist/cli/mcp.js +35 -0
  20. package/dist/cli/refresh.d.ts +12 -0
  21. package/dist/cli/refresh.js +165 -0
  22. package/dist/cli/serve.d.ts +5 -0
  23. package/dist/cli/serve.js +8 -0
  24. package/dist/cli/setup.d.ts +6 -0
  25. package/dist/cli/setup.js +218 -0
  26. package/dist/cli/status.d.ts +2 -0
  27. package/dist/cli/status.js +33 -0
  28. package/dist/cli/tool.d.ts +28 -0
  29. package/dist/cli/tool.js +87 -0
  30. package/dist/config/ignore-service.d.ts +32 -0
  31. package/dist/config/ignore-service.js +282 -0
  32. package/dist/config/supported-languages.d.ts +23 -0
  33. package/dist/config/supported-languages.js +52 -0
  34. package/dist/core/augmentation/engine.d.ts +22 -0
  35. package/dist/core/augmentation/engine.js +232 -0
  36. package/dist/core/embeddings/embedder.d.ts +35 -0
  37. package/dist/core/embeddings/embedder.js +171 -0
  38. package/dist/core/embeddings/embedding-pipeline.d.ts +41 -0
  39. package/dist/core/embeddings/embedding-pipeline.js +402 -0
  40. package/dist/core/embeddings/index.d.ts +5 -0
  41. package/dist/core/embeddings/index.js +6 -0
  42. package/dist/core/embeddings/text-generator.d.ts +20 -0
  43. package/dist/core/embeddings/text-generator.js +159 -0
  44. package/dist/core/embeddings/types.d.ts +60 -0
  45. package/dist/core/embeddings/types.js +23 -0
  46. package/dist/core/graph/graph.d.ts +4 -0
  47. package/dist/core/graph/graph.js +65 -0
  48. package/dist/core/graph/types.d.ts +69 -0
  49. package/dist/core/graph/types.js +3 -0
  50. package/dist/core/incremental/child-process.d.ts +8 -0
  51. package/dist/core/incremental/child-process.js +649 -0
  52. package/dist/core/incremental/refresh-coordinator.d.ts +32 -0
  53. package/dist/core/incremental/refresh-coordinator.js +147 -0
  54. package/dist/core/incremental/types.d.ts +78 -0
  55. package/dist/core/incremental/types.js +153 -0
  56. package/dist/core/incremental/watcher.d.ts +63 -0
  57. package/dist/core/incremental/watcher.js +338 -0
  58. package/dist/core/ingestion/ast-cache.d.ts +12 -0
  59. package/dist/core/ingestion/ast-cache.js +34 -0
  60. package/dist/core/ingestion/call-processor.d.ts +34 -0
  61. package/dist/core/ingestion/call-processor.js +937 -0
  62. package/dist/core/ingestion/call-routing.d.ts +40 -0
  63. package/dist/core/ingestion/call-routing.js +97 -0
  64. package/dist/core/ingestion/cluster-enricher.d.ts +30 -0
  65. package/dist/core/ingestion/cluster-enricher.js +151 -0
  66. package/dist/core/ingestion/community-processor.d.ts +26 -0
  67. package/dist/core/ingestion/community-processor.js +272 -0
  68. package/dist/core/ingestion/constants.d.ts +5 -0
  69. package/dist/core/ingestion/constants.js +8 -0
  70. package/dist/core/ingestion/entry-point-scoring.d.ts +23 -0
  71. package/dist/core/ingestion/entry-point-scoring.js +317 -0
  72. package/dist/core/ingestion/export-detection.d.ts +11 -0
  73. package/dist/core/ingestion/export-detection.js +203 -0
  74. package/dist/core/ingestion/filesystem-walker.d.ts +18 -0
  75. package/dist/core/ingestion/filesystem-walker.js +64 -0
  76. package/dist/core/ingestion/framework-detection.d.ts +42 -0
  77. package/dist/core/ingestion/framework-detection.js +405 -0
  78. package/dist/core/ingestion/heritage-processor.d.ts +15 -0
  79. package/dist/core/ingestion/heritage-processor.js +237 -0
  80. package/dist/core/ingestion/import-processor.d.ts +31 -0
  81. package/dist/core/ingestion/import-processor.js +416 -0
  82. package/dist/core/ingestion/language-config.d.ts +32 -0
  83. package/dist/core/ingestion/language-config.js +161 -0
  84. package/dist/core/ingestion/mro-processor.d.ts +32 -0
  85. package/dist/core/ingestion/mro-processor.js +343 -0
  86. package/dist/core/ingestion/named-binding-extraction.d.ts +51 -0
  87. package/dist/core/ingestion/named-binding-extraction.js +343 -0
  88. package/dist/core/ingestion/parsing-processor.d.ts +20 -0
  89. package/dist/core/ingestion/parsing-processor.js +282 -0
  90. package/dist/core/ingestion/pipeline.d.ts +3 -0
  91. package/dist/core/ingestion/pipeline.js +416 -0
  92. package/dist/core/ingestion/process-processor.d.ts +42 -0
  93. package/dist/core/ingestion/process-processor.js +357 -0
  94. package/dist/core/ingestion/resolution-context.d.ts +40 -0
  95. package/dist/core/ingestion/resolution-context.js +171 -0
  96. package/dist/core/ingestion/resolvers/csharp.d.ts +10 -0
  97. package/dist/core/ingestion/resolvers/csharp.js +101 -0
  98. package/dist/core/ingestion/resolvers/go.d.ts +8 -0
  99. package/dist/core/ingestion/resolvers/go.js +33 -0
  100. package/dist/core/ingestion/resolvers/index.d.ts +14 -0
  101. package/dist/core/ingestion/resolvers/index.js +10 -0
  102. package/dist/core/ingestion/resolvers/jvm.d.ts +9 -0
  103. package/dist/core/ingestion/resolvers/jvm.js +74 -0
  104. package/dist/core/ingestion/resolvers/php.d.ts +7 -0
  105. package/dist/core/ingestion/resolvers/php.js +30 -0
  106. package/dist/core/ingestion/resolvers/ruby.d.ts +9 -0
  107. package/dist/core/ingestion/resolvers/ruby.js +13 -0
  108. package/dist/core/ingestion/resolvers/rust.d.ts +5 -0
  109. package/dist/core/ingestion/resolvers/rust.js +62 -0
  110. package/dist/core/ingestion/resolvers/standard.d.ts +16 -0
  111. package/dist/core/ingestion/resolvers/standard.js +144 -0
  112. package/dist/core/ingestion/resolvers/utils.d.ts +18 -0
  113. package/dist/core/ingestion/resolvers/utils.js +113 -0
  114. package/dist/core/ingestion/structure-processor.d.ts +4 -0
  115. package/dist/core/ingestion/structure-processor.js +39 -0
  116. package/dist/core/ingestion/symbol-table.d.ts +34 -0
  117. package/dist/core/ingestion/symbol-table.js +48 -0
  118. package/dist/core/ingestion/tree-sitter-queries.d.ts +20 -0
  119. package/dist/core/ingestion/tree-sitter-queries.js +691 -0
  120. package/dist/core/ingestion/type-env.d.ts +52 -0
  121. package/dist/core/ingestion/type-env.js +349 -0
  122. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +4 -0
  123. package/dist/core/ingestion/type-extractors/c-cpp.js +214 -0
  124. package/dist/core/ingestion/type-extractors/csharp.d.ts +4 -0
  125. package/dist/core/ingestion/type-extractors/csharp.js +224 -0
  126. package/dist/core/ingestion/type-extractors/go.d.ts +4 -0
  127. package/dist/core/ingestion/type-extractors/go.js +261 -0
  128. package/dist/core/ingestion/type-extractors/index.d.ts +20 -0
  129. package/dist/core/ingestion/type-extractors/index.js +30 -0
  130. package/dist/core/ingestion/type-extractors/jvm.d.ts +5 -0
  131. package/dist/core/ingestion/type-extractors/jvm.js +386 -0
  132. package/dist/core/ingestion/type-extractors/php.d.ts +4 -0
  133. package/dist/core/ingestion/type-extractors/php.js +280 -0
  134. package/dist/core/ingestion/type-extractors/python.d.ts +4 -0
  135. package/dist/core/ingestion/type-extractors/python.js +175 -0
  136. package/dist/core/ingestion/type-extractors/ruby.d.ts +12 -0
  137. package/dist/core/ingestion/type-extractors/ruby.js +218 -0
  138. package/dist/core/ingestion/type-extractors/rust.d.ts +4 -0
  139. package/dist/core/ingestion/type-extractors/rust.js +290 -0
  140. package/dist/core/ingestion/type-extractors/shared.d.ts +81 -0
  141. package/dist/core/ingestion/type-extractors/shared.js +322 -0
  142. package/dist/core/ingestion/type-extractors/swift.d.ts +4 -0
  143. package/dist/core/ingestion/type-extractors/swift.js +140 -0
  144. package/dist/core/ingestion/type-extractors/types.d.ts +111 -0
  145. package/dist/core/ingestion/type-extractors/types.js +4 -0
  146. package/dist/core/ingestion/type-extractors/typescript.d.ts +4 -0
  147. package/dist/core/ingestion/type-extractors/typescript.js +227 -0
  148. package/dist/core/ingestion/utils.d.ts +73 -0
  149. package/dist/core/ingestion/utils.js +992 -0
  150. package/dist/core/ingestion/workers/parse-worker.d.ts +99 -0
  151. package/dist/core/ingestion/workers/parse-worker.js +1055 -0
  152. package/dist/core/ingestion/workers/worker-pool.d.ts +15 -0
  153. package/dist/core/ingestion/workers/worker-pool.js +123 -0
  154. package/dist/core/lbug/csv-generator.d.ts +28 -0
  155. package/dist/core/lbug/csv-generator.js +355 -0
  156. package/dist/core/lbug/lbug-adapter.d.ts +96 -0
  157. package/dist/core/lbug/lbug-adapter.js +753 -0
  158. package/dist/core/lbug/schema.d.ts +46 -0
  159. package/dist/core/lbug/schema.js +402 -0
  160. package/dist/core/search/bm25-index.d.ts +20 -0
  161. package/dist/core/search/bm25-index.js +123 -0
  162. package/dist/core/search/hybrid-search.d.ts +32 -0
  163. package/dist/core/search/hybrid-search.js +131 -0
  164. package/dist/core/search/query-cache.d.ts +18 -0
  165. package/dist/core/search/query-cache.js +47 -0
  166. package/dist/core/search/query-expansion.d.ts +19 -0
  167. package/dist/core/search/query-expansion.js +75 -0
  168. package/dist/core/search/reranker.d.ts +29 -0
  169. package/dist/core/search/reranker.js +122 -0
  170. package/dist/core/search/types.d.ts +154 -0
  171. package/dist/core/search/types.js +51 -0
  172. package/dist/core/semantic/tsgo-service.d.ts +67 -0
  173. package/dist/core/semantic/tsgo-service.js +355 -0
  174. package/dist/core/tree-sitter/parser-loader.d.ts +12 -0
  175. package/dist/core/tree-sitter/parser-loader.js +71 -0
  176. package/dist/lib/memory-guard.d.ts +35 -0
  177. package/dist/lib/memory-guard.js +70 -0
  178. package/dist/lib/utils.d.ts +3 -0
  179. package/dist/lib/utils.js +6 -0
  180. package/dist/mcp/compatible-stdio-transport.d.ts +32 -0
  181. package/dist/mcp/compatible-stdio-transport.js +209 -0
  182. package/dist/mcp/core/embedder.d.ts +24 -0
  183. package/dist/mcp/core/embedder.js +168 -0
  184. package/dist/mcp/core/lbug-adapter.d.ts +29 -0
  185. package/dist/mcp/core/lbug-adapter.js +330 -0
  186. package/dist/mcp/local/local-backend.d.ts +188 -0
  187. package/dist/mcp/local/local-backend.js +2759 -0
  188. package/dist/mcp/resources.d.ts +22 -0
  189. package/dist/mcp/resources.js +379 -0
  190. package/dist/mcp/server.d.ts +10 -0
  191. package/dist/mcp/server.js +217 -0
  192. package/dist/mcp/staleness.d.ts +10 -0
  193. package/dist/mcp/staleness.js +25 -0
  194. package/dist/mcp/tools.d.ts +21 -0
  195. package/dist/mcp/tools.js +202 -0
  196. package/dist/server/api.d.ts +5 -0
  197. package/dist/server/api.js +340 -0
  198. package/dist/server/mcp-http.d.ts +7 -0
  199. package/dist/server/mcp-http.js +95 -0
  200. package/dist/storage/git.d.ts +6 -0
  201. package/dist/storage/git.js +35 -0
  202. package/dist/storage/repo-manager.d.ts +87 -0
  203. package/dist/storage/repo-manager.js +249 -0
  204. package/dist/types/pipeline.d.ts +35 -0
  205. package/dist/types/pipeline.js +20 -0
  206. package/hooks/claude/code-mapper-hook.cjs +238 -0
  207. package/hooks/claude/pre-tool-use.sh +79 -0
  208. package/hooks/claude/session-start.sh +42 -0
  209. package/models/mlx-embedder.py +185 -0
  210. package/package.json +100 -0
  211. package/scripts/patch-tree-sitter-swift.cjs +74 -0
  212. package/vendor/leiden/index.cjs +355 -0
  213. package/vendor/leiden/utils.cjs +392 -0
@@ -0,0 +1,209 @@
1
+ // code-mapper/src/mcp/compatible-stdio-transport.ts
2
+ /** @file compatible-stdio-transport.ts
3
+ * @description MCP stdio transport that auto-detects content-length vs newline framing
4
+ * Handles both official MCP SDK clients and simpler newline-delimited JSON clients */
5
+ import process from 'node:process';
6
+ import { JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';
7
+ function deserializeMessage(raw) {
8
+ return JSONRPCMessageSchema.parse(JSON.parse(raw));
9
+ }
10
+ function serializeNewlineMessage(message) {
11
+ return `${JSON.stringify(message)}\n`;
12
+ }
13
+ function serializeContentLengthMessage(message) {
14
+ const body = JSON.stringify(message);
15
+ return `Content-Length: ${Buffer.byteLength(body, 'utf8')}\r\n\r\n${body}`;
16
+ }
17
+ function findHeaderEnd(buffer) {
18
+ const crlfEnd = buffer.indexOf('\r\n\r\n');
19
+ if (crlfEnd !== -1) {
20
+ return { index: crlfEnd, separatorLength: 4 };
21
+ }
22
+ const lfEnd = buffer.indexOf('\n\n');
23
+ if (lfEnd !== -1) {
24
+ return { index: lfEnd, separatorLength: 2 };
25
+ }
26
+ return null;
27
+ }
28
+ function looksLikeContentLength(buffer) {
29
+ if (buffer.length < 14) {
30
+ return false;
31
+ }
32
+ const probe = buffer.toString('utf8', 0, Math.min(buffer.length, 32));
33
+ return /^content-length\s*:/i.test(probe);
34
+ }
35
+ const MAX_BUFFER_SIZE = 10 * 1024 * 1024; // 10 MB — generous for JSON-RPC
36
+ export class CompatibleStdioServerTransport {
37
+ _stdin;
38
+ _stdout;
39
+ _readBuffer;
40
+ _started = false;
41
+ _framing = null;
42
+ /** Direct reference to the real write function, captured at construction time.
43
+ * Immune to process.stdout.write being replaced (e.g. embedder.ts silences
44
+ * stdout during ONNX model load, which would swallow MCP responses). */
45
+ _directWrite;
46
+ onmessage;
47
+ onerror;
48
+ onclose;
49
+ constructor(_stdin = process.stdin, _stdout = process.stdout) {
50
+ this._stdin = _stdin;
51
+ this._stdout = _stdout;
52
+ this._directWrite = this._stdout.write.bind(this._stdout);
53
+ }
54
+ _ondata = (chunk) => {
55
+ this._readBuffer = this._readBuffer ? Buffer.concat([this._readBuffer, chunk]) : chunk;
56
+ if (this._readBuffer.length > MAX_BUFFER_SIZE) {
57
+ this.onerror?.(new Error(`Read buffer exceeded maximum size (${MAX_BUFFER_SIZE} bytes)`));
58
+ this.discardBufferedInput();
59
+ return;
60
+ }
61
+ this.processReadBuffer();
62
+ };
63
+ _onerror = (error) => {
64
+ this.onerror?.(error);
65
+ };
66
+ async start() {
67
+ if (this._started) {
68
+ throw new Error('CompatibleStdioServerTransport already started!');
69
+ }
70
+ this._started = true;
71
+ this._stdin.on('data', this._ondata);
72
+ this._stdin.on('error', this._onerror);
73
+ }
74
+ detectFraming() {
75
+ if (!this._readBuffer || this._readBuffer.length === 0) {
76
+ return null;
77
+ }
78
+ const firstByte = this._readBuffer[0];
79
+ if (firstByte === 0x7b || firstByte === 0x5b) {
80
+ return 'newline';
81
+ }
82
+ if (looksLikeContentLength(this._readBuffer)) {
83
+ return 'content-length';
84
+ }
85
+ return null;
86
+ }
87
+ discardBufferedInput() {
88
+ this._readBuffer = undefined;
89
+ this._framing = null;
90
+ }
91
+ readContentLengthMessage() {
92
+ if (!this._readBuffer) {
93
+ return null;
94
+ }
95
+ const header = findHeaderEnd(this._readBuffer);
96
+ if (header === null) {
97
+ return null;
98
+ }
99
+ const headerText = this._readBuffer
100
+ .toString('utf8', 0, header.index)
101
+ .replace(/\r\n/g, '\n')
102
+ .replace(/\r/g, '\n');
103
+ const match = headerText.match(/(?:^|\n)content-length\s*:\s*(\d+)/i);
104
+ if (!match) {
105
+ this.discardBufferedInput();
106
+ throw new Error('Missing Content-Length header from MCP client');
107
+ }
108
+ const contentLength = Number.parseInt(match[1], 10);
109
+ if (!Number.isFinite(contentLength) || contentLength < 0) {
110
+ this.discardBufferedInput();
111
+ throw new Error('Invalid Content-Length header from MCP client');
112
+ }
113
+ if (contentLength > MAX_BUFFER_SIZE) {
114
+ this.discardBufferedInput();
115
+ throw new Error(`Content-Length ${contentLength} exceeds maximum allowed size (${MAX_BUFFER_SIZE} bytes)`);
116
+ }
117
+ const bodyStart = header.index + header.separatorLength;
118
+ const bodyEnd = bodyStart + contentLength;
119
+ if (this._readBuffer.length < bodyEnd) {
120
+ return null;
121
+ }
122
+ const body = this._readBuffer.toString('utf8', bodyStart, bodyEnd);
123
+ this._readBuffer = this._readBuffer.subarray(bodyEnd);
124
+ return deserializeMessage(body);
125
+ }
126
+ readNewlineMessage() {
127
+ if (!this._readBuffer) {
128
+ return null;
129
+ }
130
+ while (true) {
131
+ const newlineIndex = this._readBuffer.indexOf('\n');
132
+ if (newlineIndex === -1) {
133
+ return null;
134
+ }
135
+ const line = this._readBuffer.toString('utf8', 0, newlineIndex).replace(/\r$/, '');
136
+ this._readBuffer = this._readBuffer.subarray(newlineIndex + 1);
137
+ if (line.trim().length === 0) {
138
+ continue;
139
+ }
140
+ return deserializeMessage(line);
141
+ }
142
+ }
143
+ readMessage() {
144
+ if (!this._readBuffer || this._readBuffer.length === 0) {
145
+ return null;
146
+ }
147
+ if (this._framing === null) {
148
+ this._framing = this.detectFraming();
149
+ if (this._framing === null) {
150
+ return null;
151
+ }
152
+ }
153
+ return this._framing === 'content-length'
154
+ ? this.readContentLengthMessage()
155
+ : this.readNewlineMessage();
156
+ }
157
+ processReadBuffer() {
158
+ while (true) {
159
+ try {
160
+ const message = this.readMessage();
161
+ if (message === null) {
162
+ break;
163
+ }
164
+ this.onmessage?.(message);
165
+ }
166
+ catch (error) {
167
+ this.onerror?.(error);
168
+ break;
169
+ }
170
+ }
171
+ }
172
+ async close() {
173
+ this._stdin.off('data', this._ondata);
174
+ this._stdin.off('error', this._onerror);
175
+ const remainingDataListeners = this._stdin.listenerCount('data');
176
+ if (remainingDataListeners === 0) {
177
+ this._stdin.pause();
178
+ }
179
+ this._started = false;
180
+ this._readBuffer = undefined;
181
+ this.onclose?.();
182
+ }
183
+ send(message, _options) {
184
+ return new Promise((resolve, reject) => {
185
+ if (!this._started) {
186
+ reject(new Error('Transport is closed'));
187
+ return;
188
+ }
189
+ const payload = this._framing === 'newline'
190
+ ? serializeNewlineMessage(message)
191
+ : serializeContentLengthMessage(message);
192
+ const onError = (error) => {
193
+ this._stdout.removeListener('error', onError);
194
+ reject(error);
195
+ };
196
+ this._stdout.on('error', onError);
197
+ if (this._directWrite(payload)) {
198
+ this._stdout.removeListener('error', onError);
199
+ resolve();
200
+ }
201
+ else {
202
+ this._stdout.once('drain', () => {
203
+ this._stdout.removeListener('error', onError);
204
+ resolve();
205
+ });
206
+ }
207
+ });
208
+ }
209
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @file embedder.ts
3
+ * @description MLX-accelerated query embedder for MCP semantic search
4
+ *
5
+ * Spawns a persistent Python subprocess running Jina Code 1.5B on Metal.
6
+ * Query embeddings use task="nl2code" with prompt_type="query" for optimal
7
+ * natural-language-to-code matching. Includes LRU cache.
8
+ *
9
+ * Fail-fast — requires Python 3 + MLX + the downloaded model.
10
+ */
11
+ /** Initialize the MLX embedder */
12
+ export declare function initEmbedder(): Promise<any>;
13
+ /** Check if embedder is ready */
14
+ export declare function isEmbedderReady(): boolean;
15
+ /** Embed a query text for semantic search (cached, uses "query" prompt type) */
16
+ export declare function embedQuery(query: string): Promise<number[]>;
17
+ /**
18
+ * Embed multiple texts in a single batch (for incremental embedding refresh)
19
+ */
20
+ export declare function embedBatch(texts: string[]): Promise<number[][]>;
21
+ /** Get embedding dimensions */
22
+ export declare function getEmbeddingDims(): number;
23
+ /** Cleanup embedder subprocess */
24
+ export declare function disposeEmbedder(): Promise<void>;
@@ -0,0 +1,168 @@
1
+ // code-mapper/src/mcp/core/embedder.ts
2
+ /**
3
+ * @file embedder.ts
4
+ * @description MLX-accelerated query embedder for MCP semantic search
5
+ *
6
+ * Spawns a persistent Python subprocess running Jina Code 1.5B on Metal.
7
+ * Query embeddings use task="nl2code" with prompt_type="query" for optimal
8
+ * natural-language-to-code matching. Includes LRU cache.
9
+ *
10
+ * Fail-fast — requires Python 3 + MLX + the downloaded model.
11
+ */
12
+ import { spawn, execFileSync } from 'child_process';
13
+ import path from 'path';
14
+ import { fileURLToPath } from 'url';
15
+ import { queryEmbeddingCache } from '../../core/search/query-cache.js';
16
+ const __filename = fileURLToPath(import.meta.url);
17
+ const __dirname = path.dirname(__filename);
18
+ // Path to MLX embedder script
19
+ const MLX_SCRIPT = path.resolve(__dirname, '..', '..', '..', 'models', 'mlx-embedder.py');
20
+ // Embedding dimensions — must match what was used at index time
21
+ const EMBEDDING_DIMS = 256;
22
+ // Singleton subprocess
23
+ let mlxProcess = null;
24
+ let pendingResolve = null;
25
+ let pendingReject = null;
26
+ let lineBuffer = '';
27
+ let ready = false;
28
+ function ensureProcess() {
29
+ if (mlxProcess && !mlxProcess.killed)
30
+ return mlxProcess;
31
+ // Check prerequisites
32
+ try {
33
+ execFileSync('python3', ['-c', 'import mlx; import tokenizers'], {
34
+ timeout: 5000,
35
+ stdio: ['pipe', 'pipe', 'pipe'],
36
+ });
37
+ }
38
+ catch {
39
+ throw new Error('MLX embedder requires Python 3 + MLX on Apple Silicon.\n' +
40
+ 'Install: pip3 install mlx tokenizers huggingface_hub\n' +
41
+ 'The embedding model will download automatically on first use (~3GB).');
42
+ }
43
+ mlxProcess = spawn('python3', [MLX_SCRIPT], {
44
+ stdio: ['pipe', 'pipe', 'pipe'],
45
+ env: { ...process.env, TOKENIZERS_PARALLELISM: 'false' },
46
+ });
47
+ lineBuffer = '';
48
+ mlxProcess.stdout.on('data', (chunk) => {
49
+ lineBuffer += chunk.toString();
50
+ const lines = lineBuffer.split('\n');
51
+ lineBuffer = lines.pop() || '';
52
+ for (const line of lines) {
53
+ if (!line.trim())
54
+ continue;
55
+ try {
56
+ const msg = JSON.parse(line);
57
+ if (msg.status === 'ready' && !ready) {
58
+ ready = true;
59
+ console.error(`Code Mapper: MLX query embedder ready (${msg.device})`);
60
+ }
61
+ if (pendingResolve) {
62
+ const resolve = pendingResolve;
63
+ pendingResolve = null;
64
+ pendingReject = null;
65
+ resolve(msg);
66
+ }
67
+ }
68
+ catch { }
69
+ }
70
+ });
71
+ mlxProcess.stderr.on('data', (chunk) => {
72
+ // Suppress stderr during MCP stdio to avoid corrupting the protocol
73
+ // Only log in non-MCP contexts
74
+ });
75
+ mlxProcess.on('exit', (code) => {
76
+ ready = false;
77
+ mlxProcess = null;
78
+ if (pendingReject) {
79
+ const reject = pendingReject;
80
+ pendingResolve = null;
81
+ pendingReject = null;
82
+ reject(new Error(`MLX embedder exited with code ${code}`));
83
+ }
84
+ });
85
+ return mlxProcess;
86
+ }
87
+ function sendAndReceive(request) {
88
+ return new Promise((resolve, reject) => {
89
+ const proc = ensureProcess();
90
+ pendingResolve = resolve;
91
+ pendingReject = reject;
92
+ proc.stdin.write(JSON.stringify(request) + '\n');
93
+ });
94
+ }
95
+ /** Initialize the MLX embedder */
96
+ export async function initEmbedder() {
97
+ if (ready)
98
+ return;
99
+ ensureProcess();
100
+ const msg = await sendAndReceive({ cmd: 'ping' });
101
+ if (msg.error)
102
+ throw new Error(`MLX embedder failed: ${msg.error}`);
103
+ return msg;
104
+ }
105
+ /** Check if embedder is ready */
106
+ export function isEmbedderReady() {
107
+ return ready;
108
+ }
109
+ /** Embed a query text for semantic search (cached, uses "query" prompt type) */
110
+ export async function embedQuery(query) {
111
+ // Check cache first
112
+ const cached = queryEmbeddingCache.get(query);
113
+ if (cached)
114
+ return cached;
115
+ if (!ready)
116
+ await initEmbedder();
117
+ const result = await sendAndReceive({
118
+ texts: [query],
119
+ task: 'nl2code',
120
+ type: 'query', // query prompt type for natural-language-to-code matching
121
+ dims: EMBEDDING_DIMS,
122
+ });
123
+ if (result.error)
124
+ throw new Error(`Query embedding failed: ${result.error}`);
125
+ const embedding = result.embeddings[0];
126
+ // Cache for future lookups
127
+ queryEmbeddingCache.set(query, embedding);
128
+ return embedding;
129
+ }
130
+ /**
131
+ * Embed multiple texts in a single batch (for incremental embedding refresh)
132
+ */
133
+ export async function embedBatch(texts) {
134
+ if (texts.length === 0)
135
+ return [];
136
+ if (!ready)
137
+ await initEmbedder();
138
+ const result = await sendAndReceive({
139
+ texts,
140
+ task: 'nl2code',
141
+ type: 'passage',
142
+ dims: EMBEDDING_DIMS,
143
+ });
144
+ if (result.error)
145
+ throw new Error(`Batch embedding failed: ${result.error}`);
146
+ return result.embeddings;
147
+ }
148
+ /** Get embedding dimensions */
149
+ export function getEmbeddingDims() {
150
+ return EMBEDDING_DIMS;
151
+ }
152
+ /** Cleanup embedder subprocess */
153
+ export async function disposeEmbedder() {
154
+ if (mlxProcess && !mlxProcess.killed) {
155
+ try {
156
+ mlxProcess.stdin.write(JSON.stringify({ cmd: 'quit' }) + '\n');
157
+ await new Promise(resolve => setTimeout(resolve, 500));
158
+ }
159
+ catch { }
160
+ try {
161
+ mlxProcess.kill();
162
+ }
163
+ catch { }
164
+ mlxProcess = null;
165
+ }
166
+ ready = false;
167
+ queryEmbeddingCache.clear();
168
+ }
@@ -0,0 +1,29 @@
1
+ /** @file lbug-adapter.ts
2
+ * @description LadybugDB connection pool adapter keyed by repoId
3
+ * Provides checkout/return pool so each concurrent query gets its own Connection
4
+ * from the same Database (Connections are NOT thread-safe)
5
+ * @see https://docs.ladybugdb.com/concurrency */
6
+ import lbug from '@ladybugdb/core';
7
+ /**
8
+ * Initialize (or reuse) a Database + connection pool for a specific repo
9
+ * Retries on lock errors (e.g., when `code-mapper analyze` is running)
10
+ */
11
+ export declare const initLbug: (repoId: string, dbPath: string) => Promise<void>;
12
+ export declare const executeQuery: (repoId: string, cypher: string) => Promise<any[]>;
13
+ /** Execute a parameterized query using prepare/execute to prevent Cypher injection */
14
+ export declare const executeParameterized: (repoId: string, cypher: string, params: Record<string, any>) => Promise<any[]>;
15
+ /** Close one or all repo pools (provide repoId for single, omit for all) */
16
+ export declare const closeLbug: (repoId?: string) => Promise<void>;
17
+ /** Invalidate a repo's pool and shared DB cache, forcing fresh connections on next init.
18
+ * Closes the native Database handle to release the file lock so a child process
19
+ * can open the DB in read-write mode for incremental refresh.
20
+ * Used before AND after the incremental refresh child process writes to the DB. */
21
+ export declare const invalidateAndReopen: (repoId: string) => Promise<void>;
22
+ /** Check if a specific repo's pool is active */
23
+ export declare const isLbugReady: (repoId: string) => boolean;
24
+ /**
25
+ * Open a short-lived read-write connection, execute the callback, then close.
26
+ * The read-only pool must be invalidated BEFORE calling this to avoid lock
27
+ * conflicts. Retries on lock errors (e.g., concurrent `code-mapper analyze`).
28
+ */
29
+ export declare const withWriteConnection: <T>(dbPath: string, fn: (conn: lbug.Connection) => Promise<T>) => Promise<T>;