ruvector 0.1.38 โ†’ 0.1.39

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 (39) hide show
  1. package/.claude-flow/metrics/agent-metrics.json +1 -0
  2. package/.claude-flow/metrics/performance.json +87 -0
  3. package/.claude-flow/metrics/task-metrics.json +10 -0
  4. package/PACKAGE_SUMMARY.md +409 -0
  5. package/README.md +1679 -508
  6. package/bin/cli.js +2427 -0
  7. package/dist/core/agentdb-fast.d.ts +149 -0
  8. package/dist/core/agentdb-fast.d.ts.map +1 -0
  9. package/dist/core/agentdb-fast.js +301 -0
  10. package/dist/core/attention-fallbacks.d.ts +221 -0
  11. package/dist/core/attention-fallbacks.d.ts.map +1 -0
  12. package/dist/core/attention-fallbacks.js +361 -0
  13. package/dist/core/gnn-wrapper.d.ts +143 -0
  14. package/dist/core/gnn-wrapper.d.ts.map +1 -0
  15. package/dist/core/gnn-wrapper.js +213 -0
  16. package/dist/core/index.d.ts +15 -0
  17. package/dist/core/index.d.ts.map +1 -0
  18. package/dist/core/index.js +39 -0
  19. package/dist/core/sona-wrapper.d.ts +215 -0
  20. package/dist/core/sona-wrapper.d.ts.map +1 -0
  21. package/dist/core/sona-wrapper.js +258 -0
  22. package/dist/index.d.ts +87 -82
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +169 -89
  25. package/dist/services/embedding-service.d.ts +136 -0
  26. package/dist/services/embedding-service.d.ts.map +1 -0
  27. package/dist/services/embedding-service.js +294 -0
  28. package/dist/services/index.d.ts +6 -0
  29. package/dist/services/index.d.ts.map +1 -0
  30. package/dist/services/index.js +26 -0
  31. package/dist/types.d.ts +145 -0
  32. package/dist/types.d.ts.map +1 -0
  33. package/dist/types.js +2 -0
  34. package/examples/api-usage.js +211 -0
  35. package/examples/cli-demo.sh +85 -0
  36. package/package.json +41 -93
  37. package/bin/ruvector.js +0 -1150
  38. package/dist/index.d.mts +0 -95
  39. package/dist/index.mjs +0 -5
package/bin/cli.js ADDED
@@ -0,0 +1,2427 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { Command } = require('commander');
4
+ const chalk = require('chalk');
5
+ const ora = require('ora');
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+
9
+ // Lazy load ruvector (only when needed, not for install/help commands)
10
+ let VectorDB, getVersion, getImplementationType;
11
+ let ruvectorLoaded = false;
12
+
13
+ function loadRuvector() {
14
+ if (ruvectorLoaded) return true;
15
+ try {
16
+ const ruvector = require('../dist/index.js');
17
+ VectorDB = ruvector.VectorDB;
18
+ getVersion = ruvector.getVersion;
19
+ getImplementationType = ruvector.getImplementationType;
20
+ ruvectorLoaded = true;
21
+ return true;
22
+ } catch (e) {
23
+ return false;
24
+ }
25
+ }
26
+
27
+ function requireRuvector() {
28
+ if (!loadRuvector()) {
29
+ console.error(chalk.red('Error: Failed to load ruvector. Please run: npm run build'));
30
+ console.error(chalk.yellow('Or install the package: npm install ruvector'));
31
+ process.exit(1);
32
+ }
33
+ }
34
+
35
+ // Import GNN (optional - graceful fallback if not available)
36
+ let RuvectorLayer, TensorCompress, differentiableSearch, getCompressionLevel, hierarchicalForward;
37
+ let gnnAvailable = false;
38
+ try {
39
+ const gnn = require('@ruvector/gnn');
40
+ RuvectorLayer = gnn.RuvectorLayer;
41
+ TensorCompress = gnn.TensorCompress;
42
+ differentiableSearch = gnn.differentiableSearch;
43
+ getCompressionLevel = gnn.getCompressionLevel;
44
+ hierarchicalForward = gnn.hierarchicalForward;
45
+ gnnAvailable = true;
46
+ } catch (e) {
47
+ // GNN not available - commands will show helpful message
48
+ }
49
+
50
+ // Import Attention (optional - graceful fallback if not available)
51
+ let DotProductAttention, MultiHeadAttention, HyperbolicAttention, FlashAttention, LinearAttention, MoEAttention;
52
+ let GraphRoPeAttention, EdgeFeaturedAttention, DualSpaceAttention, LocalGlobalAttention;
53
+ let benchmarkAttention, computeAttentionAsync, batchAttentionCompute, parallelAttentionCompute;
54
+ let expMap, logMap, mobiusAddition, poincareDistance, projectToPoincareBall;
55
+ let attentionInfo, attentionVersion;
56
+ let attentionAvailable = false;
57
+ try {
58
+ const attention = require('@ruvector/attention');
59
+ // Core mechanisms
60
+ DotProductAttention = attention.DotProductAttention;
61
+ MultiHeadAttention = attention.MultiHeadAttention;
62
+ HyperbolicAttention = attention.HyperbolicAttention;
63
+ FlashAttention = attention.FlashAttention;
64
+ LinearAttention = attention.LinearAttention;
65
+ MoEAttention = attention.MoEAttention;
66
+ // Graph attention
67
+ GraphRoPeAttention = attention.GraphRoPeAttention;
68
+ EdgeFeaturedAttention = attention.EdgeFeaturedAttention;
69
+ DualSpaceAttention = attention.DualSpaceAttention;
70
+ LocalGlobalAttention = attention.LocalGlobalAttention;
71
+ // Utilities
72
+ benchmarkAttention = attention.benchmarkAttention;
73
+ computeAttentionAsync = attention.computeAttentionAsync;
74
+ batchAttentionCompute = attention.batchAttentionCompute;
75
+ parallelAttentionCompute = attention.parallelAttentionCompute;
76
+ // Hyperbolic math
77
+ expMap = attention.expMap;
78
+ logMap = attention.logMap;
79
+ mobiusAddition = attention.mobiusAddition;
80
+ poincareDistance = attention.poincareDistance;
81
+ projectToPoincareBall = attention.projectToPoincareBall;
82
+ // Meta
83
+ attentionInfo = attention.info;
84
+ attentionVersion = attention.version;
85
+ attentionAvailable = true;
86
+ } catch (e) {
87
+ // Attention not available - commands will show helpful message
88
+ }
89
+
90
+ const program = new Command();
91
+
92
+ // Get package version from package.json
93
+ const packageJson = require('../package.json');
94
+
95
+ // Version and description (lazy load implementation info)
96
+ program
97
+ .name('ruvector')
98
+ .description(`${chalk.cyan('ruvector')} - High-performance vector database CLI`)
99
+ .version(packageJson.version);
100
+
101
+ // Create database
102
+ program
103
+ .command('create <path>')
104
+ .description('Create a new vector database')
105
+ .option('-d, --dimension <number>', 'Vector dimension', '384')
106
+ .option('-m, --metric <type>', 'Distance metric (cosine|euclidean|dot)', 'cosine')
107
+ .action((dbPath, options) => {
108
+ requireRuvector();
109
+ const spinner = ora('Creating database...').start();
110
+
111
+ try {
112
+ const dimension = parseInt(options.dimension);
113
+ const db = new VectorDB({
114
+ dimension,
115
+ metric: options.metric,
116
+ path: dbPath,
117
+ autoPersist: true
118
+ });
119
+
120
+ db.save(dbPath);
121
+ spinner.succeed(chalk.green(`Database created: ${dbPath}`));
122
+ console.log(chalk.gray(` Dimension: ${dimension}`));
123
+ console.log(chalk.gray(` Metric: ${options.metric}`));
124
+ console.log(chalk.gray(` Implementation: ${getImplementationType()}`));
125
+ } catch (error) {
126
+ spinner.fail(chalk.red('Failed to create database'));
127
+ console.error(chalk.red(error.message));
128
+ process.exit(1);
129
+ }
130
+ });
131
+
132
+ // Insert vectors
133
+ program
134
+ .command('insert <database> <file>')
135
+ .description('Insert vectors from JSON file')
136
+ .option('-b, --batch-size <number>', 'Batch size for insertion', '1000')
137
+ .action((dbPath, file, options) => {
138
+ requireRuvector();
139
+ const spinner = ora('Loading database...').start();
140
+
141
+ try {
142
+ // Read database metadata to get dimension
143
+ let dimension = 384; // default
144
+ if (fs.existsSync(dbPath)) {
145
+ const dbData = fs.readFileSync(dbPath, 'utf8');
146
+ const parsed = JSON.parse(dbData);
147
+ dimension = parsed.dimension || 384;
148
+ }
149
+
150
+ const db = new VectorDB({ dimension });
151
+
152
+ if (fs.existsSync(dbPath)) {
153
+ db.load(dbPath);
154
+ }
155
+
156
+ spinner.text = 'Reading vectors...';
157
+ const data = JSON.parse(fs.readFileSync(file, 'utf8'));
158
+ const vectors = Array.isArray(data) ? data : [data];
159
+
160
+ spinner.text = `Inserting ${vectors.length} vectors...`;
161
+ const batchSize = parseInt(options.batchSize);
162
+
163
+ for (let i = 0; i < vectors.length; i += batchSize) {
164
+ const batch = vectors.slice(i, i + batchSize);
165
+ db.insertBatch(batch);
166
+ spinner.text = `Inserted ${Math.min(i + batchSize, vectors.length)}/${vectors.length} vectors...`;
167
+ }
168
+
169
+ db.save(dbPath);
170
+ spinner.succeed(chalk.green(`Inserted ${vectors.length} vectors`));
171
+
172
+ const stats = db.stats();
173
+ console.log(chalk.gray(` Total vectors: ${stats.count}`));
174
+ } catch (error) {
175
+ spinner.fail(chalk.red('Failed to insert vectors'));
176
+ console.error(chalk.red(error.message));
177
+ process.exit(1);
178
+ }
179
+ });
180
+
181
+ // Search vectors
182
+ program
183
+ .command('search <database>')
184
+ .description('Search for similar vectors')
185
+ .requiredOption('-v, --vector <json>', 'Query vector as JSON array')
186
+ .option('-k, --top-k <number>', 'Number of results', '10')
187
+ .option('-t, --threshold <number>', 'Similarity threshold', '0.0')
188
+ .option('-f, --filter <json>', 'Metadata filter as JSON')
189
+ .action((dbPath, options) => {
190
+ requireRuvector();
191
+ const spinner = ora('Loading database...').start();
192
+
193
+ try {
194
+ // Read database metadata
195
+ const dbData = fs.readFileSync(dbPath, 'utf8');
196
+ const parsed = JSON.parse(dbData);
197
+ const dimension = parsed.dimension || 384;
198
+
199
+ const db = new VectorDB({ dimension });
200
+ db.load(dbPath);
201
+
202
+ spinner.text = 'Searching...';
203
+
204
+ const vector = JSON.parse(options.vector);
205
+ const query = {
206
+ vector,
207
+ k: parseInt(options.topK),
208
+ threshold: parseFloat(options.threshold)
209
+ };
210
+
211
+ if (options.filter) {
212
+ query.filter = JSON.parse(options.filter);
213
+ }
214
+
215
+ const results = db.search(query);
216
+ spinner.succeed(chalk.green(`Found ${results.length} results`));
217
+
218
+ console.log(chalk.cyan('\nSearch Results:'));
219
+ results.forEach((result, i) => {
220
+ console.log(chalk.white(`\n${i + 1}. ID: ${result.id}`));
221
+ console.log(chalk.yellow(` Score: ${result.score.toFixed(4)}`));
222
+ if (result.metadata) {
223
+ console.log(chalk.gray(` Metadata: ${JSON.stringify(result.metadata)}`));
224
+ }
225
+ });
226
+ } catch (error) {
227
+ spinner.fail(chalk.red('Failed to search'));
228
+ console.error(chalk.red(error.message));
229
+ process.exit(1);
230
+ }
231
+ });
232
+
233
+ // Show stats
234
+ program
235
+ .command('stats <database>')
236
+ .description('Show database statistics')
237
+ .action((dbPath) => {
238
+ requireRuvector();
239
+ const spinner = ora('Loading database...').start();
240
+
241
+ try {
242
+ const dbData = fs.readFileSync(dbPath, 'utf8');
243
+ const parsed = JSON.parse(dbData);
244
+ const dimension = parsed.dimension || 384;
245
+
246
+ const db = new VectorDB({ dimension });
247
+ db.load(dbPath);
248
+
249
+ const stats = db.stats();
250
+ spinner.succeed(chalk.green('Database statistics'));
251
+
252
+ console.log(chalk.cyan('\nDatabase Stats:'));
253
+ console.log(chalk.white(` Vector Count: ${chalk.yellow(stats.count)}`));
254
+ console.log(chalk.white(` Dimension: ${chalk.yellow(stats.dimension)}`));
255
+ console.log(chalk.white(` Metric: ${chalk.yellow(stats.metric)}`));
256
+ console.log(chalk.white(` Implementation: ${chalk.yellow(getImplementationType())}`));
257
+
258
+ if (stats.memoryUsage) {
259
+ const mb = (stats.memoryUsage / (1024 * 1024)).toFixed(2);
260
+ console.log(chalk.white(` Memory Usage: ${chalk.yellow(mb + ' MB')}`));
261
+ }
262
+
263
+ const fileStats = fs.statSync(dbPath);
264
+ const fileMb = (fileStats.size / (1024 * 1024)).toFixed(2);
265
+ console.log(chalk.white(` File Size: ${chalk.yellow(fileMb + ' MB')}`));
266
+ } catch (error) {
267
+ spinner.fail(chalk.red('Failed to load database'));
268
+ console.error(chalk.red(error.message));
269
+ process.exit(1);
270
+ }
271
+ });
272
+
273
+ // Benchmark
274
+ program
275
+ .command('benchmark')
276
+ .description('Run performance benchmarks')
277
+ .option('-d, --dimension <number>', 'Vector dimension', '384')
278
+ .option('-n, --num-vectors <number>', 'Number of vectors', '10000')
279
+ .option('-q, --num-queries <number>', 'Number of queries', '1000')
280
+ .action((options) => {
281
+ requireRuvector();
282
+ console.log(chalk.cyan('\nruvector Performance Benchmark'));
283
+ console.log(chalk.gray(`Implementation: ${getImplementationType()}\n`));
284
+
285
+ const dimension = parseInt(options.dimension);
286
+ const numVectors = parseInt(options.numVectors);
287
+ const numQueries = parseInt(options.numQueries);
288
+
289
+ let spinner = ora('Creating database...').start();
290
+
291
+ try {
292
+ const db = new VectorDB({ dimension, metric: 'cosine' });
293
+ spinner.succeed();
294
+
295
+ // Insert benchmark
296
+ spinner = ora(`Inserting ${numVectors} vectors...`).start();
297
+ const insertStart = Date.now();
298
+
299
+ const vectors = [];
300
+ for (let i = 0; i < numVectors; i++) {
301
+ vectors.push({
302
+ id: `vec_${i}`,
303
+ vector: Array.from({ length: dimension }, () => Math.random()),
304
+ metadata: { index: i, batch: Math.floor(i / 1000) }
305
+ });
306
+ }
307
+
308
+ db.insertBatch(vectors);
309
+ const insertTime = Date.now() - insertStart;
310
+ const insertRate = (numVectors / (insertTime / 1000)).toFixed(0);
311
+
312
+ spinner.succeed(chalk.green(`Inserted ${numVectors} vectors in ${insertTime}ms`));
313
+ console.log(chalk.gray(` Rate: ${chalk.yellow(insertRate)} vectors/sec`));
314
+
315
+ // Search benchmark
316
+ spinner = ora(`Running ${numQueries} searches...`).start();
317
+ const searchStart = Date.now();
318
+
319
+ for (let i = 0; i < numQueries; i++) {
320
+ const query = {
321
+ vector: Array.from({ length: dimension }, () => Math.random()),
322
+ k: 10
323
+ };
324
+ db.search(query);
325
+ }
326
+
327
+ const searchTime = Date.now() - searchStart;
328
+ const searchRate = (numQueries / (searchTime / 1000)).toFixed(0);
329
+ const avgLatency = (searchTime / numQueries).toFixed(2);
330
+
331
+ spinner.succeed(chalk.green(`Completed ${numQueries} searches in ${searchTime}ms`));
332
+ console.log(chalk.gray(` Rate: ${chalk.yellow(searchRate)} queries/sec`));
333
+ console.log(chalk.gray(` Avg Latency: ${chalk.yellow(avgLatency)}ms`));
334
+
335
+ // Stats
336
+ const stats = db.stats();
337
+ console.log(chalk.cyan('\nFinal Stats:'));
338
+ console.log(chalk.white(` Vector Count: ${chalk.yellow(stats.count)}`));
339
+ console.log(chalk.white(` Dimension: ${chalk.yellow(stats.dimension)}`));
340
+ console.log(chalk.white(` Implementation: ${chalk.yellow(getImplementationType())}`));
341
+
342
+ } catch (error) {
343
+ spinner.fail(chalk.red('Benchmark failed'));
344
+ console.error(chalk.red(error.message));
345
+ process.exit(1);
346
+ }
347
+ });
348
+
349
+ // Info command
350
+ program
351
+ .command('info')
352
+ .description('Show ruvector information')
353
+ .action(() => {
354
+ console.log(chalk.cyan('\nruvector Information'));
355
+ console.log(chalk.white(` CLI Version: ${chalk.yellow(packageJson.version)}`));
356
+
357
+ // Try to load ruvector for implementation info
358
+ if (loadRuvector()) {
359
+ const version = typeof getVersion === 'function' ? getVersion() : 'unknown';
360
+ const impl = typeof getImplementationType === 'function' ? getImplementationType() : 'native';
361
+ console.log(chalk.white(` Core Version: ${chalk.yellow(version)}`));
362
+ console.log(chalk.white(` Implementation: ${chalk.yellow(impl)}`));
363
+ } else {
364
+ console.log(chalk.white(` Core: ${chalk.gray('Not loaded (install @ruvector/core)')}`));
365
+ }
366
+
367
+ console.log(chalk.white(` GNN Module: ${gnnAvailable ? chalk.green('Available') : chalk.gray('Not installed')}`));
368
+ console.log(chalk.white(` Node Version: ${chalk.yellow(process.version)}`));
369
+ console.log(chalk.white(` Platform: ${chalk.yellow(process.platform)}`));
370
+ console.log(chalk.white(` Architecture: ${chalk.yellow(process.arch)}`));
371
+
372
+ if (!gnnAvailable) {
373
+ console.log(chalk.gray('\n Install GNN with: npx ruvector install gnn'));
374
+ }
375
+ });
376
+
377
+ // =============================================================================
378
+ // Install Command
379
+ // =============================================================================
380
+
381
+ program
382
+ .command('install [packages...]')
383
+ .description('Install optional ruvector packages')
384
+ .option('-a, --all', 'Install all optional packages')
385
+ .option('-l, --list', 'List available packages')
386
+ .option('-i, --interactive', 'Interactive package selection')
387
+ .action(async (packages, options) => {
388
+ const { execSync } = require('child_process');
389
+
390
+ // Available optional packages - all ruvector npm packages
391
+ const availablePackages = {
392
+ // Core packages
393
+ core: {
394
+ name: '@ruvector/core',
395
+ description: 'Core vector database with native Rust bindings (HNSW, SIMD)',
396
+ installed: true, // Always installed with ruvector
397
+ category: 'core'
398
+ },
399
+ gnn: {
400
+ name: '@ruvector/gnn',
401
+ description: 'Graph Neural Network layers, tensor compression, differentiable search',
402
+ installed: gnnAvailable,
403
+ category: 'core'
404
+ },
405
+ 'graph-node': {
406
+ name: '@ruvector/graph-node',
407
+ description: 'Native Node.js bindings for hypergraph database with Cypher queries',
408
+ installed: false,
409
+ category: 'core'
410
+ },
411
+ 'agentic-synth': {
412
+ name: '@ruvector/agentic-synth',
413
+ description: 'Synthetic data generator for AI/ML training, RAG, and agentic workflows',
414
+ installed: false,
415
+ category: 'tools'
416
+ },
417
+ extensions: {
418
+ name: 'ruvector-extensions',
419
+ description: 'Advanced features: embeddings, UI, exports, temporal tracking, persistence',
420
+ installed: false,
421
+ category: 'tools'
422
+ },
423
+ // Platform-specific native bindings for @ruvector/core
424
+ 'node-linux-x64': {
425
+ name: '@ruvector/node-linux-x64-gnu',
426
+ description: 'Linux x64 native bindings for @ruvector/core',
427
+ installed: false,
428
+ category: 'platform'
429
+ },
430
+ 'node-linux-arm64': {
431
+ name: '@ruvector/node-linux-arm64-gnu',
432
+ description: 'Linux ARM64 native bindings for @ruvector/core',
433
+ installed: false,
434
+ category: 'platform'
435
+ },
436
+ 'node-darwin-x64': {
437
+ name: '@ruvector/node-darwin-x64',
438
+ description: 'macOS Intel x64 native bindings for @ruvector/core',
439
+ installed: false,
440
+ category: 'platform'
441
+ },
442
+ 'node-darwin-arm64': {
443
+ name: '@ruvector/node-darwin-arm64',
444
+ description: 'macOS Apple Silicon native bindings for @ruvector/core',
445
+ installed: false,
446
+ category: 'platform'
447
+ },
448
+ 'node-win32-x64': {
449
+ name: '@ruvector/node-win32-x64-msvc',
450
+ description: 'Windows x64 native bindings for @ruvector/core',
451
+ installed: false,
452
+ category: 'platform'
453
+ },
454
+ // Platform-specific native bindings for @ruvector/gnn
455
+ 'gnn-linux-x64': {
456
+ name: '@ruvector/gnn-linux-x64-gnu',
457
+ description: 'Linux x64 native bindings for @ruvector/gnn',
458
+ installed: false,
459
+ category: 'platform'
460
+ },
461
+ 'gnn-linux-arm64': {
462
+ name: '@ruvector/gnn-linux-arm64-gnu',
463
+ description: 'Linux ARM64 native bindings for @ruvector/gnn',
464
+ installed: false,
465
+ category: 'platform'
466
+ },
467
+ 'gnn-darwin-x64': {
468
+ name: '@ruvector/gnn-darwin-x64',
469
+ description: 'macOS Intel x64 native bindings for @ruvector/gnn',
470
+ installed: false,
471
+ category: 'platform'
472
+ },
473
+ 'gnn-darwin-arm64': {
474
+ name: '@ruvector/gnn-darwin-arm64',
475
+ description: 'macOS Apple Silicon native bindings for @ruvector/gnn',
476
+ installed: false,
477
+ category: 'platform'
478
+ },
479
+ 'gnn-win32-x64': {
480
+ name: '@ruvector/gnn-win32-x64-msvc',
481
+ description: 'Windows x64 native bindings for @ruvector/gnn',
482
+ installed: false,
483
+ category: 'platform'
484
+ },
485
+ // Legacy/standalone packages
486
+ 'ruvector-core': {
487
+ name: 'ruvector-core',
488
+ description: 'Standalone vector database (legacy, use @ruvector/core instead)',
489
+ installed: false,
490
+ category: 'legacy'
491
+ }
492
+ };
493
+
494
+ // Check which packages are actually installed
495
+ for (const [key, pkg] of Object.entries(availablePackages)) {
496
+ if (key !== 'core' && key !== 'gnn') {
497
+ try {
498
+ require.resolve(pkg.name);
499
+ pkg.installed = true;
500
+ } catch (e) {
501
+ pkg.installed = false;
502
+ }
503
+ }
504
+ }
505
+
506
+ // List packages
507
+ if (options.list || (packages.length === 0 && !options.all && !options.interactive)) {
508
+ console.log(chalk.cyan('\nโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'));
509
+ console.log(chalk.cyan(' Ruvector Packages'));
510
+ console.log(chalk.cyan('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n'));
511
+
512
+ const categories = {
513
+ core: { title: '๐Ÿ“ฆ Core Packages', packages: [] },
514
+ tools: { title: '๐Ÿ”ง Tools & Extensions', packages: [] },
515
+ platform: { title: '๐Ÿ–ฅ๏ธ Platform Bindings', packages: [] },
516
+ legacy: { title: '๐Ÿ“œ Legacy Packages', packages: [] }
517
+ };
518
+
519
+ // Group by category
520
+ Object.entries(availablePackages).forEach(([key, pkg]) => {
521
+ if (categories[pkg.category]) {
522
+ categories[pkg.category].packages.push({ key, ...pkg });
523
+ }
524
+ });
525
+
526
+ // Display by category
527
+ for (const [catKey, cat] of Object.entries(categories)) {
528
+ if (cat.packages.length === 0) continue;
529
+
530
+ console.log(chalk.cyan(`${cat.title}`));
531
+ console.log(chalk.gray('โ”€'.repeat(60)));
532
+
533
+ cat.packages.forEach(pkg => {
534
+ const status = pkg.installed ? chalk.green('โœ“') : chalk.gray('โ—‹');
535
+ const statusText = pkg.installed ? chalk.green('installed') : chalk.gray('available');
536
+ console.log(chalk.white(` ${status} ${chalk.yellow(pkg.key.padEnd(18))} ${statusText}`));
537
+ console.log(chalk.gray(` ${pkg.description}`));
538
+ console.log(chalk.gray(` npm: ${chalk.white(pkg.name)}\n`));
539
+ });
540
+ }
541
+
542
+ console.log(chalk.cyan('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'));
543
+ console.log(chalk.cyan('Usage:'));
544
+ console.log(chalk.white(' npx ruvector install gnn # Install GNN package'));
545
+ console.log(chalk.white(' npx ruvector install graph-node # Install graph database'));
546
+ console.log(chalk.white(' npx ruvector install agentic-synth # Install data generator'));
547
+ console.log(chalk.white(' npx ruvector install --all # Install all core packages'));
548
+ console.log(chalk.white(' npx ruvector install -i # Interactive selection'));
549
+ console.log(chalk.gray('\n Note: Platform bindings are auto-detected by @ruvector/core'));
550
+ return;
551
+ }
552
+
553
+ // Interactive mode
554
+ if (options.interactive) {
555
+ const readline = require('readline');
556
+ const rl = readline.createInterface({
557
+ input: process.stdin,
558
+ output: process.stdout
559
+ });
560
+
561
+ console.log(chalk.cyan('\nSelect packages to install:\n'));
562
+
563
+ const notInstalled = Object.entries(availablePackages)
564
+ .filter(([_, pkg]) => !pkg.installed);
565
+
566
+ if (notInstalled.length === 0) {
567
+ console.log(chalk.green('All packages are already installed!'));
568
+ rl.close();
569
+ return;
570
+ }
571
+
572
+ notInstalled.forEach(([key, pkg], i) => {
573
+ console.log(chalk.white(` ${i + 1}. ${chalk.yellow(key)} - ${pkg.description}`));
574
+ });
575
+ console.log(chalk.white(` ${notInstalled.length + 1}. ${chalk.yellow('all')} - Install all packages`));
576
+ console.log(chalk.white(` 0. ${chalk.gray('cancel')} - Exit without installing`));
577
+
578
+ rl.question(chalk.cyan('\nEnter selection (comma-separated for multiple): '), (answer) => {
579
+ rl.close();
580
+
581
+ const selections = answer.split(',').map(s => s.trim());
582
+ let toInstall = [];
583
+
584
+ for (const sel of selections) {
585
+ if (sel === '0' || sel.toLowerCase() === 'cancel') {
586
+ console.log(chalk.yellow('Installation cancelled.'));
587
+ return;
588
+ }
589
+ if (sel === String(notInstalled.length + 1) || sel.toLowerCase() === 'all') {
590
+ toInstall = notInstalled.map(([_, pkg]) => pkg.name);
591
+ break;
592
+ }
593
+ const idx = parseInt(sel) - 1;
594
+ if (idx >= 0 && idx < notInstalled.length) {
595
+ toInstall.push(notInstalled[idx][1].name);
596
+ }
597
+ }
598
+
599
+ if (toInstall.length === 0) {
600
+ console.log(chalk.yellow('No valid packages selected.'));
601
+ return;
602
+ }
603
+
604
+ installPackages(toInstall);
605
+ });
606
+ return;
607
+ }
608
+
609
+ // Install all (core + tools only, not platform-specific or legacy)
610
+ if (options.all) {
611
+ const toInstall = Object.values(availablePackages)
612
+ .filter(pkg => !pkg.installed && (pkg.category === 'core' || pkg.category === 'tools'))
613
+ .map(pkg => pkg.name);
614
+
615
+ if (toInstall.length === 0) {
616
+ console.log(chalk.green('All core packages are already installed!'));
617
+ return;
618
+ }
619
+
620
+ console.log(chalk.cyan(`Installing ${toInstall.length} packages...`));
621
+ installPackages(toInstall);
622
+ return;
623
+ }
624
+
625
+ // Install specific packages
626
+ const toInstall = [];
627
+ for (const pkg of packages) {
628
+ const key = pkg.toLowerCase().replace('@ruvector/', '');
629
+ if (availablePackages[key]) {
630
+ if (availablePackages[key].installed) {
631
+ console.log(chalk.yellow(`${availablePackages[key].name} is already installed`));
632
+ } else {
633
+ toInstall.push(availablePackages[key].name);
634
+ }
635
+ } else {
636
+ console.log(chalk.red(`Unknown package: ${pkg}`));
637
+ console.log(chalk.gray(`Available: ${Object.keys(availablePackages).join(', ')}`));
638
+ }
639
+ }
640
+
641
+ if (toInstall.length > 0) {
642
+ installPackages(toInstall);
643
+ }
644
+
645
+ function installPackages(pkgs) {
646
+ const spinner = ora(`Installing ${pkgs.join(', ')}...`).start();
647
+
648
+ try {
649
+ // Detect package manager
650
+ let pm = 'npm';
651
+ if (fs.existsSync('yarn.lock')) pm = 'yarn';
652
+ else if (fs.existsSync('pnpm-lock.yaml')) pm = 'pnpm';
653
+ else if (fs.existsSync('bun.lockb')) pm = 'bun';
654
+
655
+ const cmd = pm === 'yarn' ? `yarn add ${pkgs.join(' ')}`
656
+ : pm === 'pnpm' ? `pnpm add ${pkgs.join(' ')}`
657
+ : pm === 'bun' ? `bun add ${pkgs.join(' ')}`
658
+ : `npm install ${pkgs.join(' ')}`;
659
+
660
+ execSync(cmd, { stdio: 'pipe' });
661
+
662
+ spinner.succeed(chalk.green(`Installed: ${pkgs.join(', ')}`));
663
+ console.log(chalk.cyan('\nRun "npx ruvector info" to verify installation.'));
664
+ } catch (error) {
665
+ spinner.fail(chalk.red('Installation failed'));
666
+ console.error(chalk.red(error.message));
667
+ console.log(chalk.yellow(`\nTry manually: npm install ${pkgs.join(' ')}`));
668
+ process.exit(1);
669
+ }
670
+ }
671
+ });
672
+
673
+ // =============================================================================
674
+ // GNN Commands
675
+ // =============================================================================
676
+
677
+ // Helper to check GNN availability
678
+ function requireGnn() {
679
+ if (!gnnAvailable) {
680
+ console.error(chalk.red('Error: GNN module not available.'));
681
+ console.error(chalk.yellow('Install it with: npm install @ruvector/gnn'));
682
+ process.exit(1);
683
+ }
684
+ }
685
+
686
+ // GNN parent command
687
+ const gnnCmd = program
688
+ .command('gnn')
689
+ .description('Graph Neural Network operations');
690
+
691
+ // GNN Layer command
692
+ gnnCmd
693
+ .command('layer')
694
+ .description('Create and test a GNN layer')
695
+ .requiredOption('-i, --input-dim <number>', 'Input dimension')
696
+ .requiredOption('-h, --hidden-dim <number>', 'Hidden dimension')
697
+ .option('-a, --heads <number>', 'Number of attention heads', '4')
698
+ .option('-d, --dropout <number>', 'Dropout rate', '0.1')
699
+ .option('--test', 'Run a test forward pass')
700
+ .option('-o, --output <file>', 'Save layer config to JSON file')
701
+ .action((options) => {
702
+ requireGnn();
703
+ const spinner = ora('Creating GNN layer...').start();
704
+
705
+ try {
706
+ const inputDim = parseInt(options.inputDim);
707
+ const hiddenDim = parseInt(options.hiddenDim);
708
+ const heads = parseInt(options.heads);
709
+ const dropout = parseFloat(options.dropout);
710
+
711
+ const layer = new RuvectorLayer(inputDim, hiddenDim, heads, dropout);
712
+ spinner.succeed(chalk.green('GNN Layer created'));
713
+
714
+ console.log(chalk.cyan('\nLayer Configuration:'));
715
+ console.log(chalk.white(` Input Dim: ${chalk.yellow(inputDim)}`));
716
+ console.log(chalk.white(` Hidden Dim: ${chalk.yellow(hiddenDim)}`));
717
+ console.log(chalk.white(` Heads: ${chalk.yellow(heads)}`));
718
+ console.log(chalk.white(` Dropout: ${chalk.yellow(dropout)}`));
719
+
720
+ if (options.test) {
721
+ spinner.start('Running test forward pass...');
722
+
723
+ // Create test data
724
+ const nodeEmbedding = Array.from({ length: inputDim }, () => Math.random());
725
+ const neighborEmbeddings = [
726
+ Array.from({ length: inputDim }, () => Math.random()),
727
+ Array.from({ length: inputDim }, () => Math.random())
728
+ ];
729
+ const edgeWeights = [0.6, 0.4];
730
+
731
+ const output = layer.forward(nodeEmbedding, neighborEmbeddings, edgeWeights);
732
+ spinner.succeed(chalk.green('Forward pass completed'));
733
+
734
+ console.log(chalk.cyan('\nTest Results:'));
735
+ console.log(chalk.white(` Input shape: ${chalk.yellow(`[${inputDim}]`)}`));
736
+ console.log(chalk.white(` Output shape: ${chalk.yellow(`[${output.length}]`)}`));
737
+ console.log(chalk.white(` Output sample: ${chalk.gray(`[${output.slice(0, 4).map(v => v.toFixed(4)).join(', ')}...]`)}`));
738
+ }
739
+
740
+ if (options.output) {
741
+ const config = layer.toJson();
742
+ fs.writeFileSync(options.output, config);
743
+ console.log(chalk.green(`\nLayer config saved to: ${options.output}`));
744
+ }
745
+ } catch (error) {
746
+ spinner.fail(chalk.red('Failed to create GNN layer'));
747
+ console.error(chalk.red(error.message));
748
+ process.exit(1);
749
+ }
750
+ });
751
+
752
+ // GNN Compress command
753
+ gnnCmd
754
+ .command('compress')
755
+ .description('Compress embeddings using adaptive tensor compression')
756
+ .requiredOption('-f, --file <path>', 'Input JSON file with embeddings')
757
+ .option('-l, --level <type>', 'Compression level (none|half|pq8|pq4|binary)', 'auto')
758
+ .option('-a, --access-freq <number>', 'Access frequency for auto compression (0.0-1.0)', '0.5')
759
+ .option('-o, --output <file>', 'Output file for compressed data')
760
+ .action((options) => {
761
+ requireGnn();
762
+ const spinner = ora('Loading embeddings...').start();
763
+
764
+ try {
765
+ const data = JSON.parse(fs.readFileSync(options.file, 'utf8'));
766
+ const embeddings = Array.isArray(data) ? data : [data];
767
+
768
+ spinner.text = 'Compressing embeddings...';
769
+ const compressor = new TensorCompress();
770
+ const accessFreq = parseFloat(options.accessFreq);
771
+
772
+ const results = [];
773
+ let totalOriginalSize = 0;
774
+ let totalCompressedSize = 0;
775
+
776
+ for (const embedding of embeddings) {
777
+ const vec = embedding.vector || embedding;
778
+ totalOriginalSize += vec.length * 4; // float32 = 4 bytes
779
+
780
+ let compressed;
781
+ if (options.level === 'auto') {
782
+ compressed = compressor.compress(vec, accessFreq);
783
+ } else {
784
+ const levelConfig = { levelType: options.level };
785
+ if (options.level === 'pq8') {
786
+ levelConfig.subvectors = 8;
787
+ levelConfig.centroids = 256;
788
+ } else if (options.level === 'pq4') {
789
+ levelConfig.subvectors = 8;
790
+ }
791
+ compressed = compressor.compressWithLevel(vec, levelConfig);
792
+ }
793
+
794
+ totalCompressedSize += compressed.length;
795
+ results.push({
796
+ id: embedding.id,
797
+ compressed
798
+ });
799
+ }
800
+
801
+ const ratio = (totalOriginalSize / totalCompressedSize).toFixed(2);
802
+ const savings = ((1 - totalCompressedSize / totalOriginalSize) * 100).toFixed(1);
803
+
804
+ spinner.succeed(chalk.green(`Compressed ${embeddings.length} embeddings`));
805
+
806
+ console.log(chalk.cyan('\nCompression Results:'));
807
+ console.log(chalk.white(` Embeddings: ${chalk.yellow(embeddings.length)}`));
808
+ console.log(chalk.white(` Level: ${chalk.yellow(options.level === 'auto' ? `auto (${getCompressionLevel(accessFreq)})` : options.level)}`));
809
+ console.log(chalk.white(` Original: ${chalk.yellow((totalOriginalSize / 1024).toFixed(2) + ' KB')}`));
810
+ console.log(chalk.white(` Compressed: ${chalk.yellow((totalCompressedSize / 1024).toFixed(2) + ' KB')}`));
811
+ console.log(chalk.white(` Ratio: ${chalk.yellow(ratio + 'x')}`));
812
+ console.log(chalk.white(` Savings: ${chalk.yellow(savings + '%')}`));
813
+
814
+ if (options.output) {
815
+ fs.writeFileSync(options.output, JSON.stringify(results, null, 2));
816
+ console.log(chalk.green(`\nCompressed data saved to: ${options.output}`));
817
+ }
818
+ } catch (error) {
819
+ spinner.fail(chalk.red('Failed to compress embeddings'));
820
+ console.error(chalk.red(error.message));
821
+ process.exit(1);
822
+ }
823
+ });
824
+
825
+ // GNN Search command
826
+ gnnCmd
827
+ .command('search')
828
+ .description('Differentiable search with soft attention')
829
+ .requiredOption('-q, --query <json>', 'Query vector as JSON array')
830
+ .requiredOption('-c, --candidates <file>', 'Candidates file (JSON array of vectors)')
831
+ .option('-k, --top-k <number>', 'Number of results', '5')
832
+ .option('-t, --temperature <number>', 'Softmax temperature (lower=sharper)', '1.0')
833
+ .action((options) => {
834
+ requireGnn();
835
+ const spinner = ora('Loading candidates...').start();
836
+
837
+ try {
838
+ const query = JSON.parse(options.query);
839
+ const candidatesData = JSON.parse(fs.readFileSync(options.candidates, 'utf8'));
840
+ const candidates = candidatesData.map(c => c.vector || c);
841
+ const k = parseInt(options.topK);
842
+ const temperature = parseFloat(options.temperature);
843
+
844
+ spinner.text = 'Running differentiable search...';
845
+ const result = differentiableSearch(query, candidates, k, temperature);
846
+
847
+ spinner.succeed(chalk.green(`Found top-${k} results`));
848
+
849
+ console.log(chalk.cyan('\nSearch Results:'));
850
+ console.log(chalk.white(` Query dim: ${chalk.yellow(query.length)}`));
851
+ console.log(chalk.white(` Candidates: ${chalk.yellow(candidates.length)}`));
852
+ console.log(chalk.white(` Temperature: ${chalk.yellow(temperature)}`));
853
+
854
+ console.log(chalk.cyan('\nTop-K Results:'));
855
+ for (let i = 0; i < result.indices.length; i++) {
856
+ const idx = result.indices[i];
857
+ const weight = result.weights[i];
858
+ const id = candidatesData[idx]?.id || `candidate_${idx}`;
859
+ console.log(chalk.white(` ${i + 1}. ${chalk.yellow(id)} (index: ${idx})`));
860
+ console.log(chalk.gray(` Weight: ${weight.toFixed(6)}`));
861
+ }
862
+ } catch (error) {
863
+ spinner.fail(chalk.red('Failed to run search'));
864
+ console.error(chalk.red(error.message));
865
+ process.exit(1);
866
+ }
867
+ });
868
+
869
+ // GNN Info command
870
+ gnnCmd
871
+ .command('info')
872
+ .description('Show GNN module information')
873
+ .action(() => {
874
+ if (!gnnAvailable) {
875
+ console.log(chalk.yellow('\nGNN Module: Not installed'));
876
+ console.log(chalk.white('Install with: npm install @ruvector/gnn'));
877
+ return;
878
+ }
879
+
880
+ console.log(chalk.cyan('\nGNN Module Information'));
881
+ console.log(chalk.white(` Status: ${chalk.green('Available')}`));
882
+ console.log(chalk.white(` Platform: ${chalk.yellow(process.platform)}`));
883
+ console.log(chalk.white(` Architecture: ${chalk.yellow(process.arch)}`));
884
+
885
+ console.log(chalk.cyan('\nAvailable Features:'));
886
+ console.log(chalk.white(` โ€ข RuvectorLayer - GNN layer with multi-head attention`));
887
+ console.log(chalk.white(` โ€ข TensorCompress - Adaptive tensor compression (5 levels)`));
888
+ console.log(chalk.white(` โ€ข differentiableSearch - Soft attention-based search`));
889
+ console.log(chalk.white(` โ€ข hierarchicalForward - Multi-layer GNN processing`));
890
+
891
+ console.log(chalk.cyan('\nCompression Levels:'));
892
+ console.log(chalk.gray(` none (freq > 0.8) - Full precision, hot data`));
893
+ console.log(chalk.gray(` half (freq > 0.4) - ~50% savings, warm data`));
894
+ console.log(chalk.gray(` pq8 (freq > 0.1) - ~8x compression, cool data`));
895
+ console.log(chalk.gray(` pq4 (freq > 0.01) - ~16x compression, cold data`));
896
+ console.log(chalk.gray(` binary (freq <= 0.01) - ~32x compression, archive`));
897
+ });
898
+
899
+ // =============================================================================
900
+ // Attention Commands
901
+ // =============================================================================
902
+
903
+ // Helper to require attention module
904
+ function requireAttention() {
905
+ if (!attentionAvailable) {
906
+ console.error(chalk.red('Error: @ruvector/attention is not installed'));
907
+ console.error(chalk.yellow('Install it with: npm install @ruvector/attention'));
908
+ process.exit(1);
909
+ }
910
+ }
911
+
912
+ // Attention parent command
913
+ const attentionCmd = program
914
+ .command('attention')
915
+ .description('High-performance attention mechanism operations');
916
+
917
+ // Attention compute command - run attention on input vectors
918
+ attentionCmd
919
+ .command('compute')
920
+ .description('Compute attention over input vectors')
921
+ .requiredOption('-q, --query <json>', 'Query vector as JSON array')
922
+ .requiredOption('-k, --keys <file>', 'Keys file (JSON array of vectors)')
923
+ .option('-v, --values <file>', 'Values file (JSON array of vectors, defaults to keys)')
924
+ .option('-t, --type <type>', 'Attention type (dot|multi-head|flash|hyperbolic|linear)', 'dot')
925
+ .option('-h, --heads <number>', 'Number of attention heads (for multi-head)', '4')
926
+ .option('-d, --head-dim <number>', 'Head dimension (for multi-head)', '64')
927
+ .option('--curvature <number>', 'Curvature for hyperbolic attention', '1.0')
928
+ .option('-o, --output <file>', 'Output file for results')
929
+ .action((options) => {
930
+ requireAttention();
931
+ const spinner = ora('Loading keys...').start();
932
+
933
+ try {
934
+ const query = JSON.parse(options.query);
935
+ const keysData = JSON.parse(fs.readFileSync(options.keys, 'utf8'));
936
+ const keys = keysData.map(k => k.vector || k);
937
+
938
+ let values = keys;
939
+ if (options.values) {
940
+ const valuesData = JSON.parse(fs.readFileSync(options.values, 'utf8'));
941
+ values = valuesData.map(v => v.vector || v);
942
+ }
943
+
944
+ spinner.text = `Computing ${options.type} attention...`;
945
+
946
+ let result;
947
+ let attentionWeights;
948
+
949
+ switch (options.type) {
950
+ case 'dot': {
951
+ const attn = new DotProductAttention();
952
+ const queryMat = [query];
953
+ const output = attn.forward(queryMat, keys, values);
954
+ result = output[0];
955
+ attentionWeights = attn.getLastWeights ? attn.getLastWeights()[0] : null;
956
+ break;
957
+ }
958
+ case 'multi-head': {
959
+ const numHeads = parseInt(options.heads);
960
+ const headDim = parseInt(options.headDim);
961
+ const attn = new MultiHeadAttention(query.length, numHeads, headDim);
962
+ const queryMat = [query];
963
+ const output = attn.forward(queryMat, keys, values);
964
+ result = output[0];
965
+ break;
966
+ }
967
+ case 'flash': {
968
+ const attn = new FlashAttention(query.length);
969
+ const queryMat = [query];
970
+ const output = attn.forward(queryMat, keys, values);
971
+ result = output[0];
972
+ break;
973
+ }
974
+ case 'hyperbolic': {
975
+ const curvature = parseFloat(options.curvature);
976
+ const attn = new HyperbolicAttention(query.length, curvature);
977
+ const queryMat = [query];
978
+ const output = attn.forward(queryMat, keys, values);
979
+ result = output[0];
980
+ break;
981
+ }
982
+ case 'linear': {
983
+ const attn = new LinearAttention(query.length);
984
+ const queryMat = [query];
985
+ const output = attn.forward(queryMat, keys, values);
986
+ result = output[0];
987
+ break;
988
+ }
989
+ default:
990
+ throw new Error(`Unknown attention type: ${options.type}`);
991
+ }
992
+
993
+ spinner.succeed(chalk.green(`Attention computed (${options.type})`));
994
+
995
+ console.log(chalk.cyan('\nAttention Results:'));
996
+ console.log(chalk.white(` Type: ${chalk.yellow(options.type)}`));
997
+ console.log(chalk.white(` Query dim: ${chalk.yellow(query.length)}`));
998
+ console.log(chalk.white(` Num keys: ${chalk.yellow(keys.length)}`));
999
+ console.log(chalk.white(` Output dim: ${chalk.yellow(result.length)}`));
1000
+ console.log(chalk.white(` Output: ${chalk.gray(`[${result.slice(0, 4).map(v => v.toFixed(4)).join(', ')}...]`)}`));
1001
+
1002
+ if (attentionWeights) {
1003
+ console.log(chalk.cyan('\nAttention Weights:'));
1004
+ attentionWeights.slice(0, 5).forEach((w, i) => {
1005
+ console.log(chalk.gray(` Key ${i}: ${w.toFixed(4)}`));
1006
+ });
1007
+ if (attentionWeights.length > 5) {
1008
+ console.log(chalk.gray(` ... and ${attentionWeights.length - 5} more`));
1009
+ }
1010
+ }
1011
+
1012
+ if (options.output) {
1013
+ const outputData = { result, attentionWeights };
1014
+ fs.writeFileSync(options.output, JSON.stringify(outputData, null, 2));
1015
+ console.log(chalk.green(`\nResults saved to: ${options.output}`));
1016
+ }
1017
+ } catch (error) {
1018
+ spinner.fail(chalk.red('Failed to compute attention'));
1019
+ console.error(chalk.red(error.message));
1020
+ process.exit(1);
1021
+ }
1022
+ });
1023
+
1024
+ // Attention benchmark command
1025
+ attentionCmd
1026
+ .command('benchmark')
1027
+ .description('Benchmark attention mechanisms')
1028
+ .option('-d, --dimension <number>', 'Vector dimension', '256')
1029
+ .option('-n, --num-vectors <number>', 'Number of vectors', '100')
1030
+ .option('-i, --iterations <number>', 'Benchmark iterations', '100')
1031
+ .option('-t, --types <list>', 'Attention types to benchmark (comma-separated)', 'dot,flash,linear')
1032
+ .action((options) => {
1033
+ requireAttention();
1034
+ const spinner = ora('Setting up benchmark...').start();
1035
+
1036
+ try {
1037
+ const dim = parseInt(options.dimension);
1038
+ const numVectors = parseInt(options.numVectors);
1039
+ const iterations = parseInt(options.iterations);
1040
+ const types = options.types.split(',').map(t => t.trim());
1041
+
1042
+ // Generate random test data
1043
+ spinner.text = 'Generating test data...';
1044
+ const query = Array.from({ length: dim }, () => Math.random());
1045
+ const keys = Array.from({ length: numVectors }, () =>
1046
+ Array.from({ length: dim }, () => Math.random())
1047
+ );
1048
+
1049
+ console.log(chalk.cyan('\nโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'));
1050
+ console.log(chalk.cyan(' Attention Mechanism Benchmark'));
1051
+ console.log(chalk.cyan('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n'));
1052
+
1053
+ console.log(chalk.white(` Dimension: ${chalk.yellow(dim)}`));
1054
+ console.log(chalk.white(` Vectors: ${chalk.yellow(numVectors)}`));
1055
+ console.log(chalk.white(` Iterations: ${chalk.yellow(iterations)}`));
1056
+ console.log('');
1057
+
1058
+ const results = [];
1059
+
1060
+ // Convert to Float32Arrays for compute()
1061
+ const queryF32 = new Float32Array(query);
1062
+ const keysF32 = keys.map(k => new Float32Array(k));
1063
+
1064
+ for (const type of types) {
1065
+ spinner.text = `Benchmarking ${type} attention...`;
1066
+ spinner.start();
1067
+
1068
+ let attn;
1069
+ try {
1070
+ switch (type) {
1071
+ case 'dot':
1072
+ attn = new DotProductAttention(dim);
1073
+ break;
1074
+ case 'flash':
1075
+ attn = new FlashAttention(dim, 64); // dim, block_size
1076
+ break;
1077
+ case 'linear':
1078
+ attn = new LinearAttention(dim, 64); // dim, num_features
1079
+ break;
1080
+ case 'hyperbolic':
1081
+ attn = new HyperbolicAttention(dim, 1.0);
1082
+ break;
1083
+ case 'multi-head':
1084
+ attn = new MultiHeadAttention(dim, 4); // dim, num_heads
1085
+ break;
1086
+ default:
1087
+ console.log(chalk.yellow(` Skipping unknown type: ${type}`));
1088
+ continue;
1089
+ }
1090
+ } catch (e) {
1091
+ console.log(chalk.yellow(` ${type}: not available (${e.message})`));
1092
+ continue;
1093
+ }
1094
+
1095
+ // Warm up
1096
+ for (let i = 0; i < 5; i++) {
1097
+ try {
1098
+ attn.compute(queryF32, keysF32, keysF32);
1099
+ } catch (e) {
1100
+ // Some mechanisms may fail warmup
1101
+ }
1102
+ }
1103
+
1104
+ // Benchmark
1105
+ const start = process.hrtime.bigint();
1106
+ for (let i = 0; i < iterations; i++) {
1107
+ attn.compute(queryF32, keysF32, keysF32);
1108
+ }
1109
+ const end = process.hrtime.bigint();
1110
+ const totalMs = Number(end - start) / 1_000_000;
1111
+ const avgMs = totalMs / iterations;
1112
+ const opsPerSec = 1000 / avgMs;
1113
+
1114
+ results.push({ type, avgMs, opsPerSec });
1115
+ spinner.succeed(chalk.green(`${type}: ${avgMs.toFixed(3)} ms/op (${opsPerSec.toFixed(1)} ops/sec)`));
1116
+ }
1117
+
1118
+ // Summary
1119
+ if (results.length > 0) {
1120
+ console.log(chalk.cyan('\nโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'));
1121
+ console.log(chalk.cyan(' Summary'));
1122
+ console.log(chalk.cyan('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n'));
1123
+
1124
+ const fastest = results.reduce((a, b) => a.avgMs < b.avgMs ? a : b);
1125
+ console.log(chalk.green(` Fastest: ${fastest.type} (${fastest.avgMs.toFixed(3)} ms/op)\n`));
1126
+
1127
+ console.log(chalk.white(' Relative Performance:'));
1128
+ for (const r of results) {
1129
+ const relPerf = (fastest.avgMs / r.avgMs * 100).toFixed(1);
1130
+ const bar = 'โ–ˆ'.repeat(Math.round(relPerf / 5));
1131
+ console.log(chalk.white(` ${r.type.padEnd(12)} ${chalk.cyan(bar)} ${relPerf}%`));
1132
+ }
1133
+ }
1134
+ } catch (error) {
1135
+ spinner.fail(chalk.red('Benchmark failed'));
1136
+ console.error(chalk.red(error.message));
1137
+ process.exit(1);
1138
+ }
1139
+ });
1140
+
1141
+ // Hyperbolic math command
1142
+ attentionCmd
1143
+ .command('hyperbolic')
1144
+ .description('Hyperbolic geometry operations')
1145
+ .requiredOption('-a, --action <type>', 'Action: exp-map|log-map|distance|project|mobius-add')
1146
+ .requiredOption('-v, --vector <json>', 'Input vector(s) as JSON')
1147
+ .option('-b, --vector-b <json>', 'Second vector for binary operations')
1148
+ .option('-c, --curvature <number>', 'Poincarรฉ ball curvature', '1.0')
1149
+ .option('-o, --origin <json>', 'Origin point for exp/log maps')
1150
+ .action((options) => {
1151
+ requireAttention();
1152
+
1153
+ try {
1154
+ const vecArray = JSON.parse(options.vector);
1155
+ const vec = new Float32Array(vecArray);
1156
+ const curvature = parseFloat(options.curvature);
1157
+
1158
+ let result;
1159
+ let description;
1160
+
1161
+ switch (options.action) {
1162
+ case 'exp-map': {
1163
+ const originArray = options.origin ? JSON.parse(options.origin) : Array(vec.length).fill(0);
1164
+ const origin = new Float32Array(originArray);
1165
+ result = expMap(origin, vec, curvature);
1166
+ description = 'Exponential map (tangent โ†’ Poincarรฉ ball)';
1167
+ break;
1168
+ }
1169
+ case 'log-map': {
1170
+ const originArray = options.origin ? JSON.parse(options.origin) : Array(vec.length).fill(0);
1171
+ const origin = new Float32Array(originArray);
1172
+ result = logMap(origin, vec, curvature);
1173
+ description = 'Logarithmic map (Poincarรฉ ball โ†’ tangent)';
1174
+ break;
1175
+ }
1176
+ case 'distance': {
1177
+ if (!options.vectorB) {
1178
+ throw new Error('--vector-b required for distance calculation');
1179
+ }
1180
+ const vecBArray = JSON.parse(options.vectorB);
1181
+ const vecB = new Float32Array(vecBArray);
1182
+ result = poincareDistance(vec, vecB, curvature);
1183
+ description = 'Poincarรฉ distance';
1184
+ break;
1185
+ }
1186
+ case 'project': {
1187
+ result = projectToPoincareBall(vec, curvature);
1188
+ description = 'Project to Poincarรฉ ball';
1189
+ break;
1190
+ }
1191
+ case 'mobius-add': {
1192
+ if (!options.vectorB) {
1193
+ throw new Error('--vector-b required for Mรถbius addition');
1194
+ }
1195
+ const vecBArray = JSON.parse(options.vectorB);
1196
+ const vecB = new Float32Array(vecBArray);
1197
+ result = mobiusAddition(vec, vecB, curvature);
1198
+ description = 'Mรถbius addition';
1199
+ break;
1200
+ }
1201
+ default:
1202
+ throw new Error(`Unknown action: ${options.action}`);
1203
+ }
1204
+
1205
+ console.log(chalk.cyan('\nHyperbolic Operation:'));
1206
+ console.log(chalk.white(` Action: ${chalk.yellow(description)}`));
1207
+ console.log(chalk.white(` Curvature: ${chalk.yellow(curvature)}`));
1208
+
1209
+ if (typeof result === 'number') {
1210
+ console.log(chalk.white(` Result: ${chalk.green(result.toFixed(6))}`));
1211
+ } else {
1212
+ const resultArray = Array.from(result);
1213
+ console.log(chalk.white(` Input dim: ${chalk.yellow(vec.length)}`));
1214
+ console.log(chalk.white(` Output dim: ${chalk.yellow(resultArray.length)}`));
1215
+ console.log(chalk.white(` Result: ${chalk.gray(`[${resultArray.slice(0, 5).map(v => v.toFixed(4)).join(', ')}...]`)}`));
1216
+
1217
+ // Compute norm to verify it's in the ball
1218
+ const norm = Math.sqrt(resultArray.reduce((sum, x) => sum + x * x, 0));
1219
+ console.log(chalk.white(` Norm: ${chalk.yellow(norm.toFixed(6))} ${norm < 1 ? chalk.green('(inside ball)') : chalk.red('(outside ball)')}`));
1220
+ }
1221
+ } catch (error) {
1222
+ console.error(chalk.red('Hyperbolic operation failed:'), error.message);
1223
+ process.exit(1);
1224
+ }
1225
+ });
1226
+
1227
+ // Attention info command
1228
+ attentionCmd
1229
+ .command('info')
1230
+ .description('Show attention module information')
1231
+ .action(() => {
1232
+ if (!attentionAvailable) {
1233
+ console.log(chalk.yellow('\nAttention Module: Not installed'));
1234
+ console.log(chalk.white('Install with: npm install @ruvector/attention'));
1235
+ return;
1236
+ }
1237
+
1238
+ console.log(chalk.cyan('\nAttention Module Information'));
1239
+ console.log(chalk.white(` Status: ${chalk.green('Available')}`));
1240
+ console.log(chalk.white(` Version: ${chalk.yellow(attentionVersion ? attentionVersion() : 'unknown')}`));
1241
+ console.log(chalk.white(` Platform: ${chalk.yellow(process.platform)}`));
1242
+ console.log(chalk.white(` Architecture: ${chalk.yellow(process.arch)}`));
1243
+
1244
+ console.log(chalk.cyan('\nCore Attention Mechanisms:'));
1245
+ console.log(chalk.white(` โ€ข DotProductAttention - Scaled dot-product attention`));
1246
+ console.log(chalk.white(` โ€ข MultiHeadAttention - Multi-head self-attention`));
1247
+ console.log(chalk.white(` โ€ข FlashAttention - Memory-efficient IO-aware attention`));
1248
+ console.log(chalk.white(` โ€ข HyperbolicAttention - Poincarรฉ ball attention`));
1249
+ console.log(chalk.white(` โ€ข LinearAttention - O(n) linear complexity attention`));
1250
+ console.log(chalk.white(` โ€ข MoEAttention - Mixture of Experts attention`));
1251
+
1252
+ console.log(chalk.cyan('\nGraph Attention:'));
1253
+ console.log(chalk.white(` โ€ข GraphRoPeAttention - Rotary position embeddings for graphs`));
1254
+ console.log(chalk.white(` โ€ข EdgeFeaturedAttention - Edge feature-enhanced attention`));
1255
+ console.log(chalk.white(` โ€ข DualSpaceAttention - Euclidean + hyperbolic dual space`));
1256
+ console.log(chalk.white(` โ€ข LocalGlobalAttention - Local-global graph attention`));
1257
+
1258
+ console.log(chalk.cyan('\nHyperbolic Math:'));
1259
+ console.log(chalk.white(` โ€ข expMap, logMap - Exponential/logarithmic maps`));
1260
+ console.log(chalk.white(` โ€ข mobiusAddition - Mรถbius addition in Poincarรฉ ball`));
1261
+ console.log(chalk.white(` โ€ข poincareDistance - Hyperbolic distance metric`));
1262
+ console.log(chalk.white(` โ€ข projectToPoincareBall - Project vectors to ball`));
1263
+
1264
+ console.log(chalk.cyan('\nTraining Utilities:'));
1265
+ console.log(chalk.white(` โ€ข AdamOptimizer, AdamWOptimizer, SgdOptimizer`));
1266
+ console.log(chalk.white(` โ€ข InfoNceLoss, LocalContrastiveLoss`));
1267
+ console.log(chalk.white(` โ€ข CurriculumScheduler, TemperatureAnnealing`));
1268
+ console.log(chalk.white(` โ€ข HardNegativeMiner, InBatchMiner`));
1269
+ });
1270
+
1271
+ // Attention list command - list available mechanisms
1272
+ attentionCmd
1273
+ .command('list')
1274
+ .description('List all available attention mechanisms')
1275
+ .option('-v, --verbose', 'Show detailed information')
1276
+ .action((options) => {
1277
+ console.log(chalk.cyan('\nโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'));
1278
+ console.log(chalk.cyan(' Available Attention Mechanisms'));
1279
+ console.log(chalk.cyan('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n'));
1280
+
1281
+ const mechanisms = [
1282
+ { name: 'DotProductAttention', type: 'core', complexity: 'O(nยฒ)', available: !!DotProductAttention },
1283
+ { name: 'MultiHeadAttention', type: 'core', complexity: 'O(nยฒ)', available: !!MultiHeadAttention },
1284
+ { name: 'FlashAttention', type: 'core', complexity: 'O(nยฒ) IO-optimized', available: !!FlashAttention },
1285
+ { name: 'HyperbolicAttention', type: 'core', complexity: 'O(nยฒ)', available: !!HyperbolicAttention },
1286
+ { name: 'LinearAttention', type: 'core', complexity: 'O(n)', available: !!LinearAttention },
1287
+ { name: 'MoEAttention', type: 'core', complexity: 'O(n*k)', available: !!MoEAttention },
1288
+ { name: 'GraphRoPeAttention', type: 'graph', complexity: 'O(nยฒ)', available: !!GraphRoPeAttention },
1289
+ { name: 'EdgeFeaturedAttention', type: 'graph', complexity: 'O(nยฒ)', available: !!EdgeFeaturedAttention },
1290
+ { name: 'DualSpaceAttention', type: 'graph', complexity: 'O(nยฒ)', available: !!DualSpaceAttention },
1291
+ { name: 'LocalGlobalAttention', type: 'graph', complexity: 'O(n*k)', available: !!LocalGlobalAttention },
1292
+ ];
1293
+
1294
+ console.log(chalk.white(' Core Attention:'));
1295
+ mechanisms.filter(m => m.type === 'core').forEach(m => {
1296
+ const status = m.available ? chalk.green('โœ“') : chalk.red('โœ—');
1297
+ console.log(chalk.white(` ${status} ${m.name.padEnd(22)} ${chalk.gray(m.complexity)}`));
1298
+ });
1299
+
1300
+ console.log(chalk.white('\n Graph Attention:'));
1301
+ mechanisms.filter(m => m.type === 'graph').forEach(m => {
1302
+ const status = m.available ? chalk.green('โœ“') : chalk.red('โœ—');
1303
+ console.log(chalk.white(` ${status} ${m.name.padEnd(22)} ${chalk.gray(m.complexity)}`));
1304
+ });
1305
+
1306
+ if (!attentionAvailable) {
1307
+ console.log(chalk.yellow('\n Note: @ruvector/attention not installed'));
1308
+ console.log(chalk.white(' Install with: npm install @ruvector/attention'));
1309
+ }
1310
+
1311
+ if (options.verbose) {
1312
+ console.log(chalk.cyan('\n Usage Examples:'));
1313
+ console.log(chalk.gray(' # Compute dot-product attention'));
1314
+ console.log(chalk.white(' npx ruvector attention compute -q "[1,2,3]" -k keys.json -t dot'));
1315
+ console.log(chalk.gray('\n # Benchmark attention mechanisms'));
1316
+ console.log(chalk.white(' npx ruvector attention benchmark -d 256 -n 100'));
1317
+ console.log(chalk.gray('\n # Hyperbolic distance'));
1318
+ console.log(chalk.white(' npx ruvector attention hyperbolic -a distance -v "[0.1,0.2]" -b "[0.3,0.4]"'));
1319
+ }
1320
+ });
1321
+
1322
+ // =============================================================================
1323
+ // Doctor Command - Check system health and dependencies
1324
+ // =============================================================================
1325
+
1326
+ program
1327
+ .command('doctor')
1328
+ .description('Check system health and dependencies')
1329
+ .option('-v, --verbose', 'Show detailed information')
1330
+ .action(async (options) => {
1331
+ const { execSync } = require('child_process');
1332
+
1333
+ console.log(chalk.cyan('\nโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'));
1334
+ console.log(chalk.cyan(' RuVector Doctor'));
1335
+ console.log(chalk.cyan('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n'));
1336
+
1337
+ let issues = 0;
1338
+ let warnings = 0;
1339
+
1340
+ // Helper functions
1341
+ const check = (name, condition, fix) => {
1342
+ if (condition) {
1343
+ console.log(chalk.green(` โœ“ ${name}`));
1344
+ return true;
1345
+ } else {
1346
+ console.log(chalk.red(` โœ— ${name}`));
1347
+ if (fix) console.log(chalk.gray(` Fix: ${fix}`));
1348
+ issues++;
1349
+ return false;
1350
+ }
1351
+ };
1352
+
1353
+ const warn = (name, condition, suggestion) => {
1354
+ if (condition) {
1355
+ console.log(chalk.green(` โœ“ ${name}`));
1356
+ return true;
1357
+ } else {
1358
+ console.log(chalk.yellow(` ! ${name}`));
1359
+ if (suggestion) console.log(chalk.gray(` Suggestion: ${suggestion}`));
1360
+ warnings++;
1361
+ return false;
1362
+ }
1363
+ };
1364
+
1365
+ const getVersion = (cmd) => {
1366
+ try {
1367
+ return execSync(cmd, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
1368
+ } catch (e) {
1369
+ return null;
1370
+ }
1371
+ };
1372
+
1373
+ // System Information
1374
+ console.log(chalk.cyan('System Information:'));
1375
+ console.log(chalk.white(` Platform: ${chalk.yellow(process.platform)}`));
1376
+ console.log(chalk.white(` Architecture: ${chalk.yellow(process.arch)}`));
1377
+ console.log(chalk.white(` Node.js: ${chalk.yellow(process.version)}`));
1378
+ console.log('');
1379
+
1380
+ // Node.js Checks
1381
+ console.log(chalk.cyan('Node.js Environment:'));
1382
+ const nodeVersion = parseInt(process.version.slice(1).split('.')[0]);
1383
+ check('Node.js >= 14', nodeVersion >= 14, 'Upgrade Node.js: https://nodejs.org');
1384
+
1385
+ const npmVersion = getVersion('npm --version');
1386
+ if (npmVersion) {
1387
+ console.log(chalk.green(` โœ“ npm ${npmVersion}`));
1388
+ } else {
1389
+ check('npm installed', false, 'Install npm or reinstall Node.js');
1390
+ }
1391
+ console.log('');
1392
+
1393
+ // RuVector Packages
1394
+ console.log(chalk.cyan('RuVector Packages:'));
1395
+
1396
+ // Check @ruvector/core
1397
+ let coreAvailable = false;
1398
+ try {
1399
+ require.resolve('@ruvector/core');
1400
+ coreAvailable = true;
1401
+ console.log(chalk.green(` โœ“ @ruvector/core installed`));
1402
+ } catch (e) {
1403
+ console.log(chalk.yellow(` ! @ruvector/core not found (using WASM fallback)`));
1404
+ warnings++;
1405
+ }
1406
+
1407
+ // Check if native binding works
1408
+ if (coreAvailable && loadRuvector()) {
1409
+ const version = typeof getVersion === 'function' ? getVersion() : null;
1410
+ const impl = typeof getImplementationType === 'function' ? getImplementationType() : 'native';
1411
+ const versionStr = version ? `, v${version}` : '';
1412
+ console.log(chalk.green(` โœ“ Native binding working (${impl}${versionStr})`));
1413
+ } else if (coreAvailable) {
1414
+ console.log(chalk.yellow(` ! Native binding failed to load`));
1415
+ warnings++;
1416
+ }
1417
+
1418
+ // Check @ruvector/gnn
1419
+ if (gnnAvailable) {
1420
+ console.log(chalk.green(` โœ“ @ruvector/gnn installed`));
1421
+ } else {
1422
+ console.log(chalk.gray(` โ—‹ @ruvector/gnn not installed (optional)`));
1423
+ }
1424
+
1425
+ // Check @ruvector/attention
1426
+ if (attentionAvailable) {
1427
+ console.log(chalk.green(` โœ“ @ruvector/attention installed`));
1428
+ } else {
1429
+ console.log(chalk.gray(` โ—‹ @ruvector/attention not installed (optional)`));
1430
+ }
1431
+
1432
+ // Check @ruvector/graph-node
1433
+ try {
1434
+ require.resolve('@ruvector/graph-node');
1435
+ console.log(chalk.green(` โœ“ @ruvector/graph-node installed`));
1436
+ } catch (e) {
1437
+ console.log(chalk.gray(` โ—‹ @ruvector/graph-node not installed (optional)`));
1438
+ }
1439
+ console.log('');
1440
+
1441
+ // Rust Toolchain (optional for development)
1442
+ console.log(chalk.cyan('Rust Toolchain (optional):'));
1443
+
1444
+ const rustVersion = getVersion('rustc --version');
1445
+ if (rustVersion) {
1446
+ console.log(chalk.green(` โœ“ ${rustVersion}`));
1447
+ } else {
1448
+ console.log(chalk.gray(` โ—‹ Rust not installed (only needed for development)`));
1449
+ }
1450
+
1451
+ const cargoVersion = getVersion('cargo --version');
1452
+ if (cargoVersion) {
1453
+ console.log(chalk.green(` โœ“ ${cargoVersion}`));
1454
+ } else if (rustVersion) {
1455
+ console.log(chalk.yellow(` ! cargo not found`));
1456
+ warnings++;
1457
+ }
1458
+ console.log('');
1459
+
1460
+ // Build Tools (optional)
1461
+ if (options.verbose) {
1462
+ console.log(chalk.cyan('Build Tools (for native compilation):'));
1463
+
1464
+ const hasGcc = getVersion('gcc --version');
1465
+ const hasClang = getVersion('clang --version');
1466
+ const hasCc = getVersion('cc --version');
1467
+
1468
+ if (hasGcc || hasClang || hasCc) {
1469
+ console.log(chalk.green(` โœ“ C compiler available`));
1470
+ } else {
1471
+ console.log(chalk.gray(` โ—‹ No C compiler found (only needed for building from source)`));
1472
+ }
1473
+
1474
+ const hasMake = getVersion('make --version');
1475
+ if (hasMake) {
1476
+ console.log(chalk.green(` โœ“ make available`));
1477
+ } else {
1478
+ console.log(chalk.gray(` โ—‹ make not found`));
1479
+ }
1480
+
1481
+ const hasCmake = getVersion('cmake --version');
1482
+ if (hasCmake) {
1483
+ console.log(chalk.green(` โœ“ cmake available`));
1484
+ } else {
1485
+ console.log(chalk.gray(` โ—‹ cmake not found`));
1486
+ }
1487
+ console.log('');
1488
+ }
1489
+
1490
+ // Summary
1491
+ console.log(chalk.cyan('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'));
1492
+ if (issues === 0 && warnings === 0) {
1493
+ console.log(chalk.green('\n โœ“ All checks passed! RuVector is ready to use.\n'));
1494
+ } else if (issues === 0) {
1495
+ console.log(chalk.yellow(`\n ! ${warnings} warning(s) found. RuVector should work but may have limited features.\n`));
1496
+ } else {
1497
+ console.log(chalk.red(`\n โœ— ${issues} issue(s) and ${warnings} warning(s) found.\n`));
1498
+ console.log(chalk.white(' Run "npx ruvector setup" for installation instructions.\n'));
1499
+ }
1500
+ });
1501
+
1502
+ // =============================================================================
1503
+ // Setup Command - Installation instructions
1504
+ // =============================================================================
1505
+
1506
+ program
1507
+ .command('setup')
1508
+ .description('Show installation and setup instructions')
1509
+ .option('--rust', 'Show Rust installation instructions')
1510
+ .option('--npm', 'Show npm package installation instructions')
1511
+ .option('--all', 'Show all installation instructions')
1512
+ .action((options) => {
1513
+ const showAll = options.all || (!options.rust && !options.npm);
1514
+
1515
+ console.log(chalk.cyan('\nโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'));
1516
+ console.log(chalk.cyan(' RuVector Setup Guide'));
1517
+ console.log(chalk.cyan('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n'));
1518
+
1519
+ // Quick install
1520
+ console.log(chalk.cyan('Quick Install (one-liner):'));
1521
+ console.log(chalk.white(' curl -fsSL https://raw.githubusercontent.com/ruvnet/ruvector/main/install.sh | bash'));
1522
+ console.log('');
1523
+
1524
+ if (showAll || options.npm) {
1525
+ console.log(chalk.cyan('โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€'));
1526
+ console.log(chalk.cyan('npm Packages'));
1527
+ console.log(chalk.cyan('โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n'));
1528
+
1529
+ console.log(chalk.yellow('All-in-one CLI:'));
1530
+ console.log(chalk.white(' npm install -g ruvector'));
1531
+ console.log(chalk.white(' npx ruvector'));
1532
+ console.log('');
1533
+
1534
+ console.log(chalk.yellow('Core packages:'));
1535
+ console.log(chalk.white(' npm install @ruvector/core # Vector database'));
1536
+ console.log(chalk.white(' npm install @ruvector/gnn # Graph Neural Networks'));
1537
+ console.log(chalk.white(' npm install @ruvector/graph-node # Hypergraph database'));
1538
+ console.log('');
1539
+
1540
+ console.log(chalk.yellow('Install all optional packages:'));
1541
+ console.log(chalk.white(' npx ruvector install --all'));
1542
+ console.log('');
1543
+
1544
+ console.log(chalk.yellow('List available packages:'));
1545
+ console.log(chalk.white(' npx ruvector install'));
1546
+ console.log('');
1547
+ }
1548
+
1549
+ if (showAll || options.rust) {
1550
+ console.log(chalk.cyan('โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€'));
1551
+ console.log(chalk.cyan('Rust Installation'));
1552
+ console.log(chalk.cyan('โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n'));
1553
+
1554
+ console.log(chalk.yellow('1. Install Rust:'));
1555
+ console.log(chalk.white(' curl --proto \'=https\' --tlsv1.2 -sSf https://sh.rustup.rs | sh'));
1556
+ console.log(chalk.gray(' # Follow the prompts, then restart your terminal or run:'));
1557
+ console.log(chalk.white(' source $HOME/.cargo/env'));
1558
+ console.log('');
1559
+
1560
+ console.log(chalk.yellow('2. Verify installation:'));
1561
+ console.log(chalk.white(' rustc --version'));
1562
+ console.log(chalk.white(' cargo --version'));
1563
+ console.log('');
1564
+
1565
+ console.log(chalk.yellow('3. Add RuVector crates to your project:'));
1566
+ console.log(chalk.white(' cargo add ruvector-core # Vector database'));
1567
+ console.log(chalk.white(' cargo add ruvector-graph # Hypergraph with Cypher'));
1568
+ console.log(chalk.white(' cargo add ruvector-gnn # Graph Neural Networks'));
1569
+ console.log('');
1570
+
1571
+ console.log(chalk.yellow('4. Other available crates:'));
1572
+ console.log(chalk.white(' cargo add ruvector-cluster # Distributed clustering'));
1573
+ console.log(chalk.white(' cargo add ruvector-raft # Raft consensus'));
1574
+ console.log(chalk.white(' cargo add ruvector-replication # Data replication'));
1575
+ console.log(chalk.white(' cargo add ruvector-tiny-dancer-core # AI routing'));
1576
+ console.log(chalk.white(' cargo add ruvector-router-core # Semantic routing'));
1577
+ console.log('');
1578
+
1579
+ console.log(chalk.yellow('Platform-specific notes:'));
1580
+ console.log('');
1581
+
1582
+ if (process.platform === 'darwin') {
1583
+ console.log(chalk.cyan(' macOS:'));
1584
+ console.log(chalk.white(' xcode-select --install # Install command line tools'));
1585
+ console.log('');
1586
+ } else if (process.platform === 'linux') {
1587
+ console.log(chalk.cyan(' Linux (Debian/Ubuntu):'));
1588
+ console.log(chalk.white(' sudo apt-get update'));
1589
+ console.log(chalk.white(' sudo apt-get install build-essential pkg-config libssl-dev'));
1590
+ console.log('');
1591
+ console.log(chalk.cyan(' Linux (RHEL/CentOS):'));
1592
+ console.log(chalk.white(' sudo yum groupinstall "Development Tools"'));
1593
+ console.log(chalk.white(' sudo yum install openssl-devel'));
1594
+ console.log('');
1595
+ } else if (process.platform === 'win32') {
1596
+ console.log(chalk.cyan(' Windows:'));
1597
+ console.log(chalk.white(' # Install Visual Studio Build Tools'));
1598
+ console.log(chalk.white(' # https://visualstudio.microsoft.com/visual-cpp-build-tools/'));
1599
+ console.log(chalk.white(' # Or use WSL2 for best experience'));
1600
+ console.log('');
1601
+ }
1602
+ }
1603
+
1604
+ console.log(chalk.cyan('โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€'));
1605
+ console.log(chalk.cyan('Documentation & Resources'));
1606
+ console.log(chalk.cyan('โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n'));
1607
+
1608
+ console.log(chalk.white(' GitHub: https://github.com/ruvnet/ruvector'));
1609
+ console.log(chalk.white(' npm: https://www.npmjs.com/package/ruvector'));
1610
+ console.log(chalk.white(' crates.io: https://crates.io/crates/ruvector-core'));
1611
+ console.log(chalk.white(' Issues: https://github.com/ruvnet/ruvector/issues'));
1612
+ console.log('');
1613
+
1614
+ console.log(chalk.cyan('Quick Commands:'));
1615
+ console.log(chalk.white(' npx ruvector doctor # Check system health'));
1616
+ console.log(chalk.white(' npx ruvector info # Show version info'));
1617
+ console.log(chalk.white(' npx ruvector benchmark # Run performance test'));
1618
+ console.log(chalk.white(' npx ruvector install # List available packages'));
1619
+ console.log('');
1620
+ });
1621
+
1622
+ // =============================================================================
1623
+ // Graph Commands - Cypher queries and graph operations
1624
+ // =============================================================================
1625
+
1626
+ program
1627
+ .command('graph')
1628
+ .description('Graph database operations (requires @ruvector/graph-node)')
1629
+ .option('-q, --query <cypher>', 'Execute Cypher query')
1630
+ .option('-c, --create <label>', 'Create a node with label')
1631
+ .option('-p, --properties <json>', 'Node properties as JSON')
1632
+ .option('-r, --relate <spec>', 'Create relationship (from:rel:to)')
1633
+ .option('--info', 'Show graph info and stats')
1634
+ .action(async (options) => {
1635
+ let graphNode;
1636
+ try {
1637
+ graphNode = require('@ruvector/graph-node');
1638
+ } catch (e) {
1639
+ console.log(chalk.yellow('\n @ruvector/graph-node is not installed.\n'));
1640
+ console.log(chalk.cyan(' Install with:'));
1641
+ console.log(chalk.white(' npm install @ruvector/graph-node\n'));
1642
+ console.log(chalk.cyan(' Features:'));
1643
+ console.log(chalk.gray(' - Cypher query language support'));
1644
+ console.log(chalk.gray(' - Hypergraph data structure'));
1645
+ console.log(chalk.gray(' - Knowledge graph operations'));
1646
+ console.log(chalk.gray(' - Neo4j-compatible syntax\n'));
1647
+ console.log(chalk.cyan(' Example usage:'));
1648
+ console.log(chalk.white(' npx ruvector graph --query "CREATE (n:Person {name: \'Alice\'})"'));
1649
+ console.log(chalk.white(' npx ruvector graph --query "MATCH (n) RETURN n"'));
1650
+ console.log('');
1651
+ return;
1652
+ }
1653
+
1654
+ console.log(chalk.cyan('\nโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'));
1655
+ console.log(chalk.cyan(' RuVector Graph'));
1656
+ console.log(chalk.cyan('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n'));
1657
+
1658
+ if (options.info) {
1659
+ console.log(chalk.green(' @ruvector/graph-node is available!'));
1660
+ console.log(chalk.gray(` Platform: ${process.platform}-${process.arch}`));
1661
+ console.log('');
1662
+ console.log(chalk.yellow(' Available operations:'));
1663
+ console.log(chalk.white(' --query <cypher> Execute Cypher query'));
1664
+ console.log(chalk.white(' --create <label> Create node with label'));
1665
+ console.log(chalk.white(' --relate <spec> Create relationship'));
1666
+ console.log('');
1667
+ return;
1668
+ }
1669
+
1670
+ if (options.query) {
1671
+ console.log(chalk.yellow(' Cypher Query:'), chalk.white(options.query));
1672
+ console.log('');
1673
+ // Actual implementation would execute the query
1674
+ console.log(chalk.gray(' Note: Full Cypher execution requires running ruvector-server'));
1675
+ console.log(chalk.gray(' See: npx ruvector server --help'));
1676
+ }
1677
+
1678
+ if (options.create) {
1679
+ const label = options.create;
1680
+ const props = options.properties ? JSON.parse(options.properties) : {};
1681
+ console.log(chalk.yellow(' Creating node:'), chalk.white(label));
1682
+ console.log(chalk.gray(' Properties:'), JSON.stringify(props, null, 2));
1683
+ }
1684
+
1685
+ console.log('');
1686
+ });
1687
+
1688
+ // =============================================================================
1689
+ // Router Commands - AI agent routing
1690
+ // =============================================================================
1691
+
1692
+ program
1693
+ .command('router')
1694
+ .description('AI semantic router operations (requires ruvector-router-core)')
1695
+ .option('--route <text>', 'Route text to best matching intent')
1696
+ .option('--intents <file>', 'Load intents from JSON file')
1697
+ .option('--add-intent <name>', 'Add new intent')
1698
+ .option('--examples <json>', 'Example utterances for intent')
1699
+ .option('--info', 'Show router info')
1700
+ .action(async (options) => {
1701
+ console.log(chalk.cyan('\nโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'));
1702
+ console.log(chalk.cyan(' RuVector Router'));
1703
+ console.log(chalk.cyan('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n'));
1704
+
1705
+ console.log(chalk.yellow(' Semantic Router for AI Agent Routing\n'));
1706
+
1707
+ if (options.info || (!options.route && !options.intents && !options.addIntent)) {
1708
+ console.log(chalk.cyan(' Features:'));
1709
+ console.log(chalk.gray(' - Semantic intent matching'));
1710
+ console.log(chalk.gray(' - Multi-agent routing'));
1711
+ console.log(chalk.gray(' - Dynamic intent registration'));
1712
+ console.log(chalk.gray(' - Vector-based similarity matching'));
1713
+ console.log('');
1714
+ console.log(chalk.cyan(' Status:'), chalk.yellow('Coming Soon'));
1715
+ console.log(chalk.gray(' The npm package for router is in development.'));
1716
+ console.log(chalk.gray(' Rust crate available: cargo add ruvector-router-core'));
1717
+ console.log('');
1718
+ console.log(chalk.cyan(' Usage (when available):'));
1719
+ console.log(chalk.white(' npx ruvector router --route "What is the weather?"'));
1720
+ console.log(chalk.white(' npx ruvector router --intents intents.json --route "query"'));
1721
+ console.log('');
1722
+ return;
1723
+ }
1724
+
1725
+ if (options.route) {
1726
+ console.log(chalk.yellow(' Input:'), chalk.white(options.route));
1727
+ console.log(chalk.gray(' Router package not yet available in npm.'));
1728
+ console.log(chalk.gray(' Check issue #20 for roadmap.'));
1729
+ }
1730
+
1731
+ console.log('');
1732
+ });
1733
+
1734
+ // =============================================================================
1735
+ // Server Commands - HTTP/gRPC server
1736
+ // =============================================================================
1737
+
1738
+ program
1739
+ .command('server')
1740
+ .description('Start RuVector HTTP/gRPC server')
1741
+ .option('-p, --port <number>', 'HTTP port', '8080')
1742
+ .option('-g, --grpc-port <number>', 'gRPC port', '50051')
1743
+ .option('-d, --data-dir <path>', 'Data directory', './ruvector-data')
1744
+ .option('--http-only', 'Start only HTTP server')
1745
+ .option('--grpc-only', 'Start only gRPC server')
1746
+ .option('--cors', 'Enable CORS for all origins')
1747
+ .option('--info', 'Show server info')
1748
+ .action(async (options) => {
1749
+ console.log(chalk.cyan('\nโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'));
1750
+ console.log(chalk.cyan(' RuVector Server'));
1751
+ console.log(chalk.cyan('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n'));
1752
+
1753
+ if (options.info || Object.keys(options).filter(k => k !== 'port' && k !== 'grpcPort' && k !== 'dataDir').length === 0) {
1754
+ console.log(chalk.cyan(' Status:'), chalk.yellow('Coming Soon'));
1755
+ console.log('');
1756
+ console.log(chalk.cyan(' Planned Features:'));
1757
+ console.log(chalk.gray(' - REST API for vector operations'));
1758
+ console.log(chalk.gray(' - gRPC high-performance interface'));
1759
+ console.log(chalk.gray(' - WebSocket real-time updates'));
1760
+ console.log(chalk.gray(' - OpenAPI/Swagger documentation'));
1761
+ console.log(chalk.gray(' - Prometheus metrics endpoint'));
1762
+ console.log(chalk.gray(' - Health check endpoints'));
1763
+ console.log('');
1764
+ console.log(chalk.cyan(' Rust binary available:'));
1765
+ console.log(chalk.white(' cargo install ruvector-server # When published'));
1766
+ console.log('');
1767
+ console.log(chalk.cyan(' Configuration (when available):'));
1768
+ console.log(chalk.white(` --port ${options.port} # HTTP port`));
1769
+ console.log(chalk.white(` --grpc-port ${options.grpcPort} # gRPC port`));
1770
+ console.log(chalk.white(` --data-dir ${options.dataDir} # Data directory`));
1771
+ console.log('');
1772
+ console.log(chalk.gray(' Track progress: https://github.com/ruvnet/ruvector/issues/20'));
1773
+ console.log('');
1774
+ return;
1775
+ }
1776
+
1777
+ console.log(chalk.yellow(' Server package not yet available.'));
1778
+ console.log(chalk.gray(' Check issue #20 for roadmap.'));
1779
+ console.log('');
1780
+ });
1781
+
1782
+ // =============================================================================
1783
+ // Cluster Commands - Distributed operations
1784
+ // =============================================================================
1785
+
1786
+ program
1787
+ .command('cluster')
1788
+ .description('Distributed cluster operations')
1789
+ .option('--status', 'Show cluster status')
1790
+ .option('--join <address>', 'Join existing cluster')
1791
+ .option('--leave', 'Leave cluster')
1792
+ .option('--nodes', 'List cluster nodes')
1793
+ .option('--leader', 'Show current leader')
1794
+ .option('--info', 'Show cluster info')
1795
+ .action(async (options) => {
1796
+ console.log(chalk.cyan('\nโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'));
1797
+ console.log(chalk.cyan(' RuVector Cluster'));
1798
+ console.log(chalk.cyan('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n'));
1799
+
1800
+ console.log(chalk.cyan(' Status:'), chalk.yellow('Coming Soon'));
1801
+ console.log('');
1802
+ console.log(chalk.cyan(' Features:'));
1803
+ console.log(chalk.gray(' - Raft consensus for leader election'));
1804
+ console.log(chalk.gray(' - Automatic failover'));
1805
+ console.log(chalk.gray(' - Data replication'));
1806
+ console.log(chalk.gray(' - Sharding support'));
1807
+ console.log(chalk.gray(' - Distributed queries'));
1808
+ console.log('');
1809
+ console.log(chalk.cyan(' Rust crates available:'));
1810
+ console.log(chalk.white(' cargo add ruvector-cluster # Clustering'));
1811
+ console.log(chalk.white(' cargo add ruvector-raft # Raft consensus'));
1812
+ console.log(chalk.white(' cargo add ruvector-replication # Replication'));
1813
+ console.log('');
1814
+ console.log(chalk.cyan(' Commands (when available):'));
1815
+ console.log(chalk.white(' npx ruvector cluster --status'));
1816
+ console.log(chalk.white(' npx ruvector cluster --join 192.168.1.10:7000'));
1817
+ console.log(chalk.white(' npx ruvector cluster --nodes'));
1818
+ console.log('');
1819
+ console.log(chalk.gray(' Track progress: https://github.com/ruvnet/ruvector/issues/20'));
1820
+ console.log('');
1821
+ });
1822
+
1823
+ // =============================================================================
1824
+ // Export/Import Commands - Database backup/restore
1825
+ // =============================================================================
1826
+
1827
+ program
1828
+ .command('export <database>')
1829
+ .description('Export database to file')
1830
+ .option('-o, --output <file>', 'Output file path')
1831
+ .option('-f, --format <type>', 'Export format (json|binary|parquet)', 'json')
1832
+ .option('--compress', 'Compress output')
1833
+ .option('--vectors-only', 'Export only vectors (no metadata)')
1834
+ .action(async (dbPath, options) => {
1835
+ requireRuvector();
1836
+ const spinner = ora('Exporting database...').start();
1837
+
1838
+ try {
1839
+ const outputFile = options.output || `${dbPath.replace(/\/$/, '')}_export.${options.format}`;
1840
+
1841
+ // Load database
1842
+ const db = new VectorDB({ dimension: 384 }); // Will be overwritten by load
1843
+ if (fs.existsSync(dbPath)) {
1844
+ db.load(dbPath);
1845
+ } else {
1846
+ spinner.fail(chalk.red(`Database not found: ${dbPath}`));
1847
+ process.exit(1);
1848
+ }
1849
+
1850
+ const stats = db.getStats();
1851
+ const data = {
1852
+ version: packageJson.version,
1853
+ exportedAt: new Date().toISOString(),
1854
+ stats: stats,
1855
+ vectors: [] // Would contain actual vector data
1856
+ };
1857
+
1858
+ if (options.format === 'json') {
1859
+ fs.writeFileSync(outputFile, JSON.stringify(data, null, 2));
1860
+ } else {
1861
+ spinner.fail(chalk.yellow(`Format '${options.format}' not yet supported. Using JSON.`));
1862
+ fs.writeFileSync(outputFile.replace(/\.[^.]+$/, '.json'), JSON.stringify(data, null, 2));
1863
+ }
1864
+
1865
+ spinner.succeed(chalk.green(`Exported to: ${outputFile}`));
1866
+ console.log(chalk.gray(` Vectors: ${stats.count || 0}`));
1867
+ console.log(chalk.gray(` Format: ${options.format}`));
1868
+ } catch (error) {
1869
+ spinner.fail(chalk.red('Export failed'));
1870
+ console.error(chalk.red(error.message));
1871
+ process.exit(1);
1872
+ }
1873
+ });
1874
+
1875
+ program
1876
+ .command('import <file>')
1877
+ .description('Import database from file')
1878
+ .option('-d, --database <path>', 'Target database path')
1879
+ .option('--merge', 'Merge with existing data')
1880
+ .option('--replace', 'Replace existing data')
1881
+ .action(async (file, options) => {
1882
+ requireRuvector();
1883
+ const spinner = ora('Importing database...').start();
1884
+
1885
+ try {
1886
+ if (!fs.existsSync(file)) {
1887
+ spinner.fail(chalk.red(`File not found: ${file}`));
1888
+ process.exit(1);
1889
+ }
1890
+
1891
+ const data = JSON.parse(fs.readFileSync(file, 'utf8'));
1892
+ const dbPath = options.database || file.replace(/_export\.json$/, '');
1893
+
1894
+ spinner.text = 'Creating database...';
1895
+
1896
+ const db = new VectorDB({
1897
+ dimension: data.stats?.dimension || 384,
1898
+ path: dbPath,
1899
+ autoPersist: true
1900
+ });
1901
+
1902
+ // Would import actual vectors here
1903
+ db.save(dbPath);
1904
+
1905
+ spinner.succeed(chalk.green(`Imported to: ${dbPath}`));
1906
+ console.log(chalk.gray(` Source version: ${data.version}`));
1907
+ console.log(chalk.gray(` Exported at: ${data.exportedAt}`));
1908
+ } catch (error) {
1909
+ spinner.fail(chalk.red('Import failed'));
1910
+ console.error(chalk.red(error.message));
1911
+ process.exit(1);
1912
+ }
1913
+ });
1914
+
1915
+ // =============================================================================
1916
+ // Embed Command - Generate embeddings
1917
+ // =============================================================================
1918
+
1919
+ program
1920
+ .command('embed')
1921
+ .description('Generate embeddings from text')
1922
+ .option('-t, --text <string>', 'Text to embed')
1923
+ .option('-f, --file <path>', 'File containing text (one per line)')
1924
+ .option('-m, --model <name>', 'Embedding model', 'all-minilm-l6-v2')
1925
+ .option('-o, --output <file>', 'Output file for embeddings')
1926
+ .option('--info', 'Show embedding info')
1927
+ .action(async (options) => {
1928
+ console.log(chalk.cyan('\nโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'));
1929
+ console.log(chalk.cyan(' RuVector Embed'));
1930
+ console.log(chalk.cyan('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n'));
1931
+
1932
+ if (options.info || (!options.text && !options.file)) {
1933
+ console.log(chalk.cyan(' Generate vector embeddings from text\n'));
1934
+ console.log(chalk.cyan(' Supported Models:'));
1935
+ console.log(chalk.gray(' - all-minilm-l6-v2 (384 dims, fast)'));
1936
+ console.log(chalk.gray(' - nomic-embed-text-v1.5 (768 dims, balanced)'));
1937
+ console.log(chalk.gray(' - openai/text-embedding-3-small (1536 dims, requires API key)'));
1938
+ console.log('');
1939
+ console.log(chalk.cyan(' Status:'), chalk.yellow('Coming Soon'));
1940
+ console.log(chalk.gray(' Built-in embedding generation is planned for future release.'));
1941
+ console.log('');
1942
+ console.log(chalk.cyan(' Current options:'));
1943
+ console.log(chalk.gray(' 1. Use external embedding API (OpenAI, Cohere, etc.)'));
1944
+ console.log(chalk.gray(' 2. Use transformers.js in your application'));
1945
+ console.log(chalk.gray(' 3. Pre-generate embeddings with Python'));
1946
+ console.log('');
1947
+ console.log(chalk.cyan(' Usage (when available):'));
1948
+ console.log(chalk.white(' npx ruvector embed --text "Hello world"'));
1949
+ console.log(chalk.white(' npx ruvector embed --file texts.txt --output embeddings.json'));
1950
+ console.log('');
1951
+ return;
1952
+ }
1953
+
1954
+ if (options.text) {
1955
+ console.log(chalk.yellow(' Input text:'), chalk.white(options.text.substring(0, 50) + '...'));
1956
+ console.log(chalk.yellow(' Model:'), chalk.white(options.model));
1957
+ console.log('');
1958
+ console.log(chalk.gray(' Embedding generation not yet available in CLI.'));
1959
+ console.log(chalk.gray(' Use the SDK or external embedding services.'));
1960
+ }
1961
+
1962
+ console.log('');
1963
+ });
1964
+
1965
+ // =============================================================================
1966
+ // Demo Command - Interactive tutorial
1967
+ // =============================================================================
1968
+
1969
+ program
1970
+ .command('demo')
1971
+ .description('Run interactive demo and tutorials')
1972
+ .option('--basic', 'Basic vector operations demo')
1973
+ .option('--gnn', 'GNN differentiable search demo')
1974
+ .option('--graph', 'Graph database demo')
1975
+ .option('--benchmark', 'Performance benchmark demo')
1976
+ .option('-i, --interactive', 'Interactive mode')
1977
+ .action(async (options) => {
1978
+ console.log(chalk.cyan('\nโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'));
1979
+ console.log(chalk.cyan(' RuVector Demo'));
1980
+ console.log(chalk.cyan('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n'));
1981
+
1982
+ const showMenu = !options.basic && !options.gnn && !options.graph && !options.benchmark;
1983
+
1984
+ if (showMenu) {
1985
+ console.log(chalk.yellow(' Available Demos:\n'));
1986
+ console.log(chalk.white(' --basic '), chalk.gray('Basic vector operations (insert, search, delete)'));
1987
+ console.log(chalk.white(' --gnn '), chalk.gray('GNN differentiable search with gradients'));
1988
+ console.log(chalk.white(' --graph '), chalk.gray('Graph database and Cypher queries'));
1989
+ console.log(chalk.white(' --benchmark '), chalk.gray('Performance benchmark suite'));
1990
+ console.log('');
1991
+ console.log(chalk.cyan(' Run a demo:'));
1992
+ console.log(chalk.white(' npx ruvector demo --basic'));
1993
+ console.log(chalk.white(' npx ruvector demo --gnn'));
1994
+ console.log('');
1995
+ return;
1996
+ }
1997
+
1998
+ if (options.basic) {
1999
+ requireRuvector();
2000
+ console.log(chalk.yellow(' Basic Vector Operations Demo\n'));
2001
+
2002
+ const spinner = ora('Creating demo database...').start();
2003
+
2004
+ try {
2005
+ const db = new VectorDB({ dimension: 4, metric: 'cosine' });
2006
+
2007
+ spinner.text = 'Inserting vectors...';
2008
+ db.insert('vec1', [1.0, 0.0, 0.0, 0.0], { label: 'x-axis' });
2009
+ db.insert('vec2', [0.0, 1.0, 0.0, 0.0], { label: 'y-axis' });
2010
+ db.insert('vec3', [0.0, 0.0, 1.0, 0.0], { label: 'z-axis' });
2011
+ db.insert('vec4', [0.7, 0.7, 0.0, 0.0], { label: 'xy-diagonal' });
2012
+
2013
+ spinner.succeed('Demo database created with 4 vectors');
2014
+
2015
+ console.log(chalk.cyan('\n Vectors inserted:'));
2016
+ console.log(chalk.gray(' vec1: [1,0,0,0] - x-axis'));
2017
+ console.log(chalk.gray(' vec2: [0,1,0,0] - y-axis'));
2018
+ console.log(chalk.gray(' vec3: [0,0,1,0] - z-axis'));
2019
+ console.log(chalk.gray(' vec4: [0.7,0.7,0,0] - xy-diagonal'));
2020
+
2021
+ console.log(chalk.cyan('\n Searching for nearest to [0.8, 0.6, 0, 0]:'));
2022
+ const results = db.search([0.8, 0.6, 0.0, 0.0], 3);
2023
+ results.forEach((r, i) => {
2024
+ console.log(chalk.gray(` ${i + 1}. ${r.id} (score: ${r.score.toFixed(4)})`));
2025
+ });
2026
+
2027
+ console.log(chalk.green('\n Demo complete!'));
2028
+ } catch (error) {
2029
+ spinner.fail(chalk.red('Demo failed'));
2030
+ console.error(chalk.red(error.message));
2031
+ }
2032
+ }
2033
+
2034
+ if (options.gnn) {
2035
+ if (!gnnAvailable) {
2036
+ console.log(chalk.yellow(' @ruvector/gnn not installed.'));
2037
+ console.log(chalk.white(' Install with: npm install @ruvector/gnn'));
2038
+ console.log('');
2039
+ return;
2040
+ }
2041
+
2042
+ console.log(chalk.yellow(' GNN Differentiable Search Demo\n'));
2043
+
2044
+ try {
2045
+ console.log(chalk.cyan(' Running differentiable search with gradients...\n'));
2046
+
2047
+ const queryVec = [1.0, 0.5, 0.3, 0.1];
2048
+ const dbVectors = [
2049
+ [1.0, 0.0, 0.0, 0.0],
2050
+ [0.0, 1.0, 0.0, 0.0],
2051
+ [0.5, 0.5, 0.5, 0.5],
2052
+ [0.9, 0.4, 0.2, 0.1]
2053
+ ];
2054
+
2055
+ const result = differentiableSearch(queryVec, dbVectors, 3, 10.0);
2056
+
2057
+ console.log(chalk.cyan(' Query:'), JSON.stringify(queryVec));
2058
+ console.log(chalk.cyan(' Top 3 results:'));
2059
+ result.indices.forEach((idx, i) => {
2060
+ console.log(chalk.gray(` ${i + 1}. Index ${idx} (attention: ${result.attention_weights[i].toFixed(4)})`));
2061
+ });
2062
+
2063
+ console.log(chalk.cyan('\n Gradient flow enabled:'), chalk.green('Yes'));
2064
+ console.log(chalk.gray(' Use for: Neural network training, learned retrieval'));
2065
+
2066
+ console.log(chalk.green('\n GNN demo complete!'));
2067
+ } catch (error) {
2068
+ console.error(chalk.red('GNN demo failed:', error.message));
2069
+ }
2070
+ }
2071
+
2072
+ if (options.graph) {
2073
+ console.log(chalk.yellow(' Graph Database Demo\n'));
2074
+
2075
+ let graphNode;
2076
+ try {
2077
+ graphNode = require('@ruvector/graph-node');
2078
+ console.log(chalk.green(' @ruvector/graph-node is available!'));
2079
+ console.log(chalk.gray(' Full graph demo coming soon.'));
2080
+ } catch (e) {
2081
+ console.log(chalk.yellow(' @ruvector/graph-node not installed.'));
2082
+ console.log(chalk.white(' Install with: npm install @ruvector/graph-node'));
2083
+ }
2084
+ console.log('');
2085
+ }
2086
+
2087
+ if (options.benchmark) {
2088
+ console.log(chalk.yellow(' Redirecting to benchmark command...\n'));
2089
+ console.log(chalk.white(' Run: npx ruvector benchmark'));
2090
+ console.log('');
2091
+ }
2092
+ });
2093
+
2094
+ // ============================================
2095
+ // Self-Learning Intelligence Hooks
2096
+ // ============================================
2097
+
2098
+ const INTEL_PATH = path.join(require('os').homedir(), '.ruvector', 'intelligence.json');
2099
+
2100
+ class Intelligence {
2101
+ constructor() {
2102
+ this.data = this.load();
2103
+ this.alpha = 0.1;
2104
+ this.lastEditedFile = null;
2105
+ }
2106
+
2107
+ load() {
2108
+ try {
2109
+ if (fs.existsSync(INTEL_PATH)) {
2110
+ return JSON.parse(fs.readFileSync(INTEL_PATH, 'utf-8'));
2111
+ }
2112
+ } catch {}
2113
+ return {
2114
+ patterns: {},
2115
+ memories: [],
2116
+ trajectories: [],
2117
+ errors: {},
2118
+ file_sequences: [],
2119
+ agents: {},
2120
+ edges: [],
2121
+ stats: { total_patterns: 0, total_memories: 0, total_trajectories: 0, total_errors: 0, session_count: 0, last_session: 0 }
2122
+ };
2123
+ }
2124
+
2125
+ save() {
2126
+ const dir = path.dirname(INTEL_PATH);
2127
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
2128
+ fs.writeFileSync(INTEL_PATH, JSON.stringify(this.data, null, 2));
2129
+ }
2130
+
2131
+ now() { return Math.floor(Date.now() / 1000); }
2132
+
2133
+ embed(text) {
2134
+ const embedding = new Array(64).fill(0);
2135
+ for (let i = 0; i < text.length; i++) {
2136
+ const idx = (text.charCodeAt(i) + i * 7) % 64;
2137
+ embedding[idx] += 1.0;
2138
+ }
2139
+ const norm = Math.sqrt(embedding.reduce((a, b) => a + b * b, 0));
2140
+ if (norm > 0) for (let i = 0; i < embedding.length; i++) embedding[i] /= norm;
2141
+ return embedding;
2142
+ }
2143
+
2144
+ similarity(a, b) {
2145
+ if (a.length !== b.length) return 0;
2146
+ const dot = a.reduce((sum, v, i) => sum + v * b[i], 0);
2147
+ const normA = Math.sqrt(a.reduce((sum, v) => sum + v * v, 0));
2148
+ const normB = Math.sqrt(b.reduce((sum, v) => sum + v * v, 0));
2149
+ return normA > 0 && normB > 0 ? dot / (normA * normB) : 0;
2150
+ }
2151
+
2152
+ remember(memoryType, content, metadata = {}) {
2153
+ const id = `mem_${this.now()}`;
2154
+ this.data.memories.push({ id, memory_type: memoryType, content, embedding: this.embed(content), metadata, timestamp: this.now() });
2155
+ if (this.data.memories.length > 5000) this.data.memories.splice(0, 1000);
2156
+ this.data.stats.total_memories = this.data.memories.length;
2157
+ return id;
2158
+ }
2159
+
2160
+ recall(query, topK) {
2161
+ const queryEmbed = this.embed(query);
2162
+ return this.data.memories
2163
+ .map(m => ({ score: this.similarity(queryEmbed, m.embedding), memory: m }))
2164
+ .sort((a, b) => b.score - a.score).slice(0, topK).map(r => r.memory);
2165
+ }
2166
+
2167
+ getQ(state, action) {
2168
+ const key = `${state}|${action}`;
2169
+ return this.data.patterns[key]?.q_value ?? 0;
2170
+ }
2171
+
2172
+ updateQ(state, action, reward) {
2173
+ const key = `${state}|${action}`;
2174
+ if (!this.data.patterns[key]) this.data.patterns[key] = { state, action, q_value: 0, visits: 0, last_update: 0 };
2175
+ const p = this.data.patterns[key];
2176
+ p.q_value = p.q_value + this.alpha * (reward - p.q_value);
2177
+ p.visits++;
2178
+ p.last_update = this.now();
2179
+ this.data.stats.total_patterns = Object.keys(this.data.patterns).length;
2180
+ }
2181
+
2182
+ learn(state, action, outcome, reward) {
2183
+ const id = `traj_${this.now()}`;
2184
+ this.updateQ(state, action, reward);
2185
+ this.data.trajectories.push({ id, state, action, outcome, reward, timestamp: this.now() });
2186
+ if (this.data.trajectories.length > 1000) this.data.trajectories.splice(0, 200);
2187
+ this.data.stats.total_trajectories = this.data.trajectories.length;
2188
+ return id;
2189
+ }
2190
+
2191
+ suggest(state, actions) {
2192
+ let bestAction = actions[0] ?? '';
2193
+ let bestQ = -Infinity;
2194
+ for (const action of actions) {
2195
+ const q = this.getQ(state, action);
2196
+ if (q > bestQ) { bestQ = q; bestAction = action; }
2197
+ }
2198
+ return { action: bestAction, confidence: bestQ > 0 ? Math.min(bestQ, 1) : 0 };
2199
+ }
2200
+
2201
+ route(task, file, crateName, operation = 'edit') {
2202
+ const fileType = file ? path.extname(file).slice(1) : 'unknown';
2203
+ const state = `${operation}_${fileType}_in_${crateName ?? 'project'}`;
2204
+ const agentMap = {
2205
+ rs: ['rust-developer', 'coder', 'reviewer', 'tester'],
2206
+ ts: ['typescript-developer', 'coder', 'frontend-dev'],
2207
+ tsx: ['typescript-developer', 'coder', 'frontend-dev'],
2208
+ js: ['coder', 'frontend-dev'],
2209
+ py: ['python-developer', 'coder', 'ml-developer'],
2210
+ md: ['docs-writer', 'coder']
2211
+ };
2212
+ const agents = agentMap[fileType] ?? ['coder', 'reviewer'];
2213
+ const { action, confidence } = this.suggest(state, agents);
2214
+ const reason = confidence > 0.5 ? 'learned from past success' : confidence > 0 ? 'based on patterns' : `default for ${fileType} files`;
2215
+ return { agent: action, confidence, reason };
2216
+ }
2217
+
2218
+ shouldTest(file) {
2219
+ const ext = path.extname(file).slice(1);
2220
+ switch (ext) {
2221
+ case 'rs': {
2222
+ const crateMatch = file.match(/crates\/([^/]+)/);
2223
+ return crateMatch ? { suggest: true, command: `cargo test -p ${crateMatch[1]}` } : { suggest: true, command: 'cargo test' };
2224
+ }
2225
+ case 'ts': case 'tsx': case 'js': case 'jsx': return { suggest: true, command: 'npm test' };
2226
+ case 'py': return { suggest: true, command: 'pytest' };
2227
+ default: return { suggest: false, command: '' };
2228
+ }
2229
+ }
2230
+
2231
+ recordFileSequence(fromFile, toFile) {
2232
+ const existing = this.data.file_sequences.find(s => s.from_file === fromFile && s.to_file === toFile);
2233
+ if (existing) existing.count++;
2234
+ else this.data.file_sequences.push({ from_file: fromFile, to_file: toFile, count: 1 });
2235
+ this.lastEditedFile = toFile;
2236
+ }
2237
+
2238
+ suggestNext(file, limit = 3) {
2239
+ return this.data.file_sequences.filter(s => s.from_file === file).sort((a, b) => b.count - a.count).slice(0, limit).map(s => ({ file: s.to_file, score: s.count }));
2240
+ }
2241
+
2242
+ classifyCommand(command) {
2243
+ const cmd = command.toLowerCase();
2244
+ if (cmd.includes('cargo') || cmd.includes('rustc')) return { category: 'rust', subcategory: cmd.includes('test') ? 'test' : 'build', risk: 'low' };
2245
+ if (cmd.includes('npm') || cmd.includes('node')) return { category: 'javascript', subcategory: cmd.includes('test') ? 'test' : 'build', risk: 'low' };
2246
+ if (cmd.includes('git')) return { category: 'git', subcategory: 'vcs', risk: cmd.includes('push') ? 'medium' : 'low' };
2247
+ if (cmd.includes('rm') || cmd.includes('delete')) return { category: 'filesystem', subcategory: 'destructive', risk: 'high' };
2248
+ return { category: 'shell', subcategory: 'general', risk: 'low' };
2249
+ }
2250
+
2251
+ swarmStats() {
2252
+ const agents = Object.keys(this.data.agents).length;
2253
+ const edges = this.data.edges.length;
2254
+ return { agents, edges };
2255
+ }
2256
+
2257
+ stats() { return this.data.stats; }
2258
+ sessionStart() { this.data.stats.session_count++; this.data.stats.last_session = this.now(); }
2259
+ sessionEnd() {
2260
+ const duration = this.now() - this.data.stats.last_session;
2261
+ const actions = this.data.trajectories.filter(t => t.timestamp >= this.data.stats.last_session).length;
2262
+ return { duration, actions };
2263
+ }
2264
+ getLastEditedFile() { return this.lastEditedFile; }
2265
+ }
2266
+
2267
+ // Hooks command group
2268
+ const hooksCmd = program.command('hooks').description('Self-learning intelligence hooks for Claude Code');
2269
+
2270
+ hooksCmd.command('init').description('Initialize hooks in current project').option('--force', 'Force overwrite').action((opts) => {
2271
+ const settingsPath = path.join(process.cwd(), '.claude', 'settings.json');
2272
+ const settingsDir = path.dirname(settingsPath);
2273
+ if (!fs.existsSync(settingsDir)) fs.mkdirSync(settingsDir, { recursive: true });
2274
+ let settings = {};
2275
+ if (fs.existsSync(settingsPath) && !opts.force) {
2276
+ try { settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8')); } catch {}
2277
+ }
2278
+ settings.hooks = settings.hooks || {};
2279
+ settings.hooks.PreToolUse = [
2280
+ { matcher: 'Edit|Write|MultiEdit', hooks: ['ruvector hooks pre-edit "$TOOL_INPUT_file_path"'] },
2281
+ { matcher: 'Bash', hooks: ['ruvector hooks pre-command "$TOOL_INPUT_command"'] }
2282
+ ];
2283
+ settings.hooks.PostToolUse = [
2284
+ { matcher: 'Edit|Write|MultiEdit', hooks: ['ruvector hooks post-edit "$TOOL_INPUT_file_path"'] },
2285
+ { matcher: 'Bash', hooks: ['ruvector hooks post-command "$TOOL_INPUT_command"'] }
2286
+ ];
2287
+ settings.hooks.SessionStart = ['ruvector hooks session-start'];
2288
+ settings.hooks.Stop = ['ruvector hooks session-end'];
2289
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
2290
+ console.log(chalk.green('โœ… Hooks initialized in .claude/settings.json'));
2291
+ });
2292
+
2293
+ hooksCmd.command('stats').description('Show intelligence statistics').action(() => {
2294
+ const intel = new Intelligence();
2295
+ const stats = intel.stats();
2296
+ const swarm = intel.swarmStats();
2297
+ console.log(chalk.bold.cyan('\n๐Ÿง  RuVector Intelligence Stats\n'));
2298
+ console.log(` ${chalk.green(stats.total_patterns)} Q-learning patterns`);
2299
+ console.log(` ${chalk.green(stats.total_memories)} vector memories`);
2300
+ console.log(` ${chalk.green(stats.total_trajectories)} learning trajectories`);
2301
+ console.log(` ${chalk.green(stats.total_errors)} error patterns\n`);
2302
+ console.log(chalk.bold('Swarm Status:'));
2303
+ console.log(` ${chalk.cyan(swarm.agents)} agents registered`);
2304
+ console.log(` ${chalk.cyan(swarm.edges)} coordination edges`);
2305
+ });
2306
+
2307
+ hooksCmd.command('session-start').description('Session start hook').option('--resume', 'Resume previous session').action(() => {
2308
+ const intel = new Intelligence();
2309
+ intel.sessionStart();
2310
+ intel.save();
2311
+ console.log(chalk.bold.cyan('๐Ÿง  RuVector Intelligence Layer Active'));
2312
+ console.log('โšก Intelligence guides: agent routing, error fixes, file sequences');
2313
+ });
2314
+
2315
+ hooksCmd.command('session-end').description('Session end hook').option('--export-metrics', 'Export metrics').action((opts) => {
2316
+ const intel = new Intelligence();
2317
+ const sessionInfo = intel.sessionEnd();
2318
+ intel.save();
2319
+ console.log('๐Ÿ“Š Session ended. Learning data saved.');
2320
+ if (opts.exportMetrics) console.log(JSON.stringify({ duration_seconds: sessionInfo.duration, actions_recorded: sessionInfo.actions }));
2321
+ });
2322
+
2323
+ hooksCmd.command('pre-edit').description('Pre-edit intelligence').argument('<file>', 'File path').action((file) => {
2324
+ const intel = new Intelligence();
2325
+ const fileName = path.basename(file);
2326
+ const crateMatch = file.match(/crates\/([^/]+)/);
2327
+ const crate = crateMatch?.[1];
2328
+ const { agent, confidence, reason } = intel.route(`edit ${fileName}`, file, crate, 'edit');
2329
+ console.log(chalk.bold('๐Ÿง  Intelligence Analysis:'));
2330
+ console.log(` ๐Ÿ“ ${chalk.cyan(crate ?? 'project')}/${fileName}`);
2331
+ console.log(` ๐Ÿค– Recommended: ${chalk.green.bold(agent)} (${(confidence * 100).toFixed(0)}% confidence)`);
2332
+ if (reason) console.log(` โ†’ ${chalk.dim(reason)}`);
2333
+ const nextFiles = intel.suggestNext(file, 3);
2334
+ if (nextFiles.length > 0) {
2335
+ console.log(' ๐Ÿ“Ž Likely next files:');
2336
+ nextFiles.forEach(n => console.log(` - ${n.file} (${n.score} edits)`));
2337
+ }
2338
+ });
2339
+
2340
+ hooksCmd.command('post-edit').description('Post-edit learning').argument('<file>', 'File path').option('--success', 'Edit succeeded').option('--error <msg>', 'Error message').action((file, opts) => {
2341
+ const intel = new Intelligence();
2342
+ const success = opts.error ? false : (opts.success ?? true);
2343
+ const ext = path.extname(file).slice(1);
2344
+ const crateMatch = file.match(/crates\/([^/]+)/);
2345
+ const crate = crateMatch?.[1] ?? 'project';
2346
+ const state = `edit_${ext}_in_${crate}`;
2347
+ const lastFile = intel.getLastEditedFile();
2348
+ if (lastFile && lastFile !== file) intel.recordFileSequence(lastFile, file);
2349
+ intel.learn(state, success ? 'successful-edit' : 'failed-edit', success ? 'completed' : 'failed', success ? 1.0 : -0.5);
2350
+ intel.remember('edit', `${success ? 'successful' : 'failed'} edit of ${ext} in ${crate}`);
2351
+ intel.save();
2352
+ console.log(`๐Ÿ“Š Learning recorded: ${success ? 'โœ…' : 'โŒ'} ${path.basename(file)}`);
2353
+ const test = intel.shouldTest(file);
2354
+ if (test.suggest) console.log(` ๐Ÿงช Consider: ${chalk.cyan(test.command)}`);
2355
+ });
2356
+
2357
+ hooksCmd.command('pre-command').description('Pre-command intelligence').argument('<command...>', 'Command').action((command) => {
2358
+ const intel = new Intelligence();
2359
+ const cmd = command.join(' ');
2360
+ const classification = intel.classifyCommand(cmd);
2361
+ console.log(chalk.bold('๐Ÿง  Command Analysis:'));
2362
+ console.log(` ๐Ÿ“ฆ Category: ${chalk.cyan(classification.category)}`);
2363
+ console.log(` ๐Ÿท๏ธ Type: ${classification.subcategory}`);
2364
+ if (classification.risk === 'high') console.log(` โš ๏ธ Risk: ${chalk.red('HIGH')} - Review carefully`);
2365
+ else if (classification.risk === 'medium') console.log(` โšก Risk: ${chalk.yellow('MEDIUM')}`);
2366
+ else console.log(` โœ… Risk: ${chalk.green('LOW')}`);
2367
+ });
2368
+
2369
+ hooksCmd.command('post-command').description('Post-command learning').argument('<command...>', 'Command').option('--success', 'Success').option('--error <msg>', 'Error message').action((command, opts) => {
2370
+ const intel = new Intelligence();
2371
+ const cmd = command.join(' ');
2372
+ const success = opts.error ? false : (opts.success ?? true);
2373
+ const classification = intel.classifyCommand(cmd);
2374
+ intel.learn(`cmd_${classification.category}_${classification.subcategory}`, success ? 'success' : 'failure', success ? 'completed' : 'failed', success ? 0.8 : -0.3);
2375
+ intel.remember('command', `${cmd} ${success ? 'succeeded' : 'failed'}`);
2376
+ intel.save();
2377
+ console.log(`๐Ÿ“Š Command ${success ? 'โœ…' : 'โŒ'} recorded`);
2378
+ });
2379
+
2380
+ hooksCmd.command('route').description('Route task to agent').argument('<task...>', 'Task').option('--file <file>', 'File').option('--crate <crate>', 'Crate').action((task, opts) => {
2381
+ const intel = new Intelligence();
2382
+ const result = intel.route(task.join(' '), opts.file, opts.crate);
2383
+ console.log(JSON.stringify({ task: task.join(' '), recommended: result.agent, confidence: result.confidence, reasoning: result.reason }, null, 2));
2384
+ });
2385
+
2386
+ hooksCmd.command('suggest-context').description('Suggest relevant context').action(() => {
2387
+ const intel = new Intelligence();
2388
+ const stats = intel.stats();
2389
+ console.log(`RuVector Intelligence: ${stats.total_patterns} learned patterns, ${stats.total_errors} error fixes available. Use 'ruvector hooks route' for agent suggestions.`);
2390
+ });
2391
+
2392
+ hooksCmd.command('remember').description('Store in memory').requiredOption('-t, --type <type>', 'Memory type').argument('<content...>', 'Content').action((content, opts) => {
2393
+ const intel = new Intelligence();
2394
+ const id = intel.remember(opts.type, content.join(' '));
2395
+ intel.save();
2396
+ console.log(JSON.stringify({ success: true, id }));
2397
+ });
2398
+
2399
+ hooksCmd.command('recall').description('Search memory').argument('<query...>', 'Query').option('-k, --top-k <n>', 'Results', '5').action((query, opts) => {
2400
+ const intel = new Intelligence();
2401
+ const results = intel.recall(query.join(' '), parseInt(opts.topK));
2402
+ console.log(JSON.stringify({ query: query.join(' '), results: results.map(r => ({ type: r.memory_type, content: r.content.slice(0, 200), timestamp: r.timestamp })) }, null, 2));
2403
+ });
2404
+
2405
+ hooksCmd.command('pre-compact').description('Pre-compact hook').option('--auto', 'Auto mode').action(() => {
2406
+ const intel = new Intelligence();
2407
+ intel.save();
2408
+ console.log('๐Ÿ—œ๏ธ Pre-compact: State saved');
2409
+ });
2410
+
2411
+ hooksCmd.command('swarm-recommend').description('Recommend agent for task').argument('<task-type>', 'Task type').action((taskType) => {
2412
+ console.log(JSON.stringify({ task_type: taskType, recommended: 'coder', type: 'default', score: 0.8 }));
2413
+ });
2414
+
2415
+ hooksCmd.command('async-agent').description('Async agent hook').option('--action <action>', 'Action').option('--agent-id <id>', 'Agent ID').option('--task <task>', 'Task').action((opts) => {
2416
+ console.log(JSON.stringify({ action: opts.action, agent_id: opts.agentId, status: 'ok' }));
2417
+ });
2418
+
2419
+ hooksCmd.command('lsp-diagnostic').description('LSP diagnostic hook').option('--file <file>', 'File').option('--severity <sev>', 'Severity').option('--message <msg>', 'Message').action((opts) => {
2420
+ console.log(JSON.stringify({ file: opts.file, severity: opts.severity, action: 'logged' }));
2421
+ });
2422
+
2423
+ hooksCmd.command('track-notification').description('Track notification').action(() => {
2424
+ console.log(JSON.stringify({ tracked: true }));
2425
+ });
2426
+
2427
+ program.parse();