ruvector 0.2.23 → 0.2.25

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 (111) hide show
  1. package/bin/cli.js +211 -63
  2. package/dist/analysis/complexity.d.ts +52 -0
  3. package/dist/analysis/complexity.d.ts.map +1 -0
  4. package/dist/analysis/complexity.js +146 -0
  5. package/dist/analysis/index.d.ts +15 -0
  6. package/dist/analysis/index.d.ts.map +1 -0
  7. package/dist/analysis/index.js +38 -0
  8. package/dist/analysis/patterns.d.ts +71 -0
  9. package/dist/analysis/patterns.d.ts.map +1 -0
  10. package/dist/analysis/patterns.js +243 -0
  11. package/dist/analysis/security.d.ts +51 -0
  12. package/dist/analysis/security.d.ts.map +1 -0
  13. package/dist/analysis/security.js +139 -0
  14. package/dist/core/adaptive-embedder.d.ts +156 -0
  15. package/dist/core/adaptive-embedder.d.ts.map +1 -0
  16. package/dist/core/adaptive-embedder.js +838 -0
  17. package/dist/core/agentdb-fast.d.ts +149 -0
  18. package/dist/core/agentdb-fast.d.ts.map +1 -0
  19. package/dist/core/agentdb-fast.js +301 -0
  20. package/dist/core/ast-parser.d.ts +108 -0
  21. package/dist/core/ast-parser.d.ts.map +1 -0
  22. package/dist/core/ast-parser.js +602 -0
  23. package/dist/core/attention-fallbacks.d.ts +321 -0
  24. package/dist/core/attention-fallbacks.d.ts.map +1 -0
  25. package/dist/core/attention-fallbacks.js +552 -0
  26. package/dist/core/cluster-wrapper.d.ts +148 -0
  27. package/dist/core/cluster-wrapper.d.ts.map +1 -0
  28. package/dist/core/cluster-wrapper.js +271 -0
  29. package/dist/core/coverage-router.d.ts +88 -0
  30. package/dist/core/coverage-router.d.ts.map +1 -0
  31. package/dist/core/coverage-router.js +315 -0
  32. package/dist/core/diff-embeddings.d.ts +93 -0
  33. package/dist/core/diff-embeddings.d.ts.map +1 -0
  34. package/dist/core/diff-embeddings.js +334 -0
  35. package/dist/core/diskann-wrapper.d.ts +53 -0
  36. package/dist/core/diskann-wrapper.d.ts.map +1 -0
  37. package/dist/core/diskann-wrapper.js +105 -0
  38. package/dist/core/gnn-wrapper.d.ts +143 -0
  39. package/dist/core/gnn-wrapper.d.ts.map +1 -0
  40. package/dist/core/gnn-wrapper.js +213 -0
  41. package/dist/core/graph-algorithms.d.ts +83 -0
  42. package/dist/core/graph-algorithms.d.ts.map +1 -0
  43. package/dist/core/graph-algorithms.js +514 -0
  44. package/dist/core/graph-wrapper.d.ts +147 -0
  45. package/dist/core/graph-wrapper.d.ts.map +1 -0
  46. package/dist/core/graph-wrapper.js +299 -0
  47. package/dist/core/index.d.ts +50 -0
  48. package/dist/core/index.d.ts.map +1 -0
  49. package/dist/core/index.js +92 -0
  50. package/dist/core/intelligence-engine.d.ts +258 -0
  51. package/dist/core/intelligence-engine.d.ts.map +1 -0
  52. package/dist/core/intelligence-engine.js +1030 -0
  53. package/dist/core/learning-engine.d.ts +160 -0
  54. package/dist/core/learning-engine.d.ts.map +1 -0
  55. package/dist/core/learning-engine.js +589 -0
  56. package/dist/core/neural-embeddings.d.ts +393 -0
  57. package/dist/core/neural-embeddings.d.ts.map +1 -0
  58. package/dist/core/neural-embeddings.js +1091 -0
  59. package/dist/core/neural-perf.d.ts +331 -0
  60. package/dist/core/neural-perf.d.ts.map +1 -0
  61. package/dist/core/neural-perf.js +704 -0
  62. package/dist/core/onnx/pkg/package.json +3 -0
  63. package/dist/core/onnx-embedder.d.ts +105 -0
  64. package/dist/core/onnx-embedder.d.ts.map +1 -0
  65. package/dist/core/onnx-embedder.js +410 -0
  66. package/dist/core/onnx-optimized.d.ts +109 -0
  67. package/dist/core/onnx-optimized.d.ts.map +1 -0
  68. package/dist/core/onnx-optimized.js +419 -0
  69. package/dist/core/parallel-intelligence.d.ts +109 -0
  70. package/dist/core/parallel-intelligence.d.ts.map +1 -0
  71. package/dist/core/parallel-intelligence.js +340 -0
  72. package/dist/core/parallel-workers.d.ts +177 -0
  73. package/dist/core/parallel-workers.d.ts.map +1 -0
  74. package/dist/core/parallel-workers.js +783 -0
  75. package/dist/core/router-wrapper.d.ts +75 -0
  76. package/dist/core/router-wrapper.d.ts.map +1 -0
  77. package/dist/core/router-wrapper.js +243 -0
  78. package/dist/core/rvf-wrapper.d.ts +86 -0
  79. package/dist/core/rvf-wrapper.d.ts.map +1 -0
  80. package/dist/core/rvf-wrapper.js +102 -0
  81. package/dist/core/sona-wrapper.d.ts +226 -0
  82. package/dist/core/sona-wrapper.d.ts.map +1 -0
  83. package/dist/core/sona-wrapper.js +282 -0
  84. package/dist/core/tensor-compress.d.ts +134 -0
  85. package/dist/core/tensor-compress.d.ts.map +1 -0
  86. package/dist/core/tensor-compress.js +432 -0
  87. package/dist/index.d.ts +106 -0
  88. package/dist/index.d.ts.map +1 -0
  89. package/dist/index.js +258 -0
  90. package/dist/services/embedding-service.d.ts +136 -0
  91. package/dist/services/embedding-service.d.ts.map +1 -0
  92. package/dist/services/embedding-service.js +294 -0
  93. package/dist/services/index.d.ts +6 -0
  94. package/dist/services/index.d.ts.map +1 -0
  95. package/dist/services/index.js +26 -0
  96. package/dist/types.d.ts +145 -0
  97. package/dist/types.d.ts.map +1 -0
  98. package/dist/types.js +2 -0
  99. package/dist/workers/benchmark.d.ts +44 -0
  100. package/dist/workers/benchmark.d.ts.map +1 -0
  101. package/dist/workers/benchmark.js +230 -0
  102. package/dist/workers/index.d.ts +10 -0
  103. package/dist/workers/index.d.ts.map +1 -0
  104. package/dist/workers/index.js +25 -0
  105. package/dist/workers/native-worker.d.ts +76 -0
  106. package/dist/workers/native-worker.d.ts.map +1 -0
  107. package/dist/workers/native-worker.js +490 -0
  108. package/dist/workers/types.d.ts +69 -0
  109. package/dist/workers/types.d.ts.map +1 -0
  110. package/dist/workers/types.js +7 -0
  111. package/package.json +8 -7
@@ -0,0 +1,783 @@
1
+ "use strict";
2
+ /**
3
+ * Parallel Workers - Extended worker capabilities for RuVector hooks
4
+ *
5
+ * Provides parallel processing for advanced operations:
6
+ *
7
+ * 1. SPECULATIVE PRE-COMPUTATION
8
+ * - Pre-embed likely next files based on co-edit patterns
9
+ * - Warm model cache before operations
10
+ * - Predictive route caching
11
+ *
12
+ * 2. REAL-TIME CODE ANALYSIS
13
+ * - Multi-file AST parsing with tree-sitter
14
+ * - Cross-file type inference
15
+ * - Live complexity metrics
16
+ * - Dependency graph updates
17
+ *
18
+ * 3. ADVANCED LEARNING
19
+ * - Distributed trajectory replay
20
+ * - Parallel SONA micro-LoRA updates
21
+ * - Background EWC consolidation
22
+ * - Online pattern clustering
23
+ *
24
+ * 4. INTELLIGENT RETRIEVAL
25
+ * - Parallel RAG chunking and retrieval
26
+ * - Sharded similarity search
27
+ * - Context relevance ranking
28
+ * - Semantic deduplication
29
+ *
30
+ * 5. SECURITY & QUALITY
31
+ * - Parallel SAST scanning
32
+ * - Multi-rule linting
33
+ * - Vulnerability detection
34
+ * - Code smell analysis
35
+ *
36
+ * 6. GIT INTELLIGENCE
37
+ * - Parallel blame analysis
38
+ * - Branch comparison
39
+ * - Merge conflict prediction
40
+ * - Code churn metrics
41
+ */
42
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
43
+ if (k2 === undefined) k2 = k;
44
+ var desc = Object.getOwnPropertyDescriptor(m, k);
45
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
46
+ desc = { enumerable: true, get: function() { return m[k]; } };
47
+ }
48
+ Object.defineProperty(o, k2, desc);
49
+ }) : (function(o, m, k, k2) {
50
+ if (k2 === undefined) k2 = k;
51
+ o[k2] = m[k];
52
+ }));
53
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
54
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
55
+ }) : function(o, v) {
56
+ o["default"] = v;
57
+ });
58
+ var __importStar = (this && this.__importStar) || (function () {
59
+ var ownKeys = function(o) {
60
+ ownKeys = Object.getOwnPropertyNames || function (o) {
61
+ var ar = [];
62
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
63
+ return ar;
64
+ };
65
+ return ownKeys(o);
66
+ };
67
+ return function (mod) {
68
+ if (mod && mod.__esModule) return mod;
69
+ var result = {};
70
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
71
+ __setModuleDefault(result, mod);
72
+ return result;
73
+ };
74
+ })();
75
+ Object.defineProperty(exports, "__esModule", { value: true });
76
+ exports.ExtendedWorkerPool = void 0;
77
+ exports.getExtendedWorkerPool = getExtendedWorkerPool;
78
+ exports.initExtendedWorkerPool = initExtendedWorkerPool;
79
+ const worker_threads_1 = require("worker_threads");
80
+ const os = __importStar(require("os"));
81
+ // ============================================================================
82
+ // Extended Worker Pool
83
+ // ============================================================================
84
+ class ExtendedWorkerPool {
85
+ constructor(config = {}) {
86
+ this.workers = [];
87
+ this.taskQueue = [];
88
+ this.busyWorkers = new Map();
89
+ this.initialized = false;
90
+ this.speculativeCache = new Map();
91
+ this.astCache = new Map();
92
+ const isCLI = process.env.RUVECTOR_CLI === '1';
93
+ const isMCP = process.env.MCP_SERVER === '1';
94
+ this.config = {
95
+ numWorkers: config.numWorkers ?? Math.max(1, os.cpus().length - 1),
96
+ enabled: config.enabled ?? (isMCP || process.env.RUVECTOR_PARALLEL === '1'),
97
+ taskTimeout: config.taskTimeout ?? 30000,
98
+ maxQueueSize: config.maxQueueSize ?? 1000,
99
+ };
100
+ }
101
+ async init() {
102
+ if (this.initialized || !this.config.enabled)
103
+ return;
104
+ const workerCode = this.getWorkerCode();
105
+ const workerBlob = new Blob([workerCode], { type: 'application/javascript' });
106
+ for (let i = 0; i < this.config.numWorkers; i++) {
107
+ // Create worker from inline code
108
+ const worker = new worker_threads_1.Worker(`
109
+ const { parentPort, workerData } = require('worker_threads');
110
+ ${this.getWorkerHandlers()}
111
+ `, { eval: true, workerData: { workerId: i } });
112
+ worker.on('message', (result) => {
113
+ this.handleWorkerResult(worker, result);
114
+ });
115
+ worker.on('error', (err) => {
116
+ console.error(`Worker ${i} error:`, err);
117
+ this.busyWorkers.delete(worker);
118
+ this.processQueue();
119
+ });
120
+ this.workers.push(worker);
121
+ }
122
+ this.initialized = true;
123
+ }
124
+ getWorkerCode() {
125
+ return `
126
+ const { parentPort, workerData } = require('worker_threads');
127
+ ${this.getWorkerHandlers()}
128
+ `;
129
+ }
130
+ getWorkerHandlers() {
131
+ return `
132
+ parentPort.on('message', async (task) => {
133
+ try {
134
+ let result;
135
+ switch (task.type) {
136
+ case 'speculative-embed':
137
+ result = await speculativeEmbed(task.files, task.coEditGraph);
138
+ break;
139
+ case 'ast-analyze':
140
+ result = await astAnalyze(task.files);
141
+ break;
142
+ case 'security-scan':
143
+ result = await securityScan(task.files, task.rules);
144
+ break;
145
+ case 'rag-retrieve':
146
+ result = await ragRetrieve(task.query, task.chunks, task.topK);
147
+ break;
148
+ case 'context-rank':
149
+ result = await contextRank(task.context, task.query);
150
+ break;
151
+ case 'git-blame':
152
+ result = await gitBlame(task.files);
153
+ break;
154
+ case 'git-churn':
155
+ result = await gitChurn(task.files, task.since);
156
+ break;
157
+ case 'complexity-analyze':
158
+ result = await complexityAnalyze(task.files);
159
+ break;
160
+ case 'dependency-graph':
161
+ result = await dependencyGraph(task.entryPoints);
162
+ break;
163
+ case 'deduplicate':
164
+ result = await deduplicate(task.items, task.threshold);
165
+ break;
166
+ default:
167
+ throw new Error('Unknown task type: ' + task.type);
168
+ }
169
+ parentPort.postMessage({ success: true, data: result, taskId: task.taskId });
170
+ } catch (error) {
171
+ parentPort.postMessage({ success: false, error: error.message, taskId: task.taskId });
172
+ }
173
+ });
174
+
175
+ // Worker implementations
176
+
177
+ // Hash-based embedding: deterministic, no external deps, 128-dim
178
+ function hashEmbed(text, dim = 128) {
179
+ const embedding = new Float64Array(dim);
180
+ const tokens = text.split(/\\s+|[{}()\\[\\];,.<>=/+\\-*&|!~^%@#]/);
181
+
182
+ for (let t = 0; t < tokens.length; t++) {
183
+ const token = tokens[t];
184
+ if (!token) continue;
185
+
186
+ // FNV-1a hash
187
+ let h = 0x811c9dc5;
188
+ for (let i = 0; i < token.length; i++) {
189
+ h ^= token.charCodeAt(i);
190
+ h = Math.imul(h, 0x01000193);
191
+ }
192
+
193
+ // Positional weight (tokens near start matter more)
194
+ const posWeight = 1.0 / (1.0 + Math.log1p(t));
195
+
196
+ // Distribute across multiple dimensions using hash rotations
197
+ for (let d = 0; d < 4; d++) {
198
+ const idx = ((h >>> 0) + d * 37) % dim;
199
+ const sign = (h & (1 << d)) ? 1 : -1;
200
+ embedding[idx] += sign * posWeight;
201
+ h = (h >>> 7) | (h << 25); // rotate
202
+ }
203
+ }
204
+
205
+ // L2 normalize
206
+ let norm = 0;
207
+ for (let i = 0; i < dim; i++) norm += embedding[i] * embedding[i];
208
+ norm = Math.sqrt(norm) || 1;
209
+ const result = new Array(dim);
210
+ for (let i = 0; i < dim; i++) result[i] = embedding[i] / norm;
211
+ return result;
212
+ }
213
+
214
+ async function speculativeEmbed(files, coEditGraph) {
215
+ const fs = require('fs');
216
+ return files.map(file => {
217
+ try {
218
+ if (!fs.existsSync(file)) {
219
+ return { file, embedding: hashEmbed(file), confidence: 0.2, timestamp: Date.now() };
220
+ }
221
+ const content = fs.readFileSync(file, 'utf8');
222
+ const embedding = hashEmbed(content);
223
+
224
+ // Confidence based on file size (more content = higher confidence)
225
+ const lines = content.split('\\n').length;
226
+ const confidence = Math.min(0.95, 0.3 + (lines / 500) * 0.65);
227
+
228
+ return { file, embedding, confidence, timestamp: Date.now() };
229
+ } catch {
230
+ return { file, embedding: hashEmbed(file), confidence: 0.1, timestamp: Date.now() };
231
+ }
232
+ });
233
+ }
234
+
235
+ async function astAnalyze(files) {
236
+ const fs = require('fs');
237
+ return files.map(file => {
238
+ try {
239
+ const content = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : '';
240
+ const lines = content.split('\\n');
241
+ return {
242
+ file,
243
+ language: file.split('.').pop() || 'unknown',
244
+ complexity: Math.min(lines.length / 10, 100),
245
+ functions: extractFunctions(content),
246
+ imports: extractImports(content),
247
+ exports: extractExports(content),
248
+ dependencies: [],
249
+ };
250
+ } catch {
251
+ return { file, language: 'unknown', complexity: 0, functions: [], imports: [], exports: [], dependencies: [] };
252
+ }
253
+ });
254
+ }
255
+
256
+ function extractFunctions(content) {
257
+ const patterns = [
258
+ /function\\s+(\\w+)/g,
259
+ /const\\s+(\\w+)\\s*=\\s*(?:async\\s*)?\\([^)]*\\)\\s*=>/g,
260
+ /(?:async\\s+)?(?:public|private|protected)?\\s*(\\w+)\\s*\\([^)]*\\)\\s*{/g,
261
+ ];
262
+ const funcs = new Set();
263
+ for (const pattern of patterns) {
264
+ let match;
265
+ while ((match = pattern.exec(content)) !== null) {
266
+ if (match[1] && !['if', 'for', 'while', 'switch', 'catch'].includes(match[1])) {
267
+ funcs.add(match[1]);
268
+ }
269
+ }
270
+ }
271
+ return Array.from(funcs);
272
+ }
273
+
274
+ function extractImports(content) {
275
+ const imports = [];
276
+ const patterns = [
277
+ /import\\s+.*?from\\s+['"]([^'"]+)['"]/g,
278
+ /require\\s*\\(['"]([^'"]+)['"]\\)/g,
279
+ ];
280
+ for (const pattern of patterns) {
281
+ let match;
282
+ while ((match = pattern.exec(content)) !== null) {
283
+ imports.push(match[1]);
284
+ }
285
+ }
286
+ return imports;
287
+ }
288
+
289
+ function extractExports(content) {
290
+ const exports = [];
291
+ const patterns = [
292
+ /export\\s+(?:default\\s+)?(?:class|function|const|let|var)\\s+(\\w+)/g,
293
+ /module\\.exports\\s*=\\s*(\\w+)/g,
294
+ ];
295
+ for (const pattern of patterns) {
296
+ let match;
297
+ while ((match = pattern.exec(content)) !== null) {
298
+ exports.push(match[1]);
299
+ }
300
+ }
301
+ return exports;
302
+ }
303
+
304
+ async function securityScan(files, rules) {
305
+ const fs = require('fs');
306
+ const findings = [];
307
+ const securityPatterns = [
308
+ { pattern: /eval\\s*\\(/g, rule: 'no-eval', severity: 'high', message: 'Avoid eval()' },
309
+ { pattern: /innerHTML\\s*=/g, rule: 'no-inner-html', severity: 'medium', message: 'Avoid innerHTML, use textContent' },
310
+ { pattern: /password\\s*=\\s*['"][^'"]+['"]/gi, rule: 'no-hardcoded-secrets', severity: 'critical', message: 'Hardcoded password detected' },
311
+ { pattern: /api[_-]?key\\s*=\\s*['"][^'"]+['"]/gi, rule: 'no-hardcoded-secrets', severity: 'critical', message: 'Hardcoded API key detected' },
312
+ { pattern: /exec\\s*\\(/g, rule: 'no-exec', severity: 'high', message: 'Avoid exec(), use execFile or spawn' },
313
+ { pattern: /\\$\\{.*\\}/g, rule: 'template-injection', severity: 'medium', message: 'Potential template injection' },
314
+ ];
315
+
316
+ for (const file of files) {
317
+ try {
318
+ if (!fs.existsSync(file)) continue;
319
+ const content = fs.readFileSync(file, 'utf8');
320
+ const lines = content.split('\\n');
321
+
322
+ for (const { pattern, rule, severity, message } of securityPatterns) {
323
+ let match;
324
+ const regex = new RegExp(pattern.source, pattern.flags);
325
+ while ((match = regex.exec(content)) !== null) {
326
+ const lineNum = content.substring(0, match.index).split('\\n').length;
327
+ findings.push({ file, line: lineNum, severity, rule, message });
328
+ }
329
+ }
330
+ } catch {}
331
+ }
332
+ return findings;
333
+ }
334
+
335
+ function cosineSimilarity(a, b) {
336
+ if (!a || !b || a.length !== b.length || a.length === 0) return 0;
337
+ let dot = 0, normA = 0, normB = 0;
338
+ for (let i = 0; i < a.length; i++) {
339
+ dot += a[i] * b[i];
340
+ normA += a[i] * a[i];
341
+ normB += b[i] * b[i];
342
+ }
343
+ const denom = Math.sqrt(normA) * Math.sqrt(normB);
344
+ return denom === 0 ? 0 : dot / denom;
345
+ }
346
+
347
+ async function ragRetrieve(query, chunks, topK) {
348
+ // If chunks have embeddings, use cosine similarity (semantic retrieval)
349
+ const hasEmbeddings = chunks.some(c => c.embedding && c.embedding.length > 0);
350
+
351
+ if (hasEmbeddings) {
352
+ const queryEmbedding = hashEmbed(query, chunks[0].embedding.length);
353
+ return chunks
354
+ .map(chunk => {
355
+ const semantic = chunk.embedding && chunk.embedding.length > 0
356
+ ? cosineSimilarity(queryEmbedding, chunk.embedding)
357
+ : 0;
358
+ // Blend semantic + keyword for robustness
359
+ const queryTerms = query.toLowerCase().split(/\\s+/);
360
+ const content = chunk.content.toLowerCase();
361
+ const kwMatches = queryTerms.filter(t => content.includes(t)).length;
362
+ const keyword = queryTerms.length > 0 ? kwMatches / queryTerms.length : 0;
363
+ const relevance = semantic * 0.7 + keyword * 0.3;
364
+ return { ...chunk, relevance };
365
+ })
366
+ .sort((a, b) => b.relevance - a.relevance)
367
+ .slice(0, topK);
368
+ }
369
+
370
+ // Fallback: TF-IDF-weighted keyword matching
371
+ const queryTerms = query.toLowerCase().split(/\\s+/).filter(Boolean);
372
+ const allContent = chunks.map(c => c.content.toLowerCase());
373
+ // IDF: log(N / df) for each query term
374
+ const idf = {};
375
+ for (const term of queryTerms) {
376
+ const df = allContent.filter(c => c.includes(term)).length || 1;
377
+ idf[term] = Math.log(allContent.length / df);
378
+ }
379
+ return chunks
380
+ .map(chunk => {
381
+ const content = chunk.content.toLowerCase();
382
+ const words = content.split(/\\s+/);
383
+ let score = 0;
384
+ for (const term of queryTerms) {
385
+ const tf = words.filter(w => w === term).length / (words.length || 1);
386
+ score += tf * (idf[term] || 1);
387
+ }
388
+ return { ...chunk, relevance: score };
389
+ })
390
+ .sort((a, b) => b.relevance - a.relevance)
391
+ .slice(0, topK);
392
+ }
393
+
394
+ async function contextRank(context, query) {
395
+ // Use TF-IDF scoring instead of raw keyword matching
396
+ const queryTerms = query.toLowerCase().split(/\\s+/).filter(Boolean);
397
+ const allContent = context.map(c => c.toLowerCase());
398
+ const idf = {};
399
+ for (const term of queryTerms) {
400
+ const df = allContent.filter(c => c.includes(term)).length || 1;
401
+ idf[term] = Math.log(allContent.length / df);
402
+ }
403
+ return context
404
+ .map((ctx, i) => {
405
+ const content = ctx.toLowerCase();
406
+ const words = content.split(/\\s+/);
407
+ let score = 0;
408
+ for (const term of queryTerms) {
409
+ const tf = words.filter(w => w === term).length / (words.length || 1);
410
+ score += tf * (idf[term] || 1);
411
+ }
412
+ return { index: i, content: ctx, relevance: score };
413
+ })
414
+ .sort((a, b) => b.relevance - a.relevance);
415
+ }
416
+
417
+ async function gitBlame(files) {
418
+ const { execSync } = require('child_process');
419
+ const results = [];
420
+ for (const file of files) {
421
+ try {
422
+ const output = execSync(\`git blame --line-porcelain "\${file}" 2>/dev/null\`, { encoding: 'utf8', maxBuffer: 10 * 1024 * 1024 });
423
+ const lines = [];
424
+ let currentLine = {};
425
+ for (const line of output.split('\\n')) {
426
+ if (line.startsWith('author ')) currentLine.author = line.slice(7);
427
+ else if (line.startsWith('author-time ')) currentLine.date = new Date(parseInt(line.slice(12)) * 1000).toISOString();
428
+ else if (line.match(/^[a-f0-9]{40}/)) currentLine.commit = line.slice(0, 40);
429
+ else if (line.startsWith('\\t')) {
430
+ lines.push({ ...currentLine, line: lines.length + 1 });
431
+ currentLine = {};
432
+ }
433
+ }
434
+ results.push({ file, lines });
435
+ } catch {
436
+ results.push({ file, lines: [] });
437
+ }
438
+ }
439
+ return results;
440
+ }
441
+
442
+ async function gitChurn(files, since) {
443
+ const { execSync } = require('child_process');
444
+ const results = [];
445
+ const sinceArg = since ? \`--since="\${since}"\` : '--since="30 days ago"';
446
+
447
+ for (const file of files) {
448
+ try {
449
+ const log = execSync(\`git log \${sinceArg} --format="%H|%an|%aI" --numstat -- "\${file}" 2>/dev/null\`, { encoding: 'utf8' });
450
+ let additions = 0, deletions = 0, commits = 0;
451
+ const authors = new Set();
452
+ let lastModified = '';
453
+
454
+ for (const line of log.split('\\n')) {
455
+ if (line.includes('|')) {
456
+ const [commit, author, date] = line.split('|');
457
+ authors.add(author);
458
+ commits++;
459
+ if (!lastModified) lastModified = date;
460
+ } else if (line.match(/^\\d+\\s+\\d+/)) {
461
+ const [add, del] = line.split('\\t');
462
+ additions += parseInt(add) || 0;
463
+ deletions += parseInt(del) || 0;
464
+ }
465
+ }
466
+
467
+ results.push({ file, additions, deletions, commits, authors: Array.from(authors), lastModified });
468
+ } catch {
469
+ results.push({ file, additions: 0, deletions: 0, commits: 0, authors: [], lastModified: '' });
470
+ }
471
+ }
472
+ return results;
473
+ }
474
+
475
+ async function complexityAnalyze(files) {
476
+ const fs = require('fs');
477
+ return files.map(file => {
478
+ try {
479
+ const content = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : '';
480
+ const lines = content.split('\\n');
481
+ const nonEmpty = lines.filter(l => l.trim()).length;
482
+ const branches = (content.match(/\\b(if|else|switch|case|for|while|catch|\\?|&&|\\|\\|)\\b/g) || []).length;
483
+ const functions = (content.match(/function|=>|\\bdef\\b|\\bfn\\b/g) || []).length;
484
+
485
+ return {
486
+ file,
487
+ lines: lines.length,
488
+ nonEmptyLines: nonEmpty,
489
+ cyclomaticComplexity: branches + 1,
490
+ functions,
491
+ avgFunctionSize: functions > 0 ? Math.round(nonEmpty / functions) : nonEmpty,
492
+ };
493
+ } catch {
494
+ return { file, lines: 0, nonEmptyLines: 0, cyclomaticComplexity: 1, functions: 0, avgFunctionSize: 0 };
495
+ }
496
+ });
497
+ }
498
+
499
+ async function dependencyGraph(entryPoints) {
500
+ const fs = require('fs');
501
+ const path = require('path');
502
+ const graph = new Map();
503
+
504
+ function analyze(file, visited = new Set()) {
505
+ if (visited.has(file)) return;
506
+ visited.add(file);
507
+
508
+ try {
509
+ if (!fs.existsSync(file)) return;
510
+ const content = fs.readFileSync(file, 'utf8');
511
+ const deps = [];
512
+
513
+ // Extract imports
514
+ const importRegex = /(?:import|require)\\s*\\(?['"]([^'"]+)['"]/g;
515
+ let match;
516
+ while ((match = importRegex.exec(content)) !== null) {
517
+ const dep = match[1];
518
+ if (dep.startsWith('.')) {
519
+ const resolved = path.resolve(path.dirname(file), dep);
520
+ deps.push(resolved);
521
+ analyze(resolved, visited);
522
+ } else {
523
+ deps.push(dep);
524
+ }
525
+ }
526
+
527
+ graph.set(file, deps);
528
+ } catch {}
529
+ }
530
+
531
+ for (const entry of entryPoints) {
532
+ analyze(entry);
533
+ }
534
+
535
+ return Object.fromEntries(graph);
536
+ }
537
+
538
+ async function deduplicate(items, threshold) {
539
+ // Simple Jaccard similarity deduplication
540
+ const unique = [];
541
+ const seen = new Set();
542
+
543
+ for (const item of items) {
544
+ const tokens = new Set(item.toLowerCase().split(/\\s+/));
545
+ let isDup = false;
546
+
547
+ for (const existing of unique) {
548
+ const existingTokens = new Set(existing.toLowerCase().split(/\\s+/));
549
+ const intersection = [...tokens].filter(t => existingTokens.has(t)).length;
550
+ const union = new Set([...tokens, ...existingTokens]).size;
551
+ const similarity = intersection / union;
552
+
553
+ if (similarity >= threshold) {
554
+ isDup = true;
555
+ break;
556
+ }
557
+ }
558
+
559
+ if (!isDup) unique.push(item);
560
+ }
561
+
562
+ return unique;
563
+ }
564
+ `;
565
+ }
566
+ handleWorkerResult(worker, result) {
567
+ this.busyWorkers.delete(worker);
568
+ // Find and resolve the corresponding task
569
+ const taskIndex = this.taskQueue.findIndex(t => t.task.taskId === result.taskId);
570
+ if (taskIndex >= 0) {
571
+ const task = this.taskQueue.splice(taskIndex, 1)[0];
572
+ clearTimeout(task.timeout);
573
+ if (result.success) {
574
+ task.resolve(result.data);
575
+ }
576
+ else {
577
+ task.reject(new Error(result.error));
578
+ }
579
+ }
580
+ this.processQueue();
581
+ }
582
+ processQueue() {
583
+ while (this.taskQueue.length > 0) {
584
+ const availableWorker = this.workers.find(w => !this.busyWorkers.has(w));
585
+ if (!availableWorker)
586
+ break;
587
+ const task = this.taskQueue[0];
588
+ this.busyWorkers.set(availableWorker, task.task.type);
589
+ availableWorker.postMessage(task.task);
590
+ }
591
+ }
592
+ async execute(task) {
593
+ if (!this.initialized || !this.config.enabled) {
594
+ throw new Error('Worker pool not initialized');
595
+ }
596
+ const taskId = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
597
+ const taskWithId = { ...task, taskId };
598
+ return new Promise((resolve, reject) => {
599
+ if (this.taskQueue.length >= this.config.maxQueueSize) {
600
+ reject(new Error('Task queue full'));
601
+ return;
602
+ }
603
+ const timeout = setTimeout(() => {
604
+ const idx = this.taskQueue.findIndex(t => t.task.taskId === taskId);
605
+ if (idx >= 0) {
606
+ this.taskQueue.splice(idx, 1);
607
+ reject(new Error('Task timeout'));
608
+ }
609
+ }, this.config.taskTimeout);
610
+ const availableWorker = this.workers.find(w => !this.busyWorkers.has(w));
611
+ if (availableWorker) {
612
+ this.busyWorkers.set(availableWorker, task.type);
613
+ this.taskQueue.push({ task: taskWithId, resolve, reject, timeout });
614
+ availableWorker.postMessage(taskWithId);
615
+ }
616
+ else {
617
+ this.taskQueue.push({ task: taskWithId, resolve, reject, timeout });
618
+ }
619
+ });
620
+ }
621
+ // =========================================================================
622
+ // Public API - Speculative Pre-computation
623
+ // =========================================================================
624
+ /**
625
+ * Pre-embed files likely to be edited next based on co-edit patterns
626
+ * Hook: session-start, post-edit
627
+ */
628
+ async speculativeEmbed(currentFile, coEditGraph) {
629
+ const likelyFiles = coEditGraph.get(currentFile) || [];
630
+ if (likelyFiles.length === 0)
631
+ return [];
632
+ // Check cache first
633
+ const uncached = likelyFiles.filter(f => !this.speculativeCache.has(f));
634
+ if (uncached.length === 0) {
635
+ return likelyFiles.map(f => this.speculativeCache.get(f));
636
+ }
637
+ const results = await this.execute({
638
+ type: 'speculative-embed',
639
+ files: uncached,
640
+ coEditGraph,
641
+ });
642
+ // Update cache
643
+ for (const result of results) {
644
+ this.speculativeCache.set(result.file, result);
645
+ }
646
+ return likelyFiles.map(f => this.speculativeCache.get(f)).filter(Boolean);
647
+ }
648
+ // =========================================================================
649
+ // Public API - Code Analysis
650
+ // =========================================================================
651
+ /**
652
+ * Analyze AST of multiple files in parallel
653
+ * Hook: pre-edit, route
654
+ */
655
+ async analyzeAST(files) {
656
+ // Check cache
657
+ const uncached = files.filter(f => !this.astCache.has(f));
658
+ if (uncached.length === 0) {
659
+ return files.map(f => this.astCache.get(f));
660
+ }
661
+ const results = await this.execute({
662
+ type: 'ast-analyze',
663
+ files: uncached,
664
+ });
665
+ // Update cache
666
+ for (const result of results) {
667
+ this.astCache.set(result.file, result);
668
+ }
669
+ return files.map(f => this.astCache.get(f)).filter(Boolean);
670
+ }
671
+ /**
672
+ * Analyze code complexity for multiple files
673
+ * Hook: post-edit, session-end
674
+ */
675
+ async analyzeComplexity(files) {
676
+ return this.execute({ type: 'complexity-analyze', files });
677
+ }
678
+ /**
679
+ * Build dependency graph from entry points
680
+ * Hook: session-start
681
+ */
682
+ async buildDependencyGraph(entryPoints) {
683
+ return this.execute({ type: 'dependency-graph', entryPoints });
684
+ }
685
+ // =========================================================================
686
+ // Public API - Security
687
+ // =========================================================================
688
+ /**
689
+ * Scan files for security vulnerabilities
690
+ * Hook: pre-command (before commit), post-edit
691
+ */
692
+ async securityScan(files, rules) {
693
+ return this.execute({ type: 'security-scan', files, rules });
694
+ }
695
+ // =========================================================================
696
+ // Public API - RAG & Context
697
+ // =========================================================================
698
+ /**
699
+ * Retrieve relevant context chunks in parallel
700
+ * Hook: suggest-context, recall
701
+ */
702
+ async ragRetrieve(query, chunks, topK = 5) {
703
+ return this.execute({ type: 'rag-retrieve', query, chunks, topK });
704
+ }
705
+ /**
706
+ * Rank context items by relevance to query
707
+ * Hook: suggest-context
708
+ */
709
+ async rankContext(context, query) {
710
+ return this.execute({ type: 'context-rank', context, query });
711
+ }
712
+ /**
713
+ * Deduplicate similar items
714
+ * Hook: remember, suggest-context
715
+ */
716
+ async deduplicate(items, threshold = 0.8) {
717
+ return this.execute({ type: 'deduplicate', items, threshold });
718
+ }
719
+ // =========================================================================
720
+ // Public API - Git Intelligence
721
+ // =========================================================================
722
+ /**
723
+ * Get blame information for files in parallel
724
+ * Hook: pre-edit (for context), coedit
725
+ */
726
+ async gitBlame(files) {
727
+ return this.execute({ type: 'git-blame', files });
728
+ }
729
+ /**
730
+ * Analyze code churn for files
731
+ * Hook: session-start, route
732
+ */
733
+ async gitChurn(files, since) {
734
+ return this.execute({ type: 'git-churn', files, since });
735
+ }
736
+ // =========================================================================
737
+ // Stats & Lifecycle
738
+ // =========================================================================
739
+ getStats() {
740
+ return {
741
+ enabled: this.config.enabled,
742
+ workers: this.workers.length,
743
+ busy: this.busyWorkers.size,
744
+ queued: this.taskQueue.length,
745
+ speculativeCacheSize: this.speculativeCache.size,
746
+ astCacheSize: this.astCache.size,
747
+ };
748
+ }
749
+ clearCaches() {
750
+ this.speculativeCache.clear();
751
+ this.astCache.clear();
752
+ }
753
+ async shutdown() {
754
+ // Clear pending tasks
755
+ for (const task of this.taskQueue) {
756
+ clearTimeout(task.timeout);
757
+ task.reject(new Error('Worker pool shutting down'));
758
+ }
759
+ this.taskQueue = [];
760
+ // Terminate workers
761
+ await Promise.all(this.workers.map(w => w.terminate()));
762
+ this.workers = [];
763
+ this.busyWorkers.clear();
764
+ this.initialized = false;
765
+ }
766
+ }
767
+ exports.ExtendedWorkerPool = ExtendedWorkerPool;
768
+ // ============================================================================
769
+ // Singleton
770
+ // ============================================================================
771
+ let instance = null;
772
+ function getExtendedWorkerPool(config) {
773
+ if (!instance) {
774
+ instance = new ExtendedWorkerPool(config);
775
+ }
776
+ return instance;
777
+ }
778
+ async function initExtendedWorkerPool(config) {
779
+ const pool = getExtendedWorkerPool(config);
780
+ await pool.init();
781
+ return pool;
782
+ }
783
+ exports.default = ExtendedWorkerPool;