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
JavaScript
/**
* 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,