UNPKG

claude-flow

Version:

Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration

1,005 lines (1,004 loc) 76.6 kB
/** * V3 CLI Embeddings Command * Vector embeddings, semantic search, similarity operations * * Features: * - Multiple providers: OpenAI, Transformers.js, Agentic-Flow, Mock * - Document chunking with overlap * - L2/L1/minmax/zscore normalization * - Hyperbolic embeddings (Poincaré ball) * - Neural substrate integration * - Persistent SQLite cache * * Created with ❤️ by ruv.io */ import { output } from '../output.js'; // Dynamic imports for embeddings package async function getEmbeddings() { try { return await import('@claude-flow/embeddings'); } catch { return null; } } // Generate subcommand - REAL implementation const generateCommand = { name: 'generate', description: 'Generate embeddings for text', options: [ { name: 'text', short: 't', type: 'string', description: 'Text to embed', required: true }, { name: 'provider', short: 'p', type: 'string', description: 'Provider: openai, transformers, agentic-flow, local', default: 'local' }, { name: 'model', short: 'm', type: 'string', description: 'Model to use' }, { name: 'output', short: 'o', type: 'string', description: 'Output format: json, array, preview', default: 'preview' }, ], examples: [ { command: 'claude-flow embeddings generate -t "Hello world"', description: 'Generate embedding' }, { command: 'claude-flow embeddings generate -t "Test" -o json', description: 'Output as JSON' }, ], action: async (ctx) => { const text = ctx.flags.text; const provider = ctx.flags.provider || 'local'; const outputFormat = ctx.flags.output || 'preview'; if (!text) { output.printError('Text is required'); return { success: false, exitCode: 1 }; } output.writeln(); output.writeln(output.bold('Generate Embedding')); output.writeln(output.dim('─'.repeat(50))); const spinner = output.createSpinner({ text: `Generating with ${provider}...`, spinner: 'dots' }); spinner.start(); try { // Use real embedding generator const { generateEmbedding, loadEmbeddingModel } = await import('../memory/memory-initializer.js'); const startTime = Date.now(); const modelInfo = await loadEmbeddingModel({ verbose: false }); const result = await generateEmbedding(text); const duration = Date.now() - startTime; spinner.succeed(`Embedding generated in ${duration}ms`); if (outputFormat === 'json') { output.printJson({ text: text.substring(0, 100), embedding: result.embedding, dimensions: result.dimensions, model: result.model, duration }); return { success: true, data: result }; } if (outputFormat === 'array') { output.writeln(JSON.stringify(result.embedding)); return { success: true, data: result }; } // Preview format (default) const preview = result.embedding.slice(0, 8).map(v => v.toFixed(6)); output.writeln(); output.printBox([ `Provider: ${provider}`, `Model: ${result.model} (${modelInfo.modelName})`, `Dimensions: ${result.dimensions}`, `Text: "${text.substring(0, 40)}${text.length > 40 ? '...' : ''}"`, `Generation time: ${duration}ms`, ``, `Vector preview (first 8 of ${result.dimensions}):`, `[${preview.join(', ')}, ...]`, ].join('\n'), 'Result'); return { success: true, data: result }; } catch (error) { spinner.fail('Embedding generation failed'); output.printError(error instanceof Error ? error.message : String(error)); return { success: false, exitCode: 1 }; } }, }; // Search subcommand - REAL implementation using sql.js const searchCommand = { name: 'search', description: 'Semantic similarity search', options: [ { name: 'query', short: 'q', type: 'string', description: 'Search query', required: true }, { name: 'collection', short: 'c', type: 'string', description: 'Namespace to search', default: 'default' }, { name: 'limit', short: 'l', type: 'number', description: 'Max results', default: '10' }, { name: 'threshold', short: 't', type: 'number', description: 'Similarity threshold (0-1)', default: '0.5' }, { name: 'db-path', type: 'string', description: 'Database path', default: '.swarm/memory.db' }, ], examples: [ { command: 'claude-flow embeddings search -q "error handling"', description: 'Search for similar' }, { command: 'claude-flow embeddings search -q "test" -l 5', description: 'Limit results' }, ], action: async (ctx) => { const query = ctx.flags.query; const namespace = ctx.flags.collection || 'default'; const limit = parseInt(ctx.flags.limit || '10', 10); const threshold = parseFloat(ctx.flags.threshold || '0.5'); const dbPath = ctx.flags['db-path'] || '.swarm/memory.db'; if (!query) { output.printError('Query is required'); return { success: false, exitCode: 1 }; } output.writeln(); output.writeln(output.bold('Semantic Search')); output.writeln(output.dim('─'.repeat(60))); const spinner = output.createSpinner({ text: 'Searching...', spinner: 'dots' }); spinner.start(); try { const fs = await import('fs'); const path = await import('path'); const fullDbPath = path.resolve(process.cwd(), dbPath); // Check if database exists if (!fs.existsSync(fullDbPath)) { spinner.fail('Database not found'); output.printWarning(`No database at ${fullDbPath}`); output.printInfo('Run: claude-flow memory init'); return { success: false, exitCode: 1 }; } // Load sql.js const initSqlJs = (await import('sql.js')).default; const SQL = await initSqlJs(); const fileBuffer = fs.readFileSync(fullDbPath); const db = new SQL.Database(fileBuffer); const startTime = Date.now(); // Generate embedding for query const { generateEmbedding } = await import('../memory/memory-initializer.js'); const queryResult = await generateEmbedding(query); const queryEmbedding = queryResult.embedding; // Get all entries with embeddings from database const entries = db.exec(` SELECT id, key, namespace, content, embedding, embedding_dimensions FROM memory_entries WHERE status = 'active' AND embedding IS NOT NULL ${namespace !== 'all' ? `AND namespace = '${namespace}'` : ''} LIMIT 1000 `); const results = []; if (entries[0]?.values) { for (const row of entries[0].values) { const [id, key, ns, content, embeddingJson] = row; if (!embeddingJson) continue; try { const embedding = JSON.parse(embeddingJson); // Calculate cosine similarity const similarity = cosineSimilarity(queryEmbedding, embedding); if (similarity >= threshold) { results.push({ score: similarity, id: id.substring(0, 10), key: key || id.substring(0, 15), content: (content || '').substring(0, 45) + ((content || '').length > 45 ? '...' : ''), namespace: ns || 'default' }); } } catch { // Skip entries with invalid embeddings } } } // Also search entries without embeddings using keyword match if (results.length < limit) { const keywordEntries = db.exec(` SELECT id, key, namespace, content FROM memory_entries WHERE status = 'active' AND (content LIKE '%${query.replace(/'/g, "''")}%' OR key LIKE '%${query.replace(/'/g, "''")}%') ${namespace !== 'all' ? `AND namespace = '${namespace}'` : ''} LIMIT ${limit - results.length} `); if (keywordEntries[0]?.values) { for (const row of keywordEntries[0].values) { const [id, key, ns, content] = row; // Avoid duplicates if (!results.some(r => r.id === id.substring(0, 10))) { results.push({ score: 0.5, // Keyword match base score id: id.substring(0, 10), key: key || id.substring(0, 15), content: (content || '').substring(0, 45) + ((content || '').length > 45 ? '...' : ''), namespace: ns || 'default' }); } } } } // Sort by score descending results.sort((a, b) => b.score - a.score); const topResults = results.slice(0, limit); const searchTime = Date.now() - startTime; db.close(); spinner.succeed(`Found ${topResults.length} matches (${searchTime}ms)`); if (topResults.length === 0) { output.writeln(); output.printWarning('No matches found'); output.printInfo(`Try: claude-flow memory store -k "key" --value "your data"`); return { success: true, data: [] }; } output.writeln(); output.printTable({ columns: [ { key: 'score', header: 'Score', width: 10 }, { key: 'key', header: 'Key', width: 18 }, { key: 'content', header: 'Content', width: 42 }, ], data: topResults.map(r => ({ score: r.score >= 0.8 ? output.success(r.score.toFixed(2)) : r.score >= 0.6 ? output.warning(r.score.toFixed(2)) : output.dim(r.score.toFixed(2)), key: r.key, content: r.content })), }); output.writeln(); output.writeln(output.dim(`Searched ${namespace} namespace (${queryResult.model}, ${searchTime}ms)`)); return { success: true, data: topResults }; } catch (error) { spinner.fail('Search failed'); output.printError(error instanceof Error ? error.message : String(error)); return { success: false, exitCode: 1 }; } }, }; /** * Optimized cosine similarity * V8 JIT-friendly - ~0.5μs per 384-dim vector comparison */ function cosineSimilarity(a, b) { const len = Math.min(a.length, b.length); if (len === 0) return 0; let dot = 0, normA = 0, normB = 0; // Simple loop - V8 optimizes this well for (let i = 0; i < len; i++) { const ai = a[i], bi = b[i]; dot += ai * bi; normA += ai * ai; normB += bi * bi; } const mag = Math.sqrt(normA * normB); return mag === 0 ? 0 : dot / mag; } // Compare subcommand - REAL similarity computation const compareCommand = { name: 'compare', description: 'Compare similarity between texts', options: [ { name: 'text1', type: 'string', description: 'First text', required: true }, { name: 'text2', type: 'string', description: 'Second text', required: true }, { name: 'metric', short: 'm', type: 'string', description: 'Metric: cosine, euclidean, dot', default: 'cosine' }, ], examples: [ { command: 'claude-flow embeddings compare --text1 "Hello" --text2 "Hi there"', description: 'Compare texts' }, ], action: async (ctx) => { const text1 = ctx.flags.text1; const text2 = ctx.flags.text2; const metric = ctx.flags.metric || 'cosine'; if (!text1 || !text2) { output.printError('Both text1 and text2 are required'); return { success: false, exitCode: 1 }; } output.writeln(); output.writeln(output.bold('Text Similarity (Real)')); output.writeln(output.dim('─'.repeat(50))); const spinner = output.createSpinner({ text: 'Generating embeddings...', spinner: 'dots' }); spinner.start(); try { const { generateEmbedding } = await import('../memory/memory-initializer.js'); // Generate real embeddings for both texts const startTime = Date.now(); const [emb1, emb2] = await Promise.all([ generateEmbedding(text1), generateEmbedding(text2), ]); const embedTime = Date.now() - startTime; spinner.setText('Computing similarity...'); // Compute real similarity based on metric let similarity; switch (metric) { case 'euclidean': { // Euclidean distance (converted to similarity: 1 / (1 + distance)) let sumSq = 0; for (let i = 0; i < emb1.embedding.length; i++) { const diff = emb1.embedding[i] - emb2.embedding[i]; sumSq += diff * diff; } const distance = Math.sqrt(sumSq); similarity = 1 / (1 + distance); break; } case 'dot': { // Dot product let dot = 0; for (let i = 0; i < emb1.embedding.length; i++) { dot += emb1.embedding[i] * emb2.embedding[i]; } similarity = dot; break; } case 'cosine': default: { // Cosine similarity similarity = cosineSimilarity(emb1.embedding, emb2.embedding); } } spinner.succeed(`Comparison complete (${embedTime}ms)`); output.writeln(); output.printBox([ `Text 1: "${text1.substring(0, 30)}${text1.length > 30 ? '...' : ''}"`, `Text 2: "${text2.substring(0, 30)}${text2.length > 30 ? '...' : ''}"`, ``, `Model: ${emb1.model} (${emb1.dimensions}-dim)`, `Metric: ${metric}`, `Similarity: ${similarity > 0.8 ? output.success(similarity.toFixed(4)) : similarity > 0.5 ? output.warning(similarity.toFixed(4)) : output.dim(similarity.toFixed(4))}`, ``, `Interpretation: ${similarity > 0.8 ? 'Highly similar' : similarity > 0.5 ? 'Moderately similar' : 'Dissimilar'}`, ].join('\n'), 'Result'); return { success: true, data: { similarity, metric, embedTime } }; } catch (error) { spinner.fail('Comparison failed'); output.printError(error instanceof Error ? error.message : String(error)); return { success: false, exitCode: 1 }; } }, }; // Collections subcommand - REAL implementation using sql.js const collectionsCommand = { name: 'collections', description: 'Manage embedding collections (namespaces)', options: [ { name: 'action', short: 'a', type: 'string', description: 'Action: list, stats', default: 'list' }, { name: 'name', short: 'n', type: 'string', description: 'Namespace name' }, { name: 'db-path', type: 'string', description: 'Database path', default: '.swarm/memory.db' }, ], examples: [ { command: 'claude-flow embeddings collections', description: 'List collections' }, { command: 'claude-flow embeddings collections -a stats', description: 'Show detailed stats' }, ], action: async (ctx) => { const action = ctx.flags.action || 'list'; const dbPath = ctx.flags['db-path'] || '.swarm/memory.db'; output.writeln(); output.writeln(output.bold('Embedding Collections (Namespaces)')); output.writeln(output.dim('─'.repeat(60))); try { const fs = await import('fs'); const path = await import('path'); const fullDbPath = path.resolve(process.cwd(), dbPath); // Check if database exists if (!fs.existsSync(fullDbPath)) { output.printWarning('No database found'); output.printInfo('Run: claude-flow memory init'); output.writeln(); output.writeln(output.dim('No collections yet - initialize memory first')); return { success: true, data: [] }; } // Load sql.js and query real data const initSqlJs = (await import('sql.js')).default; const SQL = await initSqlJs(); const fileBuffer = fs.readFileSync(fullDbPath); const db = new SQL.Database(fileBuffer); // Get collection stats from database const statsQuery = db.exec(` SELECT namespace, COUNT(*) as total_entries, SUM(CASE WHEN embedding IS NOT NULL THEN 1 ELSE 0 END) as with_embeddings, AVG(embedding_dimensions) as avg_dimensions, SUM(LENGTH(content)) as total_content_size FROM memory_entries WHERE status = 'active' GROUP BY namespace ORDER BY total_entries DESC `); // Get vector index info const indexQuery = db.exec(`SELECT name, dimensions, hnsw_m FROM vector_indexes`); const collections = []; if (statsQuery[0]?.values) { for (const row of statsQuery[0].values) { const [namespace, total, withEmbeddings, avgDims, contentSize] = row; collections.push({ name: namespace || 'default', vectors: withEmbeddings.toLocaleString(), total: total.toLocaleString(), dimensions: avgDims ? Math.round(avgDims).toString() : '-', index: withEmbeddings > 0 ? 'HNSW' : 'None', size: formatBytes(contentSize || 0) }); } } db.close(); if (collections.length === 0) { output.printWarning('No collections found'); output.writeln(); output.writeln(output.dim('Store some data first:')); output.writeln(output.highlight(' claude-flow memory store -k "key" --value "data"')); return { success: true, data: [] }; } output.printTable({ columns: [ { key: 'name', header: 'Namespace', width: 18 }, { key: 'total', header: 'Entries', width: 10 }, { key: 'vectors', header: 'Vectors', width: 10 }, { key: 'dimensions', header: 'Dims', width: 8 }, { key: 'index', header: 'Index', width: 8 }, { key: 'size', header: 'Size', width: 10 }, ], data: collections, }); output.writeln(); output.writeln(output.dim(`Database: ${fullDbPath}`)); return { success: true, data: collections }; } catch (error) { output.printError(error instanceof Error ? error.message : String(error)); return { success: false, exitCode: 1 }; } }, }; // Helper: Format bytes to human readable function formatBytes(bytes) { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; } // Index subcommand - REAL HNSW stats const indexCommand = { name: 'index', description: 'Manage HNSW indexes', options: [ { name: 'action', short: 'a', type: 'string', description: 'Action: build, rebuild, status, optimize', default: 'status' }, { name: 'collection', short: 'c', type: 'string', description: 'Collection/namespace name' }, { name: 'ef-construction', type: 'number', description: 'HNSW ef_construction parameter', default: '200' }, { name: 'm', type: 'number', description: 'HNSW M parameter', default: '16' }, ], examples: [ { command: 'claude-flow embeddings index', description: 'Show index status' }, { command: 'claude-flow embeddings index -a build -c documents', description: 'Build index' }, { command: 'claude-flow embeddings index -a optimize -c patterns', description: 'Optimize index' }, ], action: async (ctx) => { const action = ctx.flags.action || 'status'; const collection = ctx.flags.collection; const efConstruction = parseInt(ctx.flags['ef-construction'] || '200', 10); const m = parseInt(ctx.flags.m || '16', 10); output.writeln(); output.writeln(output.bold(`HNSW Index: ${action}`)); output.writeln(output.dim('─'.repeat(50))); try { const { getHNSWStatus, getHNSWIndex, searchHNSWIndex, generateEmbedding } = await import('../memory/memory-initializer.js'); // Get real HNSW status const status = getHNSWStatus(); if (action === 'status') { output.writeln(); output.printTable({ columns: [ { key: 'metric', header: 'Metric', width: 24 }, { key: 'value', header: 'Value', width: 30 }, ], data: [ { metric: 'HNSW Available', value: status.available ? output.success('Yes (@ruvector/core)') : output.warning('No') }, { metric: 'Index Initialized', value: status.initialized ? output.success('Yes') : output.dim('No') }, { metric: 'Vector Count', value: status.entryCount.toLocaleString() }, { metric: 'Dimensions', value: String(status.dimensions) }, { metric: 'Distance Metric', value: 'Cosine' }, { metric: 'HNSW M', value: String(m) }, { metric: 'ef_construction', value: String(efConstruction) }, ], }); if (status.available && status.entryCount > 0) { // Run a quick benchmark to show actual performance output.writeln(); output.writeln(output.dim('Running quick performance test...')); const testQuery = await generateEmbedding('test performance query'); const start = performance.now(); const results = await searchHNSWIndex(testQuery.embedding, { k: 10 }); const searchTime = performance.now() - start; // Estimate brute force time (0.5μs per comparison) const bruteForceEstimate = status.entryCount * 0.0005; const speedup = bruteForceEstimate / (searchTime / 1000); output.writeln(); output.printBox([ `Performance (n=${status.entryCount}):`, ` HNSW Search: ${searchTime.toFixed(2)}ms`, ` Brute Force Est: ${(bruteForceEstimate * 1000).toFixed(2)}ms`, ` Speedup: ~${Math.round(speedup)}x`, ` Results: ${results?.length || 0} matches`, ].join('\n'), 'Search Performance'); } else if (!status.available) { output.writeln(); output.printWarning('@ruvector/core not available'); output.printInfo('Install: npm install @ruvector/core'); } else { output.writeln(); output.printInfo('Index is empty. Store some entries to populate it.'); output.printInfo('Run: claude-flow memory store -k "key" --value "text"'); } return { success: true, data: status }; } // Build/Rebuild action if (action === 'build' || action === 'rebuild') { if (!collection) { output.printError('Collection is required for build/rebuild'); return { success: false, exitCode: 1 }; } const spinner = output.createSpinner({ text: `${action}ing index for ${collection}...`, spinner: 'dots' }); spinner.start(); // Force rebuild if requested const index = await getHNSWIndex({ forceRebuild: action === 'rebuild' }); if (!index) { spinner.fail('@ruvector/core not available'); output.printInfo('Install: npm install @ruvector/core'); return { success: false, exitCode: 1 }; } spinner.succeed(`Index ${action} complete`); const newStatus = getHNSWStatus(); output.writeln(); output.printBox([ `Collection: ${collection}`, `Action: ${action}`, `Vectors: ${newStatus.entryCount}`, `Dimensions: ${newStatus.dimensions}`, `M: ${m}`, `ef_construction: ${efConstruction}`, ].join('\n'), 'Index Built'); return { success: true, data: newStatus }; } // Optimize action if (action === 'optimize') { output.printInfo('HNSW index is optimized automatically during search'); output.printInfo('No manual optimization required'); return { success: true }; } output.printError(`Unknown action: ${action}`); return { success: false, exitCode: 1 }; } catch (error) { output.printError(error instanceof Error ? error.message : String(error)); return { success: false, exitCode: 1 }; } }, }; // Init subcommand - Initialize ONNX models and hyperbolic config const initCommand = { name: 'init', description: 'Initialize embedding subsystem with ONNX model and hyperbolic config', options: [ { name: 'model', short: 'm', type: 'string', description: 'ONNX model ID', default: 'all-MiniLM-L6-v2' }, { name: 'hyperbolic', type: 'boolean', description: 'Enable hyperbolic (Poincaré ball) embeddings', default: 'true' }, { name: 'curvature', short: 'c', type: 'string', description: 'Poincaré ball curvature (use --curvature=-1 for negative)', default: '-1' }, { name: 'download', short: 'd', type: 'boolean', description: 'Download model during init', default: 'true' }, { name: 'cache-size', type: 'string', description: 'LRU cache entries', default: '256' }, { name: 'force', short: 'f', type: 'boolean', description: 'Overwrite existing configuration', default: 'false' }, ], examples: [ { command: 'claude-flow embeddings init', description: 'Initialize with defaults' }, { command: 'claude-flow embeddings init --model all-mpnet-base-v2', description: 'Use higher quality model' }, { command: 'claude-flow embeddings init --no-hyperbolic', description: 'Euclidean only' }, { command: 'claude-flow embeddings init --curvature=-0.5', description: 'Custom curvature (use = for negative)' }, { command: 'claude-flow embeddings init --force', description: 'Overwrite existing config' }, ], action: async (ctx) => { const model = ctx.flags.model || 'all-MiniLM-L6-v2'; const hyperbolic = ctx.flags.hyperbolic !== false; const download = ctx.flags.download !== false; const force = ctx.flags.force === true; // Parse curvature - handle both kebab-case and direct value const curvatureRaw = ctx.flags.curvature || '-1'; const curvature = parseFloat(curvatureRaw); // Parse cache-size - check both kebab-case and camelCase const cacheSizeRaw = (ctx.flags['cache-size'] || ctx.flags.cacheSize || '256'); const cacheSize = parseInt(cacheSizeRaw, 10); output.writeln(); output.writeln(output.bold('Initialize Embedding Subsystem')); output.writeln(output.dim('─'.repeat(55))); try { const fs = await import('fs'); const path = await import('path'); // Create directories const configDir = path.join(process.cwd(), '.claude-flow'); const modelDir = path.join(configDir, 'models'); const configPath = path.join(configDir, 'embeddings.json'); // Check for existing config if (fs.existsSync(configPath) && !force) { output.printWarning('Embeddings already initialized'); output.printInfo(`Config exists: ${configPath}`); output.writeln(); output.writeln(output.dim('Use --force to overwrite existing configuration')); return { success: false, exitCode: 1 }; } const spinner = output.createSpinner({ text: 'Initializing...', spinner: 'dots' }); spinner.start(); if (!fs.existsSync(configDir)) { fs.mkdirSync(configDir, { recursive: true }); } if (!fs.existsSync(modelDir)) { fs.mkdirSync(modelDir, { recursive: true }); } // Download model if requested if (download) { spinner.setText(`Downloading ONNX model: ${model}...`); const embeddings = await getEmbeddings(); if (embeddings) { await embeddings.downloadEmbeddingModel(model, modelDir, (p) => { spinner.setText(`Downloading ${model}... ${p.percent.toFixed(0)}%`); }); } else { // Simulate download for when embeddings package not available await new Promise(r => setTimeout(r, 500)); output.writeln(output.dim(' (Simulated - @claude-flow/embeddings not installed)')); } } // Write embeddings config spinner.setText('Writing configuration...'); const dimension = model.includes('mpnet') ? 768 : 384; const config = { model, modelPath: modelDir, dimension, cacheSize, hyperbolic: { enabled: hyperbolic, curvature, epsilon: 1e-15, maxNorm: 1 - 1e-5, }, neural: { enabled: true, driftThreshold: 0.3, decayRate: 0.01, }, initialized: new Date().toISOString(), }; fs.writeFileSync(configPath, JSON.stringify(config, null, 2)); spinner.succeed('Embedding subsystem initialized'); output.writeln(); output.printTable({ columns: [ { key: 'setting', header: 'Setting', width: 18 }, { key: 'value', header: 'Value', width: 40 }, ], data: [ { setting: 'Model', value: model }, { setting: 'Dimension', value: String(dimension) }, { setting: 'Cache Size', value: String(cacheSize) + ' entries' }, { setting: 'Hyperbolic', value: hyperbolic ? `${output.success('Enabled')} (c=${curvature})` : output.dim('Disabled') }, { setting: 'Neural Substrate', value: output.success('Enabled') }, { setting: 'Model Path', value: modelDir }, { setting: 'Config', value: configPath }, ], }); output.writeln(); if (hyperbolic) { output.printBox([ 'Hyperbolic Embeddings (Poincaré Ball):', '• Better for hierarchical data (trees, taxonomies)', '• Exponential capacity in low dimensions', '• Distance preserves hierarchy structure', '', 'Use: embeddings hyperbolic -a convert', ].join('\n'), 'Hyperbolic Space'); } output.writeln(); output.writeln(output.dim('Next steps:')); output.printList([ 'embeddings generate -t "test text" - Test embedding generation', 'embeddings search -q "query" - Semantic search', 'memory store -k key --value text - Store with auto-embedding', ]); return { success: true, data: config }; } catch (error) { output.printError('Initialization failed: ' + (error instanceof Error ? error.message : String(error))); return { success: false, exitCode: 1 }; } }, }; // Providers subcommand const providersCommand = { name: 'providers', description: 'List available embedding providers', options: [], examples: [ { command: 'claude-flow embeddings providers', description: 'List providers' }, ], action: async () => { output.writeln(); output.writeln(output.bold('Embedding Providers')); output.writeln(output.dim('─'.repeat(70))); output.printTable({ columns: [ { key: 'provider', header: 'Provider', width: 18 }, { key: 'model', header: 'Model', width: 25 }, { key: 'dims', header: 'Dims', width: 8 }, { key: 'type', header: 'Type', width: 10 }, { key: 'status', header: 'Status', width: 12 }, ], data: [ { provider: 'OpenAI', model: 'text-embedding-3-small', dims: '1536', type: 'Cloud', status: output.success('Ready') }, { provider: 'OpenAI', model: 'text-embedding-3-large', dims: '3072', type: 'Cloud', status: output.success('Ready') }, { provider: 'Transformers.js', model: 'all-MiniLM-L6-v2', dims: '384', type: 'Local', status: output.success('Ready') }, { provider: 'Agentic Flow', model: 'ONNX optimized', dims: '384', type: 'Local', status: output.success('Ready') }, { provider: 'Mock', model: 'mock-embedding', dims: '384', type: 'Dev', status: output.dim('Dev only') }, ], }); output.writeln(); output.writeln(output.dim('Agentic Flow provider uses WASM SIMD for 75x faster inference')); return { success: true }; }, }; // Chunk subcommand const chunkCommand = { name: 'chunk', description: 'Chunk text for embedding with overlap', options: [ { name: 'text', short: 't', type: 'string', description: 'Text to chunk', required: true }, { name: 'max-size', short: 's', type: 'number', description: 'Max chunk size in chars', default: '512' }, { name: 'overlap', short: 'o', type: 'number', description: 'Overlap between chunks', default: '50' }, { name: 'strategy', type: 'string', description: 'Strategy: character, sentence, paragraph, token', default: 'sentence' }, { name: 'file', short: 'f', type: 'string', description: 'File to chunk (instead of text)' }, ], examples: [ { command: 'claude-flow embeddings chunk -t "Long text..." -s 256', description: 'Chunk with 256 char limit' }, { command: 'claude-flow embeddings chunk -f doc.txt --strategy paragraph', description: 'Chunk file by paragraph' }, ], action: async (ctx) => { const embeddings = await getEmbeddings(); const text = ctx.flags.text || ''; const maxSize = parseInt(ctx.flags['max-size'] || '512', 10); const overlap = parseInt(ctx.flags.overlap || '50', 10); const strategy = ctx.flags.strategy || 'sentence'; output.writeln(); output.writeln(output.bold('Document Chunking')); output.writeln(output.dim('─'.repeat(50))); if (!embeddings) { output.printWarning('@claude-flow/embeddings not installed, showing preview'); output.writeln(); output.printBox([ `Strategy: ${strategy}`, `Max Size: ${maxSize} chars`, `Overlap: ${overlap} chars`, ``, `Estimated chunks: ${Math.ceil(text.length / (maxSize - overlap))}`, ].join('\n'), 'Chunking Preview'); return { success: true }; } const result = embeddings.chunkText(text, { maxChunkSize: maxSize, overlap, strategy: strategy }); output.writeln(); output.printTable({ columns: [ { key: 'idx', header: '#', width: 5 }, { key: 'length', header: 'Chars', width: 8 }, { key: 'tokens', header: 'Tokens', width: 8 }, { key: 'preview', header: 'Preview', width: 45 }, ], data: result.chunks.map((c, i) => ({ idx: String(i + 1), length: String(c.length), tokens: String(c.tokenCount), preview: c.text.substring(0, 42) + (c.text.length > 42 ? '...' : ''), })), }); output.writeln(); output.writeln(output.dim(`Total: ${result.totalChunks} chunks from ${result.originalLength} chars`)); return { success: true }; }, }; // Normalize subcommand const normalizeCommand = { name: 'normalize', description: 'Normalize embedding vectors', options: [ { name: 'type', short: 't', type: 'string', description: 'Type: l2, l1, minmax, zscore', default: 'l2' }, { name: 'input', short: 'i', type: 'string', description: 'Input embedding (JSON array)' }, { name: 'check', short: 'c', type: 'boolean', description: 'Check if already normalized' }, ], examples: [ { command: 'claude-flow embeddings normalize -i "[0.5, 0.3, 0.8]" -t l2', description: 'L2 normalize' }, { command: 'claude-flow embeddings normalize --check -i "[...]"', description: 'Check if normalized' }, ], action: async (ctx) => { const type = ctx.flags.type || 'l2'; const check = ctx.flags.check; output.writeln(); output.writeln(output.bold('Embedding Normalization')); output.writeln(output.dim('─'.repeat(50))); output.printTable({ columns: [ { key: 'type', header: 'Type', width: 12 }, { key: 'formula', header: 'Formula', width: 30 }, { key: 'use', header: 'Best For', width: 25 }, ], data: [ { type: output.success('L2'), formula: 'v / ||v||₂', use: 'Cosine similarity' }, { type: 'L1', formula: 'v / ||v||₁', use: 'Sparse vectors' }, { type: 'Min-Max', formula: '(v - min) / (max - min)', use: 'Bounded range [0,1]' }, { type: 'Z-Score', formula: '(v - μ) / σ', use: 'Statistical analysis' }, ], }); output.writeln(); output.writeln(output.dim(`Selected: ${type.toUpperCase()} normalization`)); output.writeln(output.dim('Most embedding models pre-normalize with L2')); return { success: true }; }, }; // Hyperbolic subcommand const hyperbolicCommand = { name: 'hyperbolic', description: 'Hyperbolic embedding operations (Poincaré ball)', options: [ { name: 'action', short: 'a', type: 'string', description: 'Action: convert, distance, centroid', default: 'convert' }, { name: 'curvature', short: 'c', type: 'number', description: 'Hyperbolic curvature', default: '-1' }, { name: 'input', short: 'i', type: 'string', description: 'Input embedding(s) JSON' }, ], examples: [ { command: 'claude-flow embeddings hyperbolic -a convert -i "[0.5, 0.3]"', description: 'Convert to Poincaré' }, { command: 'claude-flow embeddings hyperbolic -a distance', description: 'Compute hyperbolic distance' }, ], action: async (ctx) => { const action = ctx.flags.action || 'convert'; const curvature = parseFloat(ctx.flags.curvature || '-1'); const inputJson = ctx.flags.input; output.writeln(); output.writeln(output.bold('Hyperbolic Embeddings')); output.writeln(output.dim('Poincaré Ball Model')); output.writeln(output.dim('─'.repeat(50))); // Try to import hyperbolic functions from embeddings package try { const hyperbolic = await import('@claude-flow/embeddings').then(m => m).catch(() => null); if (!hyperbolic || !hyperbolic.euclideanToPoincare) { output.printWarning('@claude-flow/embeddings hyperbolic module not available'); output.printInfo('Install with: npm install @claude-flow/embeddings'); return { success: false, exitCode: 1 }; } if (!inputJson) { // Show help if no input output.printBox([ 'Hyperbolic embeddings excel at:', '• Hierarchical data representation', '• Tree-like structure preservation', '• Low-dimensional hierarchy encoding', '', 'Actions: convert, distance, centroid', '', 'Examples:', ' -a convert -i "[0.5, 0.3, 0.1]"', ' -a distance -i "[[0.1,0.2],[0.3,0.4]]"', ].join('\n'), 'Hyperbolic Geometry'); return { success: true }; } // Parse input vector(s) let input; try { input = JSON.parse(inputJson); } catch { output.printError('Invalid JSON input. Use format: "[0.5, 0.3]" or "[[0.1,0.2],[0.3,0.4]]"'); return { success: false, exitCode: 1 }; } switch (action) { case 'convert': { const vec = Array.isArray(input[0]) ? input[0] : input; const rawResult = hyperbolic.euclideanToPoincare(vec, { curvature }); const result = Array.from(rawResult); output.writeln(output.success('Euclidean → Poincaré conversion:')); output.writeln(); output.writeln(`Input (Euclidean): [${vec.slice(0, 6).map(v => v.toFixed(4)).join(', ')}${vec.length > 6 ? ', ...' : ''}]`); output.writeln(`Output (Poincaré): [${result.slice(0, 6).map(v => v.toFixed(4)).join(', ')}${result.length > 6 ? ', ...' : ''}]`); output.writeln(`Curvature: ${curvature}`); output.writeln(`Norm: ${Math.sqrt(result.reduce((s, v) => s + v * v, 0)).toFixed(6)} (must be < 1)`); return { success: true, data: { result } }; } case 'distance': { if (!Array.isArray(input[0]) || input.length < 2) { output.printError('Distance requires two vectors: "[[v1],[v2]]"'); return { success: false, exitCode: 1 }; } const [v1, v2] = input; const dist = hyperbolic.hyperbolicDistance(v1, v2, { curvature }); output.writeln(output.success('Hyperbolic (geodesic) distance:')); output.writeln(); output.writeln(`Vector 1: [${v1.slice(0, 4).map(v => v.toFixed(4)).join(', ')}...]`); output.writeln(`Vector 2: [${v2.slice(0, 4).map(v => v.toFixed(4)).join(', ')}...]`); output.writeln(`Distance: ${dist.toFixed(6)}`); return { success: true, data: { distance: dist } }; } case 'centroid': { if (!Array.isArray(input[0])) { output.printError('Centroid requires multiple vectors: "[[v1],[v2],...]"'); return { success: false, exitCode: 1 }; } const vectors = input; const rawCentroid = hyperbolic.hyperbolicCentroid(vectors, { curvature }); const centroid = Array.from(rawCentroid); output.writeln(output.success('Hyperbolic centroid (Fréchet mean):')); output.writeln(); output.writeln(`Input vectors: ${vectors.length}`); output.writeln(`Centroid: [${centroid.slice(0, 6).map(v => v.toFixed(4)).join(', ')}${centroid.length > 6 ? ', ...' : ''}]`); return { success: true, data: { centroid } }; } default: output.printError(`Unknown action: ${action}. Use: convert, distance, centroid`); return { success: false, exitCode: 1 }; } } catch (error) { output.printError(`Hyperbolic operation failed: ${error.message}`); return { success: false, exitCode: 1 }; } }, }; // Neural subcommand const neuralCommand = { name: 'neural', description: 'Neural substrate features (RuVector integration)', options: [ { name: 'feature', short: 'f', type: 'string', description: 'Feature: drift, memory, swarm, coherence, all', default: 'all' }, { name: 'init', type: 'boolean', description: 'Initialize neural substrate with RuVector' }, { name: 'drift-threshold', type: 'string', description: 'Semantic drift detection threshold', default: '0.3' }, { name: 'decay-rate', type: 'string', description: 'Memory decay rate (hippocampal dynamics)', default: '0.01' }, { name: 'consolidation-interval', type: 'string', description: 'Memory consolidation interval (ms)', default: '60000' }, ], examples: [ { command: 'claude-flow embeddings neural --init', description: 'Initialize RuVector substrate' }, { command: 'claude-flow embeddings neural -f drift', description: 'Semantic drift detection' }, { command: 'claude-flow embeddings neural -f memory', description: 'Memory physics (hippocampal)' }, { command: 'claude-flow embeddings neural -f coherence', description: 'Safety & alignment monitoring' }, { command: 'claude-flow embeddings neural --drift-threshold=0.2', description: 'Custom drift threshold' }, ], action: async (ctx) => { const feature = ctx.flags.feature || 'all'; const init = ctx.flags.init; const driftThreshold = parseFloat((ctx.flags['drift-threshold'] || ctx.flags.driftThreshold || '0.3')); const decayRate = parseFloat((ctx.flags['decay-rate'] || ctx.flags.decayRate || '0.01')); const consolidationInterval = parseInt((ctx.flags['consolidation-interval'] || ctx.flags.consolidationInterval || '60000'), 10); output.writeln(); output.writeln(output.bold('Neural Embedding Substrate (RuVector)')); output.writeln(output.dim('Treating embeddings as a synthetic nervous system')); output.writeln(output.dim('─'.repeat(60))); // Check if embeddings config exists const fs = await import('fs'); const path = await import('path'); const configPath = path.join(process.cwd(), '.claude-flow', 'embeddings.json'); if (!fs.existsSync(configPath)) { output.printWarning('Embeddings not initialized'); output.printInfo('Run "embeddings init" first to configure ONNX model'); return { success: false, exitCode: 1 }; } // Load and update config let config = {}; try { config = JSON.parse(fs.readFileSync(configPath, 'utf8')); } catch { config = {}; } if (init) { // Initialize neural substrate configuration config.neural = { enabled: true, driftThreshold, decayRate, consolidationInterval, ruvector: { enabled: true, sona: true, // Self-Optimizing Neural Architecture flashAttention: true, ewcPlusPlus: true, // Elastic Weight Consolidation }, features: { semanticDrift: true, memoryPhysics: true, stateMachine: true, swarmCoordination: true, coherenceMonitor: true,