mirror of
https://github.com/ruvnet/RuVector.git
synced 2026-05-23 12:55:26 +00:00
Add comprehensive hooks subcommand to ruvector CLI with: Core Commands: - init: Initialize hooks in project - install: Install hooks into Claude settings - stats: Show intelligence statistics Hook Operations: - pre-edit/post-edit: File editing intelligence - pre-command/post-command: Command execution hooks - session-start/session-end: Session management - pre-compact: Pre-compact hook Memory & Learning: - remember: Store content in semantic memory - recall: Search memory semantically - learn: Record Q-learning trajectories - suggest: Get best action for state - route: Route task to best agent V3 Intelligence: - record-error: Learn from error patterns - suggest-fix: Get fixes for error codes - suggest-next: Predict next files to edit - should-test: Check if tests should run Swarm/Hive-Mind: - swarm-register: Register agents - swarm-coordinate: Record coordination - swarm-optimize: Optimize task distribution - swarm-recommend: Get best agent - swarm-heal: Handle agent failures - swarm-stats: Show swarm statistics All commands tested and working. Data persists to ~/.ruvector/intelligence.json for cross-session learning.
361 lines
12 KiB
Rust
361 lines
12 KiB
Rust
//! Ruvector CLI - High-performance vector database command-line interface
|
|
|
|
use anyhow::Result;
|
|
use clap::{Parser, Subcommand};
|
|
use colored::*;
|
|
use std::path::PathBuf;
|
|
|
|
mod cli;
|
|
mod config;
|
|
|
|
use crate::cli::commands::*;
|
|
use crate::config::Config;
|
|
|
|
#[derive(Parser)]
|
|
#[command(name = "ruvector")]
|
|
#[command(about = "High-performance Rust vector database CLI", long_about = None)]
|
|
#[command(version)]
|
|
struct Cli {
|
|
/// Configuration file path
|
|
#[arg(short, long, global = true)]
|
|
config: Option<PathBuf>,
|
|
|
|
/// Enable debug mode
|
|
#[arg(short, long, global = true)]
|
|
debug: bool,
|
|
|
|
/// Disable colors
|
|
#[arg(long, global = true)]
|
|
no_color: bool,
|
|
|
|
#[command(subcommand)]
|
|
command: Commands,
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
enum Commands {
|
|
/// Create a new vector database
|
|
Create {
|
|
/// Database file path
|
|
#[arg(short, long, default_value = "./ruvector.db")]
|
|
path: String,
|
|
|
|
/// Vector dimensions
|
|
#[arg(short = 'D', long)]
|
|
dimensions: usize,
|
|
},
|
|
|
|
/// Insert vectors from a file
|
|
Insert {
|
|
/// Database file path
|
|
#[arg(short = 'b', long, default_value = "./ruvector.db")]
|
|
db: String,
|
|
|
|
/// Input file path
|
|
#[arg(short, long)]
|
|
input: String,
|
|
|
|
/// Input format (json, csv, npy)
|
|
#[arg(short, long, default_value = "json")]
|
|
format: String,
|
|
|
|
/// Hide progress bar
|
|
#[arg(long)]
|
|
no_progress: bool,
|
|
},
|
|
|
|
/// Search for similar vectors
|
|
Search {
|
|
/// Database file path
|
|
#[arg(short = 'b', long, default_value = "./ruvector.db")]
|
|
db: String,
|
|
|
|
/// Query vector (comma-separated floats or JSON array)
|
|
#[arg(short, long)]
|
|
query: String,
|
|
|
|
/// Number of results
|
|
#[arg(short = 'k', long, default_value = "10")]
|
|
top_k: usize,
|
|
|
|
/// Show full vectors in results
|
|
#[arg(long)]
|
|
show_vectors: bool,
|
|
},
|
|
|
|
/// Show database information
|
|
Info {
|
|
/// Database file path
|
|
#[arg(short = 'b', long, default_value = "./ruvector.db")]
|
|
db: String,
|
|
},
|
|
|
|
/// Run a quick performance benchmark
|
|
Benchmark {
|
|
/// Database file path
|
|
#[arg(short = 'b', long, default_value = "./ruvector.db")]
|
|
db: String,
|
|
|
|
/// Number of queries to run
|
|
#[arg(short = 'n', long, default_value = "1000")]
|
|
queries: usize,
|
|
},
|
|
|
|
/// Export database to file
|
|
Export {
|
|
/// Database file path
|
|
#[arg(short = 'b', long, default_value = "./ruvector.db")]
|
|
db: String,
|
|
|
|
/// Output file path
|
|
#[arg(short, long)]
|
|
output: String,
|
|
|
|
/// Output format (json, csv)
|
|
#[arg(short, long, default_value = "json")]
|
|
format: String,
|
|
},
|
|
|
|
/// Import from other vector databases
|
|
Import {
|
|
/// Database file path
|
|
#[arg(short = 'b', long, default_value = "./ruvector.db")]
|
|
db: String,
|
|
|
|
/// Source database type (faiss, pinecone, weaviate)
|
|
#[arg(short, long)]
|
|
source: String,
|
|
|
|
/// Source file or connection path
|
|
#[arg(short = 'p', long)]
|
|
source_path: String,
|
|
},
|
|
|
|
/// Graph database operations (Neo4j-compatible)
|
|
Graph {
|
|
#[command(subcommand)]
|
|
action: cli::graph::GraphCommands,
|
|
},
|
|
|
|
/// Self-learning intelligence hooks for Claude Code
|
|
Hooks {
|
|
#[command(subcommand)]
|
|
action: cli::hooks::HooksCommands,
|
|
},
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<()> {
|
|
let cli = Cli::parse();
|
|
|
|
// Initialize logging
|
|
if cli.debug {
|
|
tracing_subscriber::fmt()
|
|
.with_env_filter("ruvector=debug")
|
|
.init();
|
|
}
|
|
|
|
// Disable colors if requested
|
|
if cli.no_color {
|
|
colored::control::set_override(false);
|
|
}
|
|
|
|
// Load configuration
|
|
let config = Config::load(cli.config)?;
|
|
|
|
// Execute command
|
|
let result = match cli.command {
|
|
Commands::Create { path, dimensions } => create_database(&path, dimensions, &config),
|
|
Commands::Insert {
|
|
db,
|
|
input,
|
|
format,
|
|
no_progress,
|
|
} => insert_vectors(&db, &input, &format, &config, !no_progress),
|
|
Commands::Search {
|
|
db,
|
|
query,
|
|
top_k,
|
|
show_vectors,
|
|
} => {
|
|
let query_vec = parse_query_vector(&query)?;
|
|
search_vectors(&db, query_vec, top_k, &config, show_vectors)
|
|
}
|
|
Commands::Info { db } => show_info(&db, &config),
|
|
Commands::Benchmark { db, queries } => run_benchmark(&db, &config, queries),
|
|
Commands::Export { db, output, format } => export_database(&db, &output, &format, &config),
|
|
Commands::Import {
|
|
db,
|
|
source,
|
|
source_path,
|
|
} => import_from_external(&db, &source, &source_path, &config),
|
|
Commands::Graph { action } => {
|
|
use cli::graph::GraphCommands;
|
|
match action {
|
|
GraphCommands::Create {
|
|
path,
|
|
name,
|
|
indexed,
|
|
} => cli::graph::create_graph(&path, &name, indexed, &config),
|
|
GraphCommands::Query {
|
|
db,
|
|
cypher,
|
|
format,
|
|
explain,
|
|
} => cli::graph::execute_query(&db, &cypher, &format, explain, &config),
|
|
GraphCommands::Shell { db, multiline } => {
|
|
cli::graph::run_shell(&db, multiline, &config)
|
|
}
|
|
GraphCommands::Import {
|
|
db,
|
|
input,
|
|
format,
|
|
graph,
|
|
skip_errors,
|
|
} => cli::graph::import_graph(&db, &input, &format, &graph, skip_errors, &config),
|
|
GraphCommands::Export {
|
|
db,
|
|
output,
|
|
format,
|
|
graph,
|
|
} => cli::graph::export_graph(&db, &output, &format, &graph, &config),
|
|
GraphCommands::Info { db, detailed } => {
|
|
cli::graph::show_graph_info(&db, detailed, &config)
|
|
}
|
|
GraphCommands::Benchmark {
|
|
db,
|
|
queries,
|
|
bench_type,
|
|
} => cli::graph::run_graph_benchmark(&db, queries, &bench_type, &config),
|
|
GraphCommands::Serve {
|
|
db,
|
|
host,
|
|
http_port,
|
|
grpc_port,
|
|
graphql,
|
|
} => cli::graph::serve_graph(&db, &host, http_port, grpc_port, graphql, &config),
|
|
}
|
|
}
|
|
Commands::Hooks { action } => {
|
|
use cli::hooks::HooksCommands;
|
|
match action {
|
|
HooksCommands::Init { force } => cli::hooks::init_hooks(force, &config),
|
|
HooksCommands::Install { settings_dir } => cli::hooks::install_hooks(&settings_dir, &config),
|
|
HooksCommands::Stats => cli::hooks::show_stats(&config),
|
|
HooksCommands::Remember { memory_type, content } => {
|
|
cli::hooks::remember_content(&memory_type, &content.join(" "), &config)
|
|
}
|
|
HooksCommands::Recall { query, top_k } => {
|
|
cli::hooks::recall_content(&query.join(" "), top_k, &config)
|
|
}
|
|
HooksCommands::Learn { state, action, reward } => {
|
|
cli::hooks::learn_trajectory(&state, &action, reward, &config)
|
|
}
|
|
HooksCommands::Suggest { state, actions } => {
|
|
cli::hooks::suggest_action(&state, &actions, &config)
|
|
}
|
|
HooksCommands::Route { task, file, crate_name, operation } => {
|
|
cli::hooks::route_task(
|
|
&task.join(" "),
|
|
file.as_deref(),
|
|
crate_name.as_deref(),
|
|
&operation,
|
|
&config,
|
|
)
|
|
}
|
|
HooksCommands::PreEdit { file } => cli::hooks::pre_edit_hook(&file, &config),
|
|
HooksCommands::PostEdit { file, success } => {
|
|
cli::hooks::post_edit_hook(&file, success, &config)
|
|
}
|
|
HooksCommands::PreCommand { command } => {
|
|
cli::hooks::pre_command_hook(&command.join(" "), &config)
|
|
}
|
|
HooksCommands::PostCommand { command, success, stderr } => {
|
|
cli::hooks::post_command_hook(&command.join(" "), success, stderr.as_deref(), &config)
|
|
}
|
|
HooksCommands::SessionStart { session_id } => {
|
|
cli::hooks::session_start_hook(session_id.as_deref(), &config)
|
|
}
|
|
HooksCommands::SessionEnd { export_metrics } => {
|
|
cli::hooks::session_end_hook(export_metrics, &config)
|
|
}
|
|
HooksCommands::PreCompact { length } => {
|
|
cli::hooks::pre_compact_hook(length, &config)
|
|
}
|
|
HooksCommands::RecordError { command, stderr } => {
|
|
cli::hooks::record_error_cmd(&command, &stderr, &config)
|
|
}
|
|
HooksCommands::SuggestFix { error_code } => {
|
|
cli::hooks::suggest_fix_cmd(&error_code, &config)
|
|
}
|
|
HooksCommands::SuggestNext { file, count } => {
|
|
cli::hooks::suggest_next_cmd(&file, count, &config)
|
|
}
|
|
HooksCommands::ShouldTest { file } => {
|
|
cli::hooks::should_test_cmd(&file, &config)
|
|
}
|
|
HooksCommands::SwarmRegister { agent_id, agent_type, capabilities } => {
|
|
cli::hooks::swarm_register_cmd(&agent_id, &agent_type, capabilities.as_deref(), &config)
|
|
}
|
|
HooksCommands::SwarmCoordinate { source, target, weight } => {
|
|
cli::hooks::swarm_coordinate_cmd(&source, &target, weight, &config)
|
|
}
|
|
HooksCommands::SwarmOptimize { tasks } => {
|
|
cli::hooks::swarm_optimize_cmd(&tasks, &config)
|
|
}
|
|
HooksCommands::SwarmRecommend { task_type } => {
|
|
cli::hooks::swarm_recommend_cmd(&task_type, &config)
|
|
}
|
|
HooksCommands::SwarmHeal { agent_id } => {
|
|
cli::hooks::swarm_heal_cmd(&agent_id, &config)
|
|
}
|
|
HooksCommands::SwarmStats => cli::hooks::swarm_stats_cmd(&config),
|
|
}
|
|
}
|
|
};
|
|
|
|
// Handle errors
|
|
if let Err(e) = result {
|
|
eprintln!("{}", cli::format::format_error(&e.to_string()));
|
|
if cli.debug {
|
|
eprintln!("\n{:#?}", e);
|
|
} else {
|
|
eprintln!("\n{}", "Run with --debug for more details".dimmed());
|
|
}
|
|
std::process::exit(1);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Parse query vector from string
|
|
fn parse_query_vector(s: &str) -> Result<Vec<f32>> {
|
|
// Try JSON first
|
|
if s.trim().starts_with('[') {
|
|
return serde_json::from_str(s)
|
|
.map_err(|e| anyhow::anyhow!("Failed to parse query vector as JSON: {}", e));
|
|
}
|
|
|
|
// Try comma-separated
|
|
s.split(',')
|
|
.map(|s| s.trim().parse::<f32>())
|
|
.collect::<std::result::Result<Vec<f32>, _>>()
|
|
.map_err(|e| anyhow::anyhow!("Failed to parse query vector: {}", e))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_parse_query_vector_json() {
|
|
let vec = parse_query_vector("[1.0, 2.0, 3.0]").unwrap();
|
|
assert_eq!(vec, vec![1.0, 2.0, 3.0]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_query_vector_csv() {
|
|
let vec = parse_query_vector("1.0, 2.0, 3.0").unwrap();
|
|
assert_eq!(vec, vec![1.0, 2.0, 3.0]);
|
|
}
|
|
}
|