diff --git "a/bin/cli.js" "b/bin/cli.js" new file mode 100755--- /dev/null +++ "b/bin/cli.js" @@ -0,0 +1,7356 @@ +#!/usr/bin/env node + +// Signal CLI context (disables parallel workers - hooks are short-lived) +process.env.RUVECTOR_CLI = '1'; + +const { Command } = require('commander'); +const chalk = require('chalk'); +const ora = require('ora'); +const fs = require('fs'); +const path = require('path'); + +// Lazy load ruvector (only when needed, not for install/help commands) +let VectorDB, getVersion, getImplementationType; +let ruvectorLoaded = false; + +function loadRuvector() { + if (ruvectorLoaded) return true; + try { + const ruvector = require('../dist/index.js'); + VectorDB = ruvector.VectorDB; + getVersion = ruvector.getVersion; + getImplementationType = ruvector.getImplementationType; + ruvectorLoaded = true; + return true; + } catch (e) { + return false; + } +} + +function requireRuvector() { + if (!loadRuvector()) { + console.error(chalk.red('Error: Failed to load ruvector. Please run: npm run build')); + console.error(chalk.yellow('Or install the package: npm install ruvector')); + process.exit(1); + } +} + +// Import GNN (optional - graceful fallback if not available) +let RuvectorLayer, TensorCompress, differentiableSearch, getCompressionLevel, hierarchicalForward; +let gnnAvailable = false; +try { + const gnn = require('@ruvector/gnn'); + RuvectorLayer = gnn.RuvectorLayer; + TensorCompress = gnn.TensorCompress; + differentiableSearch = gnn.differentiableSearch; + getCompressionLevel = gnn.getCompressionLevel; + hierarchicalForward = gnn.hierarchicalForward; + gnnAvailable = true; +} catch (e) { + // GNN not available - commands will show helpful message +} + +// Import Attention (optional - graceful fallback if not available) +let DotProductAttention, MultiHeadAttention, HyperbolicAttention, FlashAttention, LinearAttention, MoEAttention; +let GraphRoPeAttention, EdgeFeaturedAttention, DualSpaceAttention, LocalGlobalAttention; +let benchmarkAttention, computeAttentionAsync, batchAttentionCompute, parallelAttentionCompute; +let expMap, logMap, mobiusAddition, poincareDistance, projectToPoincareBall; +let attentionInfo, attentionVersion; +let attentionAvailable = false; +try { + const attention = require('@ruvector/attention'); + // Core mechanisms + DotProductAttention = attention.DotProductAttention; + MultiHeadAttention = attention.MultiHeadAttention; + HyperbolicAttention = attention.HyperbolicAttention; + FlashAttention = attention.FlashAttention; + LinearAttention = attention.LinearAttention; + MoEAttention = attention.MoEAttention; + // Graph attention + GraphRoPeAttention = attention.GraphRoPeAttention; + EdgeFeaturedAttention = attention.EdgeFeaturedAttention; + DualSpaceAttention = attention.DualSpaceAttention; + LocalGlobalAttention = attention.LocalGlobalAttention; + // Utilities + benchmarkAttention = attention.benchmarkAttention; + computeAttentionAsync = attention.computeAttentionAsync; + batchAttentionCompute = attention.batchAttentionCompute; + parallelAttentionCompute = attention.parallelAttentionCompute; + // Hyperbolic math + expMap = attention.expMap; + logMap = attention.logMap; + mobiusAddition = attention.mobiusAddition; + poincareDistance = attention.poincareDistance; + projectToPoincareBall = attention.projectToPoincareBall; + // Meta + attentionInfo = attention.info; + attentionVersion = attention.version; + attentionAvailable = true; +} catch (e) { + // Attention not available - commands will show helpful message +} + +const program = new Command(); + +// Get package version from package.json +const packageJson = require('../package.json'); + +// Version and description (lazy load implementation info) +program + .name('ruvector') + .description(`${chalk.cyan('ruvector')} - High-performance vector database CLI`) + .version(packageJson.version); + +// Create database +program + .command('create ') + .description('Create a new vector database') + .option('-d, --dimension ', 'Vector dimension', '384') + .option('-m, --metric ', 'Distance metric (cosine|euclidean|dot)', 'cosine') + .action((dbPath, options) => { + requireRuvector(); + const spinner = ora('Creating database...').start(); + + try { + const dimension = parseInt(options.dimension); + const db = new VectorDB({ + dimension, + metric: options.metric, + path: dbPath, + autoPersist: true + }); + + db.save(dbPath); + spinner.succeed(chalk.green(`Database created: ${dbPath}`)); + console.log(chalk.gray(` Dimension: ${dimension}`)); + console.log(chalk.gray(` Metric: ${options.metric}`)); + console.log(chalk.gray(` Implementation: ${getImplementationType()}`)); + } catch (error) { + spinner.fail(chalk.red('Failed to create database')); + console.error(chalk.red(error.message)); + process.exit(1); + } + }); + +// Insert vectors +program + .command('insert ') + .description('Insert vectors from JSON file') + .option('-b, --batch-size ', 'Batch size for insertion', '1000') + .action((dbPath, file, options) => { + requireRuvector(); + const spinner = ora('Loading database...').start(); + + try { + // Read database metadata to get dimension + let dimension = 384; // default + if (fs.existsSync(dbPath)) { + const dbData = fs.readFileSync(dbPath, 'utf8'); + const parsed = JSON.parse(dbData); + dimension = parsed.dimension || 384; + } + + const db = new VectorDB({ dimension }); + + if (fs.existsSync(dbPath)) { + db.load(dbPath); + } + + spinner.text = 'Reading vectors...'; + const data = JSON.parse(fs.readFileSync(file, 'utf8')); + const vectors = Array.isArray(data) ? data : [data]; + + spinner.text = `Inserting ${vectors.length} vectors...`; + const batchSize = parseInt(options.batchSize); + + for (let i = 0; i < vectors.length; i += batchSize) { + const batch = vectors.slice(i, i + batchSize); + db.insertBatch(batch); + spinner.text = `Inserted ${Math.min(i + batchSize, vectors.length)}/${vectors.length} vectors...`; + } + + db.save(dbPath); + spinner.succeed(chalk.green(`Inserted ${vectors.length} vectors`)); + + const stats = db.stats(); + console.log(chalk.gray(` Total vectors: ${stats.count}`)); + } catch (error) { + spinner.fail(chalk.red('Failed to insert vectors')); + console.error(chalk.red(error.message)); + process.exit(1); + } + }); + +// Search vectors +program + .command('search ') + .description('Search for similar vectors') + .requiredOption('-v, --vector ', 'Query vector as JSON array') + .option('-k, --top-k ', 'Number of results', '10') + .option('-t, --threshold ', 'Similarity threshold', '0.0') + .option('-f, --filter ', 'Metadata filter as JSON') + .action((dbPath, options) => { + requireRuvector(); + const spinner = ora('Loading database...').start(); + + try { + // Read database metadata + const dbData = fs.readFileSync(dbPath, 'utf8'); + const parsed = JSON.parse(dbData); + const dimension = parsed.dimension || 384; + + const db = new VectorDB({ dimension }); + db.load(dbPath); + + spinner.text = 'Searching...'; + + const vector = JSON.parse(options.vector); + const query = { + vector, + k: parseInt(options.topK), + threshold: parseFloat(options.threshold) + }; + + if (options.filter) { + query.filter = JSON.parse(options.filter); + } + + const results = db.search(query); + spinner.succeed(chalk.green(`Found ${results.length} results`)); + + console.log(chalk.cyan('\nSearch Results:')); + results.forEach((result, i) => { + console.log(chalk.white(`\n${i + 1}. ID: ${result.id}`)); + console.log(chalk.yellow(` Score: ${result.score.toFixed(4)}`)); + if (result.metadata) { + console.log(chalk.gray(` Metadata: ${JSON.stringify(result.metadata)}`)); + } + }); + } catch (error) { + spinner.fail(chalk.red('Failed to search')); + console.error(chalk.red(error.message)); + process.exit(1); + } + }); + +// Show stats +program + .command('stats ') + .description('Show database statistics') + .action((dbPath) => { + requireRuvector(); + const spinner = ora('Loading database...').start(); + + try { + const dbData = fs.readFileSync(dbPath, 'utf8'); + const parsed = JSON.parse(dbData); + const dimension = parsed.dimension || 384; + + const db = new VectorDB({ dimension }); + db.load(dbPath); + + const stats = db.stats(); + spinner.succeed(chalk.green('Database statistics')); + + console.log(chalk.cyan('\nDatabase Stats:')); + console.log(chalk.white(` Vector Count: ${chalk.yellow(stats.count)}`)); + console.log(chalk.white(` Dimension: ${chalk.yellow(stats.dimension)}`)); + console.log(chalk.white(` Metric: ${chalk.yellow(stats.metric)}`)); + console.log(chalk.white(` Implementation: ${chalk.yellow(getImplementationType())}`)); + + if (stats.memoryUsage) { + const mb = (stats.memoryUsage / (1024 * 1024)).toFixed(2); + console.log(chalk.white(` Memory Usage: ${chalk.yellow(mb + ' MB')}`)); + } + + const fileStats = fs.statSync(dbPath); + const fileMb = (fileStats.size / (1024 * 1024)).toFixed(2); + console.log(chalk.white(` File Size: ${chalk.yellow(fileMb + ' MB')}`)); + } catch (error) { + spinner.fail(chalk.red('Failed to load database')); + console.error(chalk.red(error.message)); + process.exit(1); + } + }); + +// Benchmark +program + .command('benchmark') + .description('Run performance benchmarks') + .option('-d, --dimension ', 'Vector dimension', '384') + .option('-n, --num-vectors ', 'Number of vectors', '10000') + .option('-q, --num-queries ', 'Number of queries', '1000') + .action((options) => { + requireRuvector(); + console.log(chalk.cyan('\nruvector Performance Benchmark')); + console.log(chalk.gray(`Implementation: ${getImplementationType()}\n`)); + + const dimension = parseInt(options.dimension); + const numVectors = parseInt(options.numVectors); + const numQueries = parseInt(options.numQueries); + + let spinner = ora('Creating database...').start(); + + try { + const db = new VectorDB({ dimension, metric: 'cosine' }); + spinner.succeed(); + + // Insert benchmark + spinner = ora(`Inserting ${numVectors} vectors...`).start(); + const insertStart = Date.now(); + + const vectors = []; + for (let i = 0; i < numVectors; i++) { + vectors.push({ + id: `vec_${i}`, + vector: Array.from({ length: dimension }, () => Math.random()), + metadata: { index: i, batch: Math.floor(i / 1000) } + }); + } + + db.insertBatch(vectors); + const insertTime = Date.now() - insertStart; + const insertRate = (numVectors / (insertTime / 1000)).toFixed(0); + + spinner.succeed(chalk.green(`Inserted ${numVectors} vectors in ${insertTime}ms`)); + console.log(chalk.gray(` Rate: ${chalk.yellow(insertRate)} vectors/sec`)); + + // Search benchmark + spinner = ora(`Running ${numQueries} searches...`).start(); + const searchStart = Date.now(); + + for (let i = 0; i < numQueries; i++) { + const query = { + vector: Array.from({ length: dimension }, () => Math.random()), + k: 10 + }; + db.search(query); + } + + const searchTime = Date.now() - searchStart; + const searchRate = (numQueries / (searchTime / 1000)).toFixed(0); + const avgLatency = (searchTime / numQueries).toFixed(2); + + spinner.succeed(chalk.green(`Completed ${numQueries} searches in ${searchTime}ms`)); + console.log(chalk.gray(` Rate: ${chalk.yellow(searchRate)} queries/sec`)); + console.log(chalk.gray(` Avg Latency: ${chalk.yellow(avgLatency)}ms`)); + + // Stats + const stats = db.stats(); + console.log(chalk.cyan('\nFinal Stats:')); + console.log(chalk.white(` Vector Count: ${chalk.yellow(stats.count)}`)); + console.log(chalk.white(` Dimension: ${chalk.yellow(stats.dimension)}`)); + console.log(chalk.white(` Implementation: ${chalk.yellow(getImplementationType())}`)); + + } catch (error) { + spinner.fail(chalk.red('Benchmark failed')); + console.error(chalk.red(error.message)); + process.exit(1); + } + }); + +// Info command +program + .command('info') + .description('Show ruvector information') + .action(() => { + console.log(chalk.cyan('\nruvector Information')); + console.log(chalk.white(` CLI Version: ${chalk.yellow(packageJson.version)}`)); + + // Try to load ruvector for implementation info + if (loadRuvector()) { + const version = typeof getVersion === 'function' ? getVersion() : 'unknown'; + const impl = typeof getImplementationType === 'function' ? getImplementationType() : 'native'; + console.log(chalk.white(` Core Version: ${chalk.yellow(version)}`)); + console.log(chalk.white(` Implementation: ${chalk.yellow(impl)}`)); + } else { + console.log(chalk.white(` Core: ${chalk.gray('Not loaded (install @ruvector/core)')}`)); + } + + console.log(chalk.white(` GNN Module: ${gnnAvailable ? chalk.green('Available') : chalk.gray('Not installed')}`)); + console.log(chalk.white(` Node Version: ${chalk.yellow(process.version)}`)); + console.log(chalk.white(` Platform: ${chalk.yellow(process.platform)}`)); + console.log(chalk.white(` Architecture: ${chalk.yellow(process.arch)}`)); + + if (!gnnAvailable) { + console.log(chalk.gray('\n Install GNN with: npx ruvector install gnn')); + } + }); + +// ============================================================================= +// Install Command +// ============================================================================= + +program + .command('install [packages...]') + .description('Install optional ruvector packages') + .option('-a, --all', 'Install all optional packages') + .option('-l, --list', 'List available packages') + .option('-i, --interactive', 'Interactive package selection') + .action(async (packages, options) => { + const { execSync } = require('child_process'); + + // Available optional packages - all ruvector npm packages + const availablePackages = { + // Core packages + core: { + name: '@ruvector/core', + description: 'Core vector database with native Rust bindings (HNSW, SIMD)', + installed: true, // Always installed with ruvector + category: 'core' + }, + gnn: { + name: '@ruvector/gnn', + description: 'Graph Neural Network layers, tensor compression, differentiable search', + installed: gnnAvailable, + category: 'core' + }, + 'graph-node': { + name: '@ruvector/graph-node', + description: 'Native Node.js bindings for hypergraph database with Cypher queries', + installed: false, + category: 'core' + }, + 'agentic-synth': { + name: '@ruvector/agentic-synth', + description: 'Synthetic data generator for AI/ML training, RAG, and agentic workflows', + installed: false, + category: 'tools' + }, + extensions: { + name: 'ruvector-extensions', + description: 'Advanced features: embeddings, UI, exports, temporal tracking, persistence', + installed: false, + category: 'tools' + }, + // Platform-specific native bindings for @ruvector/core + 'node-linux-x64': { + name: '@ruvector/node-linux-x64-gnu', + description: 'Linux x64 native bindings for @ruvector/core', + installed: false, + category: 'platform' + }, + 'node-linux-arm64': { + name: '@ruvector/node-linux-arm64-gnu', + description: 'Linux ARM64 native bindings for @ruvector/core', + installed: false, + category: 'platform' + }, + 'node-darwin-x64': { + name: '@ruvector/node-darwin-x64', + description: 'macOS Intel x64 native bindings for @ruvector/core', + installed: false, + category: 'platform' + }, + 'node-darwin-arm64': { + name: '@ruvector/node-darwin-arm64', + description: 'macOS Apple Silicon native bindings for @ruvector/core', + installed: false, + category: 'platform' + }, + 'node-win32-x64': { + name: '@ruvector/node-win32-x64-msvc', + description: 'Windows x64 native bindings for @ruvector/core', + installed: false, + category: 'platform' + }, + // Platform-specific native bindings for @ruvector/gnn + 'gnn-linux-x64': { + name: '@ruvector/gnn-linux-x64-gnu', + description: 'Linux x64 native bindings for @ruvector/gnn', + installed: false, + category: 'platform' + }, + 'gnn-linux-arm64': { + name: '@ruvector/gnn-linux-arm64-gnu', + description: 'Linux ARM64 native bindings for @ruvector/gnn', + installed: false, + category: 'platform' + }, + 'gnn-darwin-x64': { + name: '@ruvector/gnn-darwin-x64', + description: 'macOS Intel x64 native bindings for @ruvector/gnn', + installed: false, + category: 'platform' + }, + 'gnn-darwin-arm64': { + name: '@ruvector/gnn-darwin-arm64', + description: 'macOS Apple Silicon native bindings for @ruvector/gnn', + installed: false, + category: 'platform' + }, + 'gnn-win32-x64': { + name: '@ruvector/gnn-win32-x64-msvc', + description: 'Windows x64 native bindings for @ruvector/gnn', + installed: false, + category: 'platform' + }, + // Legacy/standalone packages + 'ruvector-core': { + name: 'ruvector-core', + description: 'Standalone vector database (legacy, use @ruvector/core instead)', + installed: false, + category: 'legacy' + } + }; + + // Check which packages are actually installed + for (const [key, pkg] of Object.entries(availablePackages)) { + if (key !== 'core' && key !== 'gnn') { + try { + require.resolve(pkg.name); + pkg.installed = true; + } catch (e) { + pkg.installed = false; + } + } + } + + // List packages + if (options.list || (packages.length === 0 && !options.all && !options.interactive)) { + console.log(chalk.cyan('\n═══════════════════════════════════════════════════════════════')); + console.log(chalk.cyan(' Ruvector Packages')); + console.log(chalk.cyan('═══════════════════════════════════════════════════════════════\n')); + + const categories = { + core: { title: '📦 Core Packages', packages: [] }, + tools: { title: '🔧 Tools & Extensions', packages: [] }, + platform: { title: '🖥️ Platform Bindings', packages: [] }, + legacy: { title: '📜 Legacy Packages', packages: [] } + }; + + // Group by category + Object.entries(availablePackages).forEach(([key, pkg]) => { + if (categories[pkg.category]) { + categories[pkg.category].packages.push({ key, ...pkg }); + } + }); + + // Display by category + for (const [catKey, cat] of Object.entries(categories)) { + if (cat.packages.length === 0) continue; + + console.log(chalk.cyan(`${cat.title}`)); + console.log(chalk.gray('─'.repeat(60))); + + cat.packages.forEach(pkg => { + const status = pkg.installed ? chalk.green('✓') : chalk.gray('○'); + const statusText = pkg.installed ? chalk.green('installed') : chalk.gray('available'); + console.log(chalk.white(` ${status} ${chalk.yellow(pkg.key.padEnd(18))} ${statusText}`)); + console.log(chalk.gray(` ${pkg.description}`)); + console.log(chalk.gray(` npm: ${chalk.white(pkg.name)}\n`)); + }); + } + + console.log(chalk.cyan('═══════════════════════════════════════════════════════════════')); + console.log(chalk.cyan('Usage:')); + console.log(chalk.white(' npx ruvector install gnn # Install GNN package')); + console.log(chalk.white(' npx ruvector install graph-node # Install graph database')); + console.log(chalk.white(' npx ruvector install agentic-synth # Install data generator')); + console.log(chalk.white(' npx ruvector install --all # Install all core packages')); + console.log(chalk.white(' npx ruvector install -i # Interactive selection')); + console.log(chalk.gray('\n Note: Platform bindings are auto-detected by @ruvector/core')); + return; + } + + // Interactive mode + if (options.interactive) { + const readline = require('readline'); + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); + + console.log(chalk.cyan('\nSelect packages to install:\n')); + + const notInstalled = Object.entries(availablePackages) + .filter(([_, pkg]) => !pkg.installed); + + if (notInstalled.length === 0) { + console.log(chalk.green('All packages are already installed!')); + rl.close(); + return; + } + + notInstalled.forEach(([key, pkg], i) => { + console.log(chalk.white(` ${i + 1}. ${chalk.yellow(key)} - ${pkg.description}`)); + }); + console.log(chalk.white(` ${notInstalled.length + 1}. ${chalk.yellow('all')} - Install all packages`)); + console.log(chalk.white(` 0. ${chalk.gray('cancel')} - Exit without installing`)); + + rl.question(chalk.cyan('\nEnter selection (comma-separated for multiple): '), (answer) => { + rl.close(); + + const selections = answer.split(',').map(s => s.trim()); + let toInstall = []; + + for (const sel of selections) { + if (sel === '0' || sel.toLowerCase() === 'cancel') { + console.log(chalk.yellow('Installation cancelled.')); + return; + } + if (sel === String(notInstalled.length + 1) || sel.toLowerCase() === 'all') { + toInstall = notInstalled.map(([_, pkg]) => pkg.name); + break; + } + const idx = parseInt(sel) - 1; + if (idx >= 0 && idx < notInstalled.length) { + toInstall.push(notInstalled[idx][1].name); + } + } + + if (toInstall.length === 0) { + console.log(chalk.yellow('No valid packages selected.')); + return; + } + + installPackages(toInstall); + }); + return; + } + + // Install all (core + tools only, not platform-specific or legacy) + if (options.all) { + const toInstall = Object.values(availablePackages) + .filter(pkg => !pkg.installed && (pkg.category === 'core' || pkg.category === 'tools')) + .map(pkg => pkg.name); + + if (toInstall.length === 0) { + console.log(chalk.green('All core packages are already installed!')); + return; + } + + console.log(chalk.cyan(`Installing ${toInstall.length} packages...`)); + installPackages(toInstall); + return; + } + + // Install specific packages + const toInstall = []; + for (const pkg of packages) { + const key = pkg.toLowerCase().replace('@ruvector/', ''); + if (availablePackages[key]) { + if (availablePackages[key].installed) { + console.log(chalk.yellow(`${availablePackages[key].name} is already installed`)); + } else { + toInstall.push(availablePackages[key].name); + } + } else { + console.log(chalk.red(`Unknown package: ${pkg}`)); + console.log(chalk.gray(`Available: ${Object.keys(availablePackages).join(', ')}`)); + } + } + + if (toInstall.length > 0) { + installPackages(toInstall); + } + + function installPackages(pkgs) { + const spinner = ora(`Installing ${pkgs.join(', ')}...`).start(); + + try { + // Detect package manager + let pm = 'npm'; + if (fs.existsSync('yarn.lock')) pm = 'yarn'; + else if (fs.existsSync('pnpm-lock.yaml')) pm = 'pnpm'; + else if (fs.existsSync('bun.lockb')) pm = 'bun'; + + const cmd = pm === 'yarn' ? `yarn add ${pkgs.join(' ')}` + : pm === 'pnpm' ? `pnpm add ${pkgs.join(' ')}` + : pm === 'bun' ? `bun add ${pkgs.join(' ')}` + : `npm install ${pkgs.join(' ')}`; + + execSync(cmd, { stdio: 'pipe' }); + + spinner.succeed(chalk.green(`Installed: ${pkgs.join(', ')}`)); + console.log(chalk.cyan('\nRun "npx ruvector info" to verify installation.')); + } catch (error) { + spinner.fail(chalk.red('Installation failed')); + console.error(chalk.red(error.message)); + console.log(chalk.yellow(`\nTry manually: npm install ${pkgs.join(' ')}`)); + process.exit(1); + } + } + }); + +// ============================================================================= +// GNN Commands +// ============================================================================= + +// Helper to check GNN availability +function requireGnn() { + if (!gnnAvailable) { + console.error(chalk.red('Error: GNN module not available.')); + console.error(chalk.yellow('Install it with: npm install @ruvector/gnn')); + process.exit(1); + } +} + +// GNN parent command +const gnnCmd = program + .command('gnn') + .description('Graph Neural Network operations'); + +// GNN Layer command +gnnCmd + .command('layer') + .description('Create and test a GNN layer') + .requiredOption('-i, --input-dim ', 'Input dimension') + .requiredOption('-h, --hidden-dim ', 'Hidden dimension') + .option('-a, --heads ', 'Number of attention heads', '4') + .option('-d, --dropout ', 'Dropout rate', '0.1') + .option('--test', 'Run a test forward pass') + .option('-o, --output ', 'Save layer config to JSON file') + .action((options) => { + requireGnn(); + const spinner = ora('Creating GNN layer...').start(); + + try { + const inputDim = parseInt(options.inputDim); + const hiddenDim = parseInt(options.hiddenDim); + const heads = parseInt(options.heads); + const dropout = parseFloat(options.dropout); + + const layer = new RuvectorLayer(inputDim, hiddenDim, heads, dropout); + spinner.succeed(chalk.green('GNN Layer created')); + + console.log(chalk.cyan('\nLayer Configuration:')); + console.log(chalk.white(` Input Dim: ${chalk.yellow(inputDim)}`)); + console.log(chalk.white(` Hidden Dim: ${chalk.yellow(hiddenDim)}`)); + console.log(chalk.white(` Heads: ${chalk.yellow(heads)}`)); + console.log(chalk.white(` Dropout: ${chalk.yellow(dropout)}`)); + + if (options.test) { + spinner.start('Running test forward pass...'); + + // Create test data + const nodeEmbedding = Array.from({ length: inputDim }, () => Math.random()); + const neighborEmbeddings = [ + Array.from({ length: inputDim }, () => Math.random()), + Array.from({ length: inputDim }, () => Math.random()) + ]; + const edgeWeights = [0.6, 0.4]; + + const output = layer.forward(nodeEmbedding, neighborEmbeddings, edgeWeights); + spinner.succeed(chalk.green('Forward pass completed')); + + console.log(chalk.cyan('\nTest Results:')); + console.log(chalk.white(` Input shape: ${chalk.yellow(`[${inputDim}]`)}`)); + console.log(chalk.white(` Output shape: ${chalk.yellow(`[${output.length}]`)}`)); + console.log(chalk.white(` Output sample: ${chalk.gray(`[${output.slice(0, 4).map(v => v.toFixed(4)).join(', ')}...]`)}`)); + } + + if (options.output) { + const config = layer.toJson(); + fs.writeFileSync(options.output, config); + console.log(chalk.green(`\nLayer config saved to: ${options.output}`)); + } + } catch (error) { + spinner.fail(chalk.red('Failed to create GNN layer')); + console.error(chalk.red(error.message)); + process.exit(1); + } + }); + +// GNN Compress command +gnnCmd + .command('compress') + .description('Compress embeddings using adaptive tensor compression') + .requiredOption('-f, --file ', 'Input JSON file with embeddings') + .option('-l, --level ', 'Compression level (none|half|pq8|pq4|binary)', 'auto') + .option('-a, --access-freq ', 'Access frequency for auto compression (0.0-1.0)', '0.5') + .option('-o, --output ', 'Output file for compressed data') + .action((options) => { + requireGnn(); + const spinner = ora('Loading embeddings...').start(); + + try { + const data = JSON.parse(fs.readFileSync(options.file, 'utf8')); + const embeddings = Array.isArray(data) ? data : [data]; + + spinner.text = 'Compressing embeddings...'; + const compressor = new TensorCompress(); + const accessFreq = parseFloat(options.accessFreq); + + const results = []; + let totalOriginalSize = 0; + let totalCompressedSize = 0; + + for (const embedding of embeddings) { + const vec = embedding.vector || embedding; + totalOriginalSize += vec.length * 4; // float32 = 4 bytes + + let compressed; + if (options.level === 'auto') { + compressed = compressor.compress(vec, accessFreq); + } else { + const levelConfig = { levelType: options.level }; + if (options.level === 'pq8') { + levelConfig.subvectors = 8; + levelConfig.centroids = 256; + } else if (options.level === 'pq4') { + levelConfig.subvectors = 8; + } + compressed = compressor.compressWithLevel(vec, levelConfig); + } + + totalCompressedSize += compressed.length; + results.push({ + id: embedding.id, + compressed + }); + } + + const ratio = (totalOriginalSize / totalCompressedSize).toFixed(2); + const savings = ((1 - totalCompressedSize / totalOriginalSize) * 100).toFixed(1); + + spinner.succeed(chalk.green(`Compressed ${embeddings.length} embeddings`)); + + console.log(chalk.cyan('\nCompression Results:')); + console.log(chalk.white(` Embeddings: ${chalk.yellow(embeddings.length)}`)); + console.log(chalk.white(` Level: ${chalk.yellow(options.level === 'auto' ? `auto (${getCompressionLevel(accessFreq)})` : options.level)}`)); + console.log(chalk.white(` Original: ${chalk.yellow((totalOriginalSize / 1024).toFixed(2) + ' KB')}`)); + console.log(chalk.white(` Compressed: ${chalk.yellow((totalCompressedSize / 1024).toFixed(2) + ' KB')}`)); + console.log(chalk.white(` Ratio: ${chalk.yellow(ratio + 'x')}`)); + console.log(chalk.white(` Savings: ${chalk.yellow(savings + '%')}`)); + + if (options.output) { + fs.writeFileSync(options.output, JSON.stringify(results, null, 2)); + console.log(chalk.green(`\nCompressed data saved to: ${options.output}`)); + } + } catch (error) { + spinner.fail(chalk.red('Failed to compress embeddings')); + console.error(chalk.red(error.message)); + process.exit(1); + } + }); + +// GNN Search command +gnnCmd + .command('search') + .description('Differentiable search with soft attention') + .requiredOption('-q, --query ', 'Query vector as JSON array') + .requiredOption('-c, --candidates ', 'Candidates file (JSON array of vectors)') + .option('-k, --top-k ', 'Number of results', '5') + .option('-t, --temperature ', 'Softmax temperature (lower=sharper)', '1.0') + .action((options) => { + requireGnn(); + const spinner = ora('Loading candidates...').start(); + + try { + const query = JSON.parse(options.query); + const candidatesData = JSON.parse(fs.readFileSync(options.candidates, 'utf8')); + const candidates = candidatesData.map(c => c.vector || c); + const k = parseInt(options.topK); + const temperature = parseFloat(options.temperature); + + spinner.text = 'Running differentiable search...'; + const result = differentiableSearch(query, candidates, k, temperature); + + spinner.succeed(chalk.green(`Found top-${k} results`)); + + console.log(chalk.cyan('\nSearch Results:')); + console.log(chalk.white(` Query dim: ${chalk.yellow(query.length)}`)); + console.log(chalk.white(` Candidates: ${chalk.yellow(candidates.length)}`)); + console.log(chalk.white(` Temperature: ${chalk.yellow(temperature)}`)); + + console.log(chalk.cyan('\nTop-K Results:')); + for (let i = 0; i < result.indices.length; i++) { + const idx = result.indices[i]; + const weight = result.weights[i]; + const id = candidatesData[idx]?.id || `candidate_${idx}`; + console.log(chalk.white(` ${i + 1}. ${chalk.yellow(id)} (index: ${idx})`)); + console.log(chalk.gray(` Weight: ${weight.toFixed(6)}`)); + } + } catch (error) { + spinner.fail(chalk.red('Failed to run search')); + console.error(chalk.red(error.message)); + process.exit(1); + } + }); + +// GNN Info command +gnnCmd + .command('info') + .description('Show GNN module information') + .action(() => { + if (!gnnAvailable) { + console.log(chalk.yellow('\nGNN Module: Not installed')); + console.log(chalk.white('Install with: npm install @ruvector/gnn')); + return; + } + + console.log(chalk.cyan('\nGNN Module Information')); + console.log(chalk.white(` Status: ${chalk.green('Available')}`)); + console.log(chalk.white(` Platform: ${chalk.yellow(process.platform)}`)); + console.log(chalk.white(` Architecture: ${chalk.yellow(process.arch)}`)); + + console.log(chalk.cyan('\nAvailable Features:')); + console.log(chalk.white(` • RuvectorLayer - GNN layer with multi-head attention`)); + console.log(chalk.white(` • TensorCompress - Adaptive tensor compression (5 levels)`)); + console.log(chalk.white(` • differentiableSearch - Soft attention-based search`)); + console.log(chalk.white(` • hierarchicalForward - Multi-layer GNN processing`)); + + console.log(chalk.cyan('\nCompression Levels:')); + console.log(chalk.gray(` none (freq > 0.8) - Full precision, hot data`)); + console.log(chalk.gray(` half (freq > 0.4) - ~50% savings, warm data`)); + console.log(chalk.gray(` pq8 (freq > 0.1) - ~8x compression, cool data`)); + console.log(chalk.gray(` pq4 (freq > 0.01) - ~16x compression, cold data`)); + console.log(chalk.gray(` binary (freq <= 0.01) - ~32x compression, archive`)); + }); + +// ============================================================================= +// Attention Commands +// ============================================================================= + +// Helper to require attention module +function requireAttention() { + if (!attentionAvailable) { + console.error(chalk.red('Error: @ruvector/attention is not installed')); + console.error(chalk.yellow('Install it with: npm install @ruvector/attention')); + process.exit(1); + } +} + +// Attention parent command +const attentionCmd = program + .command('attention') + .description('High-performance attention mechanism operations'); + +// Attention compute command - run attention on input vectors +attentionCmd + .command('compute') + .description('Compute attention over input vectors') + .requiredOption('-q, --query ', 'Query vector as JSON array') + .requiredOption('-k, --keys ', 'Keys file (JSON array of vectors)') + .option('-v, --values ', 'Values file (JSON array of vectors, defaults to keys)') + .option('-t, --type ', 'Attention type (dot|multi-head|flash|hyperbolic|linear)', 'dot') + .option('-h, --heads ', 'Number of attention heads (for multi-head)', '4') + .option('-d, --head-dim ', 'Head dimension (for multi-head)', '64') + .option('--curvature ', 'Curvature for hyperbolic attention', '1.0') + .option('-o, --output ', 'Output file for results') + .action((options) => { + requireAttention(); + const spinner = ora('Loading keys...').start(); + + try { + const query = JSON.parse(options.query); + const keysData = JSON.parse(fs.readFileSync(options.keys, 'utf8')); + const keys = keysData.map(k => k.vector || k); + + let values = keys; + if (options.values) { + const valuesData = JSON.parse(fs.readFileSync(options.values, 'utf8')); + values = valuesData.map(v => v.vector || v); + } + + spinner.text = `Computing ${options.type} attention...`; + + let result; + let attentionWeights; + + switch (options.type) { + case 'dot': { + const attn = new DotProductAttention(); + const queryMat = [query]; + const output = attn.forward(queryMat, keys, values); + result = output[0]; + attentionWeights = attn.getLastWeights ? attn.getLastWeights()[0] : null; + break; + } + case 'multi-head': { + const numHeads = parseInt(options.heads); + const headDim = parseInt(options.headDim); + const attn = new MultiHeadAttention(query.length, numHeads, headDim); + const queryMat = [query]; + const output = attn.forward(queryMat, keys, values); + result = output[0]; + break; + } + case 'flash': { + const attn = new FlashAttention(query.length); + const queryMat = [query]; + const output = attn.forward(queryMat, keys, values); + result = output[0]; + break; + } + case 'hyperbolic': { + const curvature = parseFloat(options.curvature); + const attn = new HyperbolicAttention(query.length, curvature); + const queryMat = [query]; + const output = attn.forward(queryMat, keys, values); + result = output[0]; + break; + } + case 'linear': { + const attn = new LinearAttention(query.length); + const queryMat = [query]; + const output = attn.forward(queryMat, keys, values); + result = output[0]; + break; + } + default: + throw new Error(`Unknown attention type: ${options.type}`); + } + + spinner.succeed(chalk.green(`Attention computed (${options.type})`)); + + console.log(chalk.cyan('\nAttention Results:')); + console.log(chalk.white(` Type: ${chalk.yellow(options.type)}`)); + console.log(chalk.white(` Query dim: ${chalk.yellow(query.length)}`)); + console.log(chalk.white(` Num keys: ${chalk.yellow(keys.length)}`)); + console.log(chalk.white(` Output dim: ${chalk.yellow(result.length)}`)); + console.log(chalk.white(` Output: ${chalk.gray(`[${result.slice(0, 4).map(v => v.toFixed(4)).join(', ')}...]`)}`)); + + if (attentionWeights) { + console.log(chalk.cyan('\nAttention Weights:')); + attentionWeights.slice(0, 5).forEach((w, i) => { + console.log(chalk.gray(` Key ${i}: ${w.toFixed(4)}`)); + }); + if (attentionWeights.length > 5) { + console.log(chalk.gray(` ... and ${attentionWeights.length - 5} more`)); + } + } + + if (options.output) { + const outputData = { result, attentionWeights }; + fs.writeFileSync(options.output, JSON.stringify(outputData, null, 2)); + console.log(chalk.green(`\nResults saved to: ${options.output}`)); + } + } catch (error) { + spinner.fail(chalk.red('Failed to compute attention')); + console.error(chalk.red(error.message)); + process.exit(1); + } + }); + +// Attention benchmark command +attentionCmd + .command('benchmark') + .description('Benchmark attention mechanisms') + .option('-d, --dimension ', 'Vector dimension', '256') + .option('-n, --num-vectors ', 'Number of vectors', '100') + .option('-i, --iterations ', 'Benchmark iterations', '100') + .option('-t, --types ', 'Attention types to benchmark (comma-separated)', 'dot,flash,linear') + .action((options) => { + requireAttention(); + const spinner = ora('Setting up benchmark...').start(); + + try { + const dim = parseInt(options.dimension); + const numVectors = parseInt(options.numVectors); + const iterations = parseInt(options.iterations); + const types = options.types.split(',').map(t => t.trim()); + + // Generate random test data + spinner.text = 'Generating test data...'; + const query = Array.from({ length: dim }, () => Math.random()); + const keys = Array.from({ length: numVectors }, () => + Array.from({ length: dim }, () => Math.random()) + ); + + console.log(chalk.cyan('\n═══════════════════════════════════════════════════════════════')); + console.log(chalk.cyan(' Attention Mechanism Benchmark')); + console.log(chalk.cyan('═══════════════════════════════════════════════════════════════\n')); + + console.log(chalk.white(` Dimension: ${chalk.yellow(dim)}`)); + console.log(chalk.white(` Vectors: ${chalk.yellow(numVectors)}`)); + console.log(chalk.white(` Iterations: ${chalk.yellow(iterations)}`)); + console.log(''); + + const results = []; + + // Convert to Float32Arrays for compute() + const queryF32 = new Float32Array(query); + const keysF32 = keys.map(k => new Float32Array(k)); + + for (const type of types) { + spinner.text = `Benchmarking ${type} attention...`; + spinner.start(); + + let attn; + try { + switch (type) { + case 'dot': + attn = new DotProductAttention(dim); + break; + case 'flash': + attn = new FlashAttention(dim, 64); // dim, block_size + break; + case 'linear': + attn = new LinearAttention(dim, 64); // dim, num_features + break; + case 'hyperbolic': + attn = new HyperbolicAttention(dim, 1.0); + break; + case 'multi-head': + attn = new MultiHeadAttention(dim, 4); // dim, num_heads + break; + default: + console.log(chalk.yellow(` Skipping unknown type: ${type}`)); + continue; + } + } catch (e) { + console.log(chalk.yellow(` ${type}: not available (${e.message})`)); + continue; + } + + // Warm up + for (let i = 0; i < 5; i++) { + try { + attn.compute(queryF32, keysF32, keysF32); + } catch (e) { + // Some mechanisms may fail warmup + } + } + + // Benchmark + const start = process.hrtime.bigint(); + for (let i = 0; i < iterations; i++) { + attn.compute(queryF32, keysF32, keysF32); + } + const end = process.hrtime.bigint(); + const totalMs = Number(end - start) / 1_000_000; + const avgMs = totalMs / iterations; + const opsPerSec = 1000 / avgMs; + + results.push({ type, avgMs, opsPerSec }); + spinner.succeed(chalk.green(`${type}: ${avgMs.toFixed(3)} ms/op (${opsPerSec.toFixed(1)} ops/sec)`)); + } + + // Summary + if (results.length > 0) { + console.log(chalk.cyan('\n═══════════════════════════════════════════════════════════════')); + console.log(chalk.cyan(' Summary')); + console.log(chalk.cyan('═══════════════════════════════════════════════════════════════\n')); + + const fastest = results.reduce((a, b) => a.avgMs < b.avgMs ? a : b); + console.log(chalk.green(` Fastest: ${fastest.type} (${fastest.avgMs.toFixed(3)} ms/op)\n`)); + + console.log(chalk.white(' Relative Performance:')); + for (const r of results) { + const relPerf = (fastest.avgMs / r.avgMs * 100).toFixed(1); + const bar = '█'.repeat(Math.round(relPerf / 5)); + console.log(chalk.white(` ${r.type.padEnd(12)} ${chalk.cyan(bar)} ${relPerf}%`)); + } + } + } catch (error) { + spinner.fail(chalk.red('Benchmark failed')); + console.error(chalk.red(error.message)); + process.exit(1); + } + }); + +// Hyperbolic math command +attentionCmd + .command('hyperbolic') + .description('Hyperbolic geometry operations') + .requiredOption('-a, --action ', 'Action: exp-map|log-map|distance|project|mobius-add') + .requiredOption('-v, --vector ', 'Input vector(s) as JSON') + .option('-b, --vector-b ', 'Second vector for binary operations') + .option('-c, --curvature ', 'Poincaré ball curvature', '1.0') + .option('-o, --origin ', 'Origin point for exp/log maps') + .action((options) => { + requireAttention(); + + try { + const vecArray = JSON.parse(options.vector); + const vec = new Float32Array(vecArray); + const curvature = parseFloat(options.curvature); + + let result; + let description; + + switch (options.action) { + case 'exp-map': { + const originArray = options.origin ? JSON.parse(options.origin) : Array(vec.length).fill(0); + const origin = new Float32Array(originArray); + result = expMap(origin, vec, curvature); + description = 'Exponential map (tangent → Poincaré ball)'; + break; + } + case 'log-map': { + const originArray = options.origin ? JSON.parse(options.origin) : Array(vec.length).fill(0); + const origin = new Float32Array(originArray); + result = logMap(origin, vec, curvature); + description = 'Logarithmic map (Poincaré ball → tangent)'; + break; + } + case 'distance': { + if (!options.vectorB) { + throw new Error('--vector-b required for distance calculation'); + } + const vecBArray = JSON.parse(options.vectorB); + const vecB = new Float32Array(vecBArray); + result = poincareDistance(vec, vecB, curvature); + description = 'Poincaré distance'; + break; + } + case 'project': { + result = projectToPoincareBall(vec, curvature); + description = 'Project to Poincaré ball'; + break; + } + case 'mobius-add': { + if (!options.vectorB) { + throw new Error('--vector-b required for Möbius addition'); + } + const vecBArray = JSON.parse(options.vectorB); + const vecB = new Float32Array(vecBArray); + result = mobiusAddition(vec, vecB, curvature); + description = 'Möbius addition'; + break; + } + default: + throw new Error(`Unknown action: ${options.action}`); + } + + console.log(chalk.cyan('\nHyperbolic Operation:')); + console.log(chalk.white(` Action: ${chalk.yellow(description)}`)); + console.log(chalk.white(` Curvature: ${chalk.yellow(curvature)}`)); + + if (typeof result === 'number') { + console.log(chalk.white(` Result: ${chalk.green(result.toFixed(6))}`)); + } else { + const resultArray = Array.from(result); + console.log(chalk.white(` Input dim: ${chalk.yellow(vec.length)}`)); + console.log(chalk.white(` Output dim: ${chalk.yellow(resultArray.length)}`)); + console.log(chalk.white(` Result: ${chalk.gray(`[${resultArray.slice(0, 5).map(v => v.toFixed(4)).join(', ')}...]`)}`)); + + // Compute norm to verify it's in the ball + const norm = Math.sqrt(resultArray.reduce((sum, x) => sum + x * x, 0)); + console.log(chalk.white(` Norm: ${chalk.yellow(norm.toFixed(6))} ${norm < 1 ? chalk.green('(inside ball)') : chalk.red('(outside ball)')}`)); + } + } catch (error) { + console.error(chalk.red('Hyperbolic operation failed:'), error.message); + process.exit(1); + } + }); + +// Attention info command +attentionCmd + .command('info') + .description('Show attention module information') + .action(() => { + if (!attentionAvailable) { + console.log(chalk.yellow('\nAttention Module: Not installed')); + console.log(chalk.white('Install with: npm install @ruvector/attention')); + return; + } + + console.log(chalk.cyan('\nAttention Module Information')); + console.log(chalk.white(` Status: ${chalk.green('Available')}`)); + console.log(chalk.white(` Version: ${chalk.yellow(attentionVersion ? attentionVersion() : 'unknown')}`)); + console.log(chalk.white(` Platform: ${chalk.yellow(process.platform)}`)); + console.log(chalk.white(` Architecture: ${chalk.yellow(process.arch)}`)); + + console.log(chalk.cyan('\nCore Attention Mechanisms:')); + console.log(chalk.white(` • DotProductAttention - Scaled dot-product attention`)); + console.log(chalk.white(` • MultiHeadAttention - Multi-head self-attention`)); + console.log(chalk.white(` • FlashAttention - Memory-efficient IO-aware attention`)); + console.log(chalk.white(` • HyperbolicAttention - Poincaré ball attention`)); + console.log(chalk.white(` • LinearAttention - O(n) linear complexity attention`)); + console.log(chalk.white(` • MoEAttention - Mixture of Experts attention`)); + + console.log(chalk.cyan('\nGraph Attention:')); + console.log(chalk.white(` • GraphRoPeAttention - Rotary position embeddings for graphs`)); + console.log(chalk.white(` • EdgeFeaturedAttention - Edge feature-enhanced attention`)); + console.log(chalk.white(` • DualSpaceAttention - Euclidean + hyperbolic dual space`)); + console.log(chalk.white(` • LocalGlobalAttention - Local-global graph attention`)); + + console.log(chalk.cyan('\nHyperbolic Math:')); + console.log(chalk.white(` • expMap, logMap - Exponential/logarithmic maps`)); + console.log(chalk.white(` • mobiusAddition - Möbius addition in Poincaré ball`)); + console.log(chalk.white(` • poincareDistance - Hyperbolic distance metric`)); + console.log(chalk.white(` • projectToPoincareBall - Project vectors to ball`)); + + console.log(chalk.cyan('\nTraining Utilities:')); + console.log(chalk.white(` • AdamOptimizer, AdamWOptimizer, SgdOptimizer`)); + console.log(chalk.white(` • InfoNceLoss, LocalContrastiveLoss`)); + console.log(chalk.white(` • CurriculumScheduler, TemperatureAnnealing`)); + console.log(chalk.white(` • HardNegativeMiner, InBatchMiner`)); + }); + +// Attention list command - list available mechanisms +attentionCmd + .command('list') + .description('List all available attention mechanisms') + .option('-v, --verbose', 'Show detailed information') + .action((options) => { + console.log(chalk.cyan('\n═══════════════════════════════════════════════════════════════')); + console.log(chalk.cyan(' Available Attention Mechanisms')); + console.log(chalk.cyan('═══════════════════════════════════════════════════════════════\n')); + + const mechanisms = [ + { name: 'DotProductAttention', type: 'core', complexity: 'O(n²)', available: !!DotProductAttention }, + { name: 'MultiHeadAttention', type: 'core', complexity: 'O(n²)', available: !!MultiHeadAttention }, + { name: 'FlashAttention', type: 'core', complexity: 'O(n²) IO-optimized', available: !!FlashAttention }, + { name: 'HyperbolicAttention', type: 'core', complexity: 'O(n²)', available: !!HyperbolicAttention }, + { name: 'LinearAttention', type: 'core', complexity: 'O(n)', available: !!LinearAttention }, + { name: 'MoEAttention', type: 'core', complexity: 'O(n*k)', available: !!MoEAttention }, + { name: 'GraphRoPeAttention', type: 'graph', complexity: 'O(n²)', available: !!GraphRoPeAttention }, + { name: 'EdgeFeaturedAttention', type: 'graph', complexity: 'O(n²)', available: !!EdgeFeaturedAttention }, + { name: 'DualSpaceAttention', type: 'graph', complexity: 'O(n²)', available: !!DualSpaceAttention }, + { name: 'LocalGlobalAttention', type: 'graph', complexity: 'O(n*k)', available: !!LocalGlobalAttention }, + ]; + + console.log(chalk.white(' Core Attention:')); + mechanisms.filter(m => m.type === 'core').forEach(m => { + const status = m.available ? chalk.green('✓') : chalk.red('✗'); + console.log(chalk.white(` ${status} ${m.name.padEnd(22)} ${chalk.gray(m.complexity)}`)); + }); + + console.log(chalk.white('\n Graph Attention:')); + mechanisms.filter(m => m.type === 'graph').forEach(m => { + const status = m.available ? chalk.green('✓') : chalk.red('✗'); + console.log(chalk.white(` ${status} ${m.name.padEnd(22)} ${chalk.gray(m.complexity)}`)); + }); + + if (!attentionAvailable) { + console.log(chalk.yellow('\n Note: @ruvector/attention not installed')); + console.log(chalk.white(' Install with: npm install @ruvector/attention')); + } + + if (options.verbose) { + console.log(chalk.cyan('\n Usage Examples:')); + console.log(chalk.gray(' # Compute dot-product attention')); + console.log(chalk.white(' npx ruvector attention compute -q "[1,2,3]" -k keys.json -t dot')); + console.log(chalk.gray('\n # Benchmark attention mechanisms')); + console.log(chalk.white(' npx ruvector attention benchmark -d 256 -n 100')); + console.log(chalk.gray('\n # Hyperbolic distance')); + console.log(chalk.white(' npx ruvector attention hyperbolic -a distance -v "[0.1,0.2]" -b "[0.3,0.4]"')); + } + }); + +// ============================================================================= +// Doctor Command - Check system health and dependencies +// ============================================================================= + +program + .command('doctor') + .description('Check system health and dependencies') + .option('-v, --verbose', 'Show detailed information') + .action(async (options) => { + const { execSync } = require('child_process'); + + console.log(chalk.cyan('\n═══════════════════════════════════════════════════════════════')); + console.log(chalk.cyan(' RuVector Doctor')); + console.log(chalk.cyan('═══════════════════════════════════════════════════════��═══════\n')); + + let issues = 0; + let warnings = 0; + + // Helper functions + const check = (name, condition, fix) => { + if (condition) { + console.log(chalk.green(` ✓ ${name}`)); + return true; + } else { + console.log(chalk.red(` ✗ ${name}`)); + if (fix) console.log(chalk.gray(` Fix: ${fix}`)); + issues++; + return false; + } + }; + + const warn = (name, condition, suggestion) => { + if (condition) { + console.log(chalk.green(` ✓ ${name}`)); + return true; + } else { + console.log(chalk.yellow(` ! ${name}`)); + if (suggestion) console.log(chalk.gray(` Suggestion: ${suggestion}`)); + warnings++; + return false; + } + }; + + const getVersion = (cmd) => { + try { + return execSync(cmd, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim(); + } catch (e) { + return null; + } + }; + + // System Information + console.log(chalk.cyan('System Information:')); + console.log(chalk.white(` Platform: ${chalk.yellow(process.platform)}`)); + console.log(chalk.white(` Architecture: ${chalk.yellow(process.arch)}`)); + console.log(chalk.white(` Node.js: ${chalk.yellow(process.version)}`)); + console.log(''); + + // Node.js Checks + console.log(chalk.cyan('Node.js Environment:')); + const nodeVersion = parseInt(process.version.slice(1).split('.')[0]); + check('Node.js >= 14', nodeVersion >= 14, 'Upgrade Node.js: https://nodejs.org'); + + const npmVersion = getVersion('npm --version'); + if (npmVersion) { + console.log(chalk.green(` ✓ npm ${npmVersion}`)); + } else { + check('npm installed', false, 'Install npm or reinstall Node.js'); + } + console.log(''); + + // RuVector Packages + console.log(chalk.cyan('RuVector Packages:')); + + // Check @ruvector/core + let coreAvailable = false; + try { + require.resolve('@ruvector/core'); + coreAvailable = true; + console.log(chalk.green(` ✓ @ruvector/core installed`)); + } catch (e) { + console.log(chalk.yellow(` ! @ruvector/core not found (using WASM fallback)`)); + warnings++; + } + + // Check if native binding works + if (coreAvailable && loadRuvector()) { + const version = typeof getVersion === 'function' ? getVersion() : null; + const impl = typeof getImplementationType === 'function' ? getImplementationType() : 'native'; + const versionStr = version ? `, v${version}` : ''; + console.log(chalk.green(` ✓ Native binding working (${impl}${versionStr})`)); + } else if (coreAvailable) { + console.log(chalk.yellow(` ! Native binding failed to load`)); + warnings++; + } + + // Check @ruvector/gnn + if (gnnAvailable) { + console.log(chalk.green(` ✓ @ruvector/gnn installed`)); + } else { + console.log(chalk.gray(` ○ @ruvector/gnn not installed (optional)`)); + } + + // Check @ruvector/attention + if (attentionAvailable) { + console.log(chalk.green(` ✓ @ruvector/attention installed`)); + } else { + console.log(chalk.gray(` ○ @ruvector/attention not installed (optional)`)); + } + + // Check @ruvector/graph-node + try { + require.resolve('@ruvector/graph-node'); + console.log(chalk.green(` ✓ @ruvector/graph-node installed`)); + } catch (e) { + console.log(chalk.gray(` ○ @ruvector/graph-node not installed (optional)`)); + } + console.log(''); + + // Rust Toolchain (optional for development) + console.log(chalk.cyan('Rust Toolchain (optional):')); + + const rustVersion = getVersion('rustc --version'); + if (rustVersion) { + console.log(chalk.green(` ✓ ${rustVersion}`)); + } else { + console.log(chalk.gray(` ○ Rust not installed (only needed for development)`)); + } + + const cargoVersion = getVersion('cargo --version'); + if (cargoVersion) { + console.log(chalk.green(` ✓ ${cargoVersion}`)); + } else if (rustVersion) { + console.log(chalk.yellow(` ! cargo not found`)); + warnings++; + } + console.log(''); + + // Build Tools (optional) + if (options.verbose) { + console.log(chalk.cyan('Build Tools (for native compilation):')); + + const hasGcc = getVersion('gcc --version'); + const hasClang = getVersion('clang --version'); + const hasCc = getVersion('cc --version'); + + if (hasGcc || hasClang || hasCc) { + console.log(chalk.green(` ✓ C compiler available`)); + } else { + console.log(chalk.gray(` ○ No C compiler found (only needed for building from source)`)); + } + + const hasMake = getVersion('make --version'); + if (hasMake) { + console.log(chalk.green(` ✓ make available`)); + } else { + console.log(chalk.gray(` ○ make not found`)); + } + + const hasCmake = getVersion('cmake --version'); + if (hasCmake) { + console.log(chalk.green(` ✓ cmake available`)); + } else { + console.log(chalk.gray(` ○ cmake not found`)); + } + console.log(''); + } + + // Summary + console.log(chalk.cyan('═══════════════════════════════════════════════════════════════')); + if (issues === 0 && warnings === 0) { + console.log(chalk.green('\n ✓ All checks passed! RuVector is ready to use.\n')); + } else if (issues === 0) { + console.log(chalk.yellow(`\n ! ${warnings} warning(s) found. RuVector should work but may have limited features.\n`)); + } else { + console.log(chalk.red(`\n ✗ ${issues} issue(s) and ${warnings} warning(s) found.\n`)); + console.log(chalk.white(' Run "npx ruvector setup" for installation instructions.\n')); + } + }); + +// ============================================================================= +// Setup Command - Installation instructions +// ============================================================================= + +program + .command('setup') + .description('Show installation and setup instructions') + .option('--rust', 'Show Rust installation instructions') + .option('--npm', 'Show npm package installation instructions') + .option('--all', 'Show all installation instructions') + .action((options) => { + const showAll = options.all || (!options.rust && !options.npm); + + console.log(chalk.cyan('\n═══════════════════════════════════════════════════════════════')); + console.log(chalk.cyan(' RuVector Setup Guide')); + console.log(chalk.cyan('═══════════════════════════════════════════════════════════════\n')); + + // Quick install + console.log(chalk.cyan('Quick Install (one-liner):')); + console.log(chalk.white(' curl -fsSL https://raw.githubusercontent.com/ruvnet/ruvector/main/install.sh | bash')); + console.log(''); + + if (showAll || options.npm) { + console.log(chalk.cyan('───────────────────────────────────────────────────────────────')); + console.log(chalk.cyan('npm Packages')); + console.log(chalk.cyan('───────────────────────────────────────────────────────────────\n')); + + console.log(chalk.yellow('All-in-one CLI:')); + console.log(chalk.white(' npm install -g ruvector')); + console.log(chalk.white(' npx ruvector')); + console.log(''); + + console.log(chalk.yellow('Core packages:')); + console.log(chalk.white(' npm install @ruvector/core # Vector database')); + console.log(chalk.white(' npm install @ruvector/gnn # Graph Neural Networks')); + console.log(chalk.white(' npm install @ruvector/graph-node # Hypergraph database')); + console.log(''); + + console.log(chalk.yellow('Install all optional packages:')); + console.log(chalk.white(' npx ruvector install --all')); + console.log(''); + + console.log(chalk.yellow('List available packages:')); + console.log(chalk.white(' npx ruvector install')); + console.log(''); + } + + if (showAll || options.rust) { + console.log(chalk.cyan('───────────────────────────────────────────────────────────────')); + console.log(chalk.cyan('Rust Installation')); + console.log(chalk.cyan('───────────────────────────────────────────────────────────────\n')); + + console.log(chalk.yellow('1. Install Rust:')); + console.log(chalk.white(' curl --proto \'=https\' --tlsv1.2 -sSf https://sh.rustup.rs | sh')); + console.log(chalk.gray(' # Follow the prompts, then restart your terminal or run:')); + console.log(chalk.white(' source $HOME/.cargo/env')); + console.log(''); + + console.log(chalk.yellow('2. Verify installation:')); + console.log(chalk.white(' rustc --version')); + console.log(chalk.white(' cargo --version')); + console.log(''); + + console.log(chalk.yellow('3. Add RuVector crates to your project:')); + console.log(chalk.white(' cargo add ruvector-core # Vector database')); + console.log(chalk.white(' cargo add ruvector-graph # Hypergraph with Cypher')); + console.log(chalk.white(' cargo add ruvector-gnn # Graph Neural Networks')); + console.log(''); + + console.log(chalk.yellow('4. Other available crates:')); + console.log(chalk.white(' cargo add ruvector-cluster # Distributed clustering')); + console.log(chalk.white(' cargo add ruvector-raft # Raft consensus')); + console.log(chalk.white(' cargo add ruvector-replication # Data replication')); + console.log(chalk.white(' cargo add ruvector-tiny-dancer-core # AI routing')); + console.log(chalk.white(' cargo add ruvector-router-core # Semantic routing')); + console.log(''); + + console.log(chalk.yellow('Platform-specific notes:')); + console.log(''); + + if (process.platform === 'darwin') { + console.log(chalk.cyan(' macOS:')); + console.log(chalk.white(' xcode-select --install # Install command line tools')); + console.log(''); + } else if (process.platform === 'linux') { + console.log(chalk.cyan(' Linux (Debian/Ubuntu):')); + console.log(chalk.white(' sudo apt-get update')); + console.log(chalk.white(' sudo apt-get install build-essential pkg-config libssl-dev')); + console.log(''); + console.log(chalk.cyan(' Linux (RHEL/CentOS):')); + console.log(chalk.white(' sudo yum groupinstall "Development Tools"')); + console.log(chalk.white(' sudo yum install openssl-devel')); + console.log(''); + } else if (process.platform === 'win32') { + console.log(chalk.cyan(' Windows:')); + console.log(chalk.white(' # Install Visual Studio Build Tools')); + console.log(chalk.white(' # https://visualstudio.microsoft.com/visual-cpp-build-tools/')); + console.log(chalk.white(' # Or use WSL2 for best experience')); + console.log(''); + } + } + + console.log(chalk.cyan('───────────────────────────────────────────────────────────────')); + console.log(chalk.cyan('Documentation & Resources')); + console.log(chalk.cyan('───────────────────────────────────────────────────────────────\n')); + + console.log(chalk.white(' GitHub: https://github.com/ruvnet/ruvector')); + console.log(chalk.white(' npm: https://www.npmjs.com/package/ruvector')); + console.log(chalk.white(' crates.io: https://crates.io/crates/ruvector-core')); + console.log(chalk.white(' Issues: https://github.com/ruvnet/ruvector/issues')); + console.log(''); + + console.log(chalk.cyan('Quick Commands:')); + console.log(chalk.white(' npx ruvector doctor # Check system health')); + console.log(chalk.white(' npx ruvector info # Show version info')); + console.log(chalk.white(' npx ruvector benchmark # Run performance test')); + console.log(chalk.white(' npx ruvector install # List available packages')); + console.log(''); + }); + +// ============================================================================= +// Graph Commands - Cypher queries and graph operations +// ============================================================================= + +program + .command('graph') + .description('Graph database operations (requires @ruvector/graph-node)') + .option('-q, --query ', 'Execute Cypher query') + .option('-c, --create