Files
wifi-densepose/vendor/ruvector/docs/hooks/ARCHITECTURE.md

24 KiB
Raw Blame History

RuVector Hooks Architecture

Technical architecture documentation for the RuVector hooks system.

Table of Contents

  1. System Overview
  2. Component Architecture
  3. Intelligence Layer
  4. Hook Execution Flow
  5. Data Storage
  6. Integration Points
  7. Security Model
  8. Performance Optimization

System Overview

High-Level Architecture

┌──────────────────────────────────────────────────────────────────────┐
│                         Claude Code Runtime                           │
├──────────────────────────────────────────────────────────────────────┤
│                                                                        │
│  ┌─────────────┐     ┌─────────────┐     ┌─────────────┐             │
│  │  PreToolUse │────►│  Tool Exec  │────►│ PostToolUse │             │
│  │    Hooks    │     │             │     │    Hooks    │             │
│  └──────┬──────┘     └─────────────┘     └──────┬──────┘             │
│         │                                        │                    │
│         ▼                                        ▼                    │
│  ┌────────────────────────────────────────────────────────────┐      │
│  │                    Hook Dispatcher                          │      │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐   │      │
│  │  │ Matcher  │  │ Executor │  │ Response │  │ Timeout  │   │      │
│  │  │  Engine  │  │  Engine  │  │ Handler  │  │ Manager  │   │      │
│  │  └──────────┘  └──────────┘  └──────────┘  └──────────┘   │      │
│  └────────────────────────────────────────────────────────────┘      │
│                              │                                        │
│                              ▼                                        │
│  ┌────────────────────────────────────────────────────────────┐      │
│  │                  RuVector CLI Layer                         │      │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐   │      │
│  │  │ Command  │  │ Template │  │   Path   │  │ Config   │   │      │
│  │  │ Parser   │  │  Engine  │  │ Resolver │  │ Manager  │   │      │
│  │  └──────────┘  └──────────┘  └──────────┘  └──────────┘   │      │
│  └────────────────────────────────────────────────────────────┘      │
│                              │                                        │
│                              ▼                                        │
│  ┌────────────────────────────────────────────────────────────┐      │
│  │                   Intelligence Layer                        │      │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐   │      │
│  │  │Q-Learning│  │  Vector  │  │  Agent   │  │  Neural  │   │      │
│  │  │  Engine  │  │  Memory  │  │  Router  │  │ Trainer  │   │      │
│  │  └──────────┘  └──────────┘  └──────────┘  └──────────┘   │      │
│  └────────────────────────────────────────────────────────────┘      │
│                              │                                        │
│                              ▼                                        │
│  ┌────────────────────────────────────────────────────────────┐      │
│  │                    Storage Layer                            │      │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐                 │      │
│  │  │   JSON   │  │  RvLite  │  │  Global  │                 │      │
│  │  │  Files   │  │   HNSW   │  │ Patterns │                 │      │
│  │  └──────────┘  └──────────┘  └──────────┘                 │      │
│  └────────────────────────────────────────────────────────────┘      │
│                                                                        │
└──────────────────────────────────────────────────────────────────────┘

Design Principles

  1. Portability: No hardcoded paths; runtime resolution
  2. Minimal Overhead: <100ms total hook overhead
  3. Graceful Degradation: Hooks never block main flow
  4. Learning by Default: Automatic pattern improvement
  5. Cross-Platform: Linux, macOS, Windows support

Component Architecture

CLI Layer (crates/ruvector-cli)

The command-line interface for hook management.

crates/ruvector-cli/
├── src/
│   ├── cli/
│   │   ├── commands.rs        # Command definitions
│   │   ├── hooks/             # Hooks subcommands
│   │   │   ├── mod.rs         # Module exports
│   │   │   ├── init.rs        # hooks init
│   │   │   ├── install.rs     # hooks install
│   │   │   ├── migrate.rs     # hooks migrate
│   │   │   ├── stats.rs       # hooks stats
│   │   │   ├── export.rs      # hooks export
│   │   │   └── import.rs      # hooks import
│   │   └── ...
│   └── main.rs
├── templates/                  # Hook templates
│   ├── hooks.json.j2          # Portable hooks template
│   ├── config.toml.j2         # Configuration template
│   └── gitignore.j2           # Gitignore template
└── Cargo.toml

Template Engine

Uses Askama for type-safe template rendering:

#[derive(Template)]
#[template(path = "hooks.json.j2")]
struct HookTemplate {
    shell: String,          // Platform shell wrapper
    ruvector_cli: String,   // CLI invocation method
    project_root: String,   // Project root path
}

fn render_hooks() -> Result<String> {
    let template = HookTemplate {
        shell: get_shell_wrapper(),
        ruvector_cli: get_cli_invocation(),
        project_root: env::current_dir()?.display().to_string(),
    };
    Ok(template.render()?)
}

Path Resolution

Dynamic path resolution at runtime:

pub fn get_ruvector_home() -> Result<PathBuf> {
    // Priority order:
    // 1. RUVECTOR_HOME environment variable
    // 2. ~/.ruvector (Unix) or %APPDATA%\ruvector (Windows)

    if let Ok(home) = env::var("RUVECTOR_HOME") {
        return Ok(PathBuf::from(shellexpand::tilde(&home).to_string()));
    }

    let home_dir = dirs::home_dir()
        .ok_or_else(|| anyhow!("Could not determine home directory"))?;

    Ok(home_dir.join(".ruvector"))
}

pub fn get_cli_path() -> Result<String> {
    // Priority order:
    // 1. Binary in PATH
    // 2. npx ruvector
    // 3. Current executable

    if let Ok(path) = which::which("ruvector") {
        return Ok(path.display().to_string());
    }

    Ok("npx ruvector".to_string())
}

Intelligence Layer

Q-Learning Engine

Implements temporal difference learning for action selection:

// Q-value update equation
// Q(s,a) ← Q(s,a) + α[r + γ max_a' Q(s',a') - Q(s,a)]

class QLearning {
    constructor(options = {}) {
        this.alpha = options.learningRate || 0.1;   // Learning rate
        this.gamma = options.discount || 0.95;      // Discount factor
        this.qTable = new Map();                    // State-action values
    }

    update(state, action, reward, nextState) {
        const currentQ = this.getQ(state, action);
        const maxNextQ = this.getMaxQ(nextState);
        const newQ = currentQ + this.alpha * (reward + this.gamma * maxNextQ - currentQ);
        this.setQ(state, action, newQ);
    }

    selectAction(state) {
        // Epsilon-greedy exploration
        if (Math.random() < this.epsilon) {
            return this.randomAction(state);
        }
        return this.bestAction(state);
    }
}

State Representation

States encode file context and action type:

function encodeState(context) {
    const { file, crate, tool, previousSuccess } = context;

    return {
        fileType: getFileExtension(file),           // 'rs', 'ts', 'py'
        crateName: crate || 'unknown',              // 'ruvector-core'
        toolCategory: categorize(tool),             // 'edit', 'bash', 'search'
        historyHash: hashRecent(previousSuccess),   // Recent success pattern
    };
}

// State key for Q-table lookup
function stateKey(state) {
    return `${state.toolCategory}_${state.fileType}_in_${state.crateName}`;
}

Vector Memory

Semantic search using HNSW indexing:

class VectorMemory {
    constructor(dimensions = 128) {
        this.dimensions = dimensions;
        this.index = new HnswIndex({ dimensions, maxElements: 50000 });
        this.metadata = new Map();
    }

    async store(key, text, metadata) {
        const embedding = await this.embed(text);
        const id = this.index.add(embedding);
        this.metadata.set(id, { key, ...metadata });
        return id;
    }

    async search(query, k = 5) {
        const embedding = await this.embed(query);
        const results = this.index.search(embedding, k);
        return results.map(r => ({
            ...this.metadata.get(r.id),
            distance: r.distance
        }));
    }

    async embed(text) {
        // Simple embedding: TF-IDF + dimensionality reduction
        // Production: Use sentence-transformers or similar
        return textToEmbedding(text, this.dimensions);
    }
}

Agent Router

Intelligent agent assignment based on context:

class AgentRouter {
    constructor(qLearning, vectorMemory) {
        this.q = qLearning;
        this.memory = vectorMemory;
        this.agentTypes = loadAgentTypes();
    }

    async route(context) {
        const state = encodeState(context);

        // 1. Check Q-learning suggestion
        const qSuggestion = this.q.selectAction(state);
        const qConfidence = this.q.getQ(state, qSuggestion);

        // 2. Check similar past edits
        const similar = await this.memory.search(context.file, 3);
        const historyAgent = this.majorityVote(similar);

        // 3. Apply file type heuristics
        const heuristicAgent = this.fileTypeHeuristic(context.file);

        // 4. Combine signals
        return this.combine({
            q: { agent: qSuggestion, confidence: qConfidence },
            history: { agent: historyAgent, confidence: similar.length / 3 },
            heuristic: { agent: heuristicAgent, confidence: 0.5 }
        });
    }

    fileTypeHeuristic(file) {
        const ext = path.extname(file);
        const mapping = {
            '.rs': 'rust-developer',
            '.ts': 'typescript-developer',
            '.tsx': 'react-developer',
            '.py': 'python-developer',
            '.go': 'go-developer',
            '.sql': 'database-specialist',
        };
        return mapping[ext] || 'coder';
    }
}

Hook Execution Flow

PreToolUse Flow

┌─────────────────┐
│  Claude Code    │
│  Tool Request   │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  Match Hooks    │  ──► No match ──► Execute Tool
│  (Regex/Type)   │
└────────┬────────┘
         │ Match
         ▼
┌─────────────────┐
│  Execute Hook   │  timeout: 3000ms
│  Command        │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  Parse Result   │
│  (JSON/stdout)  │
└────────┬────────┘
         │
         ▼
    ┌────┴────┐
    │continue?│
    └────┬────┘
    │    │
   Yes   No
    │    │
    ▼    ▼
Execute  Block
 Tool    Tool

PostToolUse Flow

┌─────────────────┐
│  Tool Completed │
│  (Result Ready) │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  Match Hooks    │
│  (Regex/Type)   │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  Execute Hook   │  (async, non-blocking)
│  Command        │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  Intelligence   │
│  Update         │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  Q-value Train  │
│  Memory Store   │
│  Pattern Update │
└─────────────────┘

Session Hook Flow

Session Start                           Session End
     │                                       │
     ▼                                       ▼
┌──────────┐                          ┌──────────┐
│  Load    │                          │  Persist │
│  Config  │                          │  State   │
└────┬─────┘                          └────┬─────┘
     │                                     │
     ▼                                     ▼
┌──────────┐                          ┌──────────┐
│ Restore  │                          │  Export  │
│ Memory   │                          │ Metrics  │
└────┬─────┘                          └────┬─────┘
     │                                     │
     ▼                                     ▼
┌──────────┐                          ┌──────────┐
│ Display  │                          │ Generate │
│ Status   │                          │ Summary  │
└──────────┘                          └──────────┘

Data Storage

Directory Structure

Project Root
├── .ruvector/                    # Project-local data
│   ├── config.toml               # Configuration
│   ├── intelligence/
│   │   ├── memory.json           # Vector memory (JSON fallback)
│   │   ├── patterns.json         # Q-learning patterns
│   │   ├── trajectories.json     # Learning history
│   │   ├── feedback.json         # User feedback
│   │   └── memory.rvdb           # RvLite vector database
│   ├── logs/
│   │   └── hooks-YYYY-MM-DD.log  # Daily logs
│   └── .gitignore
│
└── .claude/
    └── settings.json             # Hook configurations

Global (~/.ruvector/)
├── global/
│   ├── patterns.json             # Cross-project patterns
│   ├── memory.rvdb               # Global vector memory
│   └── sequences.json            # Common file sequences
├── config.toml                   # Global configuration
└── cache/
    └── cli-path.txt              # Cached CLI location

Data Formats

patterns.json (Q-Learning)

{
  "edit_rs_in_ruvector-core": {
    "rust-developer": {
      "q_value": 0.823,
      "update_count": 47,
      "last_update": "2025-12-27T10:30:00Z"
    },
    "coder": {
      "q_value": 0.312,
      "update_count": 5,
      "last_update": "2025-12-20T14:22:00Z"
    }
  }
}

trajectories.json (Learning History)

[
  {
    "id": "traj_001",
    "state": "edit_rs_in_ruvector-core",
    "action": "rust-developer",
    "outcome": "success",
    "reward": 1.0,
    "timestamp": "2025-12-27T10:30:00Z",
    "ab_group": "treatment",
    "metadata": {
      "file": "crates/ruvector-core/src/lib.rs",
      "duration_ms": 1500
    }
  }
]

memory.rvdb (Vector Database)

RvLite database with HNSW indexing:

-- Schema (auto-created)
CREATE TABLE memories (
    id TEXT PRIMARY KEY,
    embedding VECTOR(128),
    content TEXT,
    metadata JSON,
    created_at TIMESTAMP
);

CREATE INDEX memories_hnsw ON memories USING hnsw (embedding);

Integration Points

Claude Code Integration

Hook configuration in .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "pattern",    // Regex for tool name
      "hooks": [{
        "type": "command",     // Shell command
        "command": "...",      // Command to execute
        "timeout": 3000        // Timeout in ms
      }]
    }],
    "PostToolUse": [...],
    "SessionStart": [...],
    "Stop": [...]              // Session end
  }
}

Claude-Flow Integration

MCP tool coordination:

// Pre-task hook with MCP
async function preTask(description) {
    // Store in coordination memory
    await mcp__claude_flow__memory_usage({
        action: "store",
        key: "swarm/task/current",
        namespace: "coordination",
        value: JSON.stringify({ description, started: Date.now() })
    });

    // Spawn recommended agents
    const agents = analyzeTaskNeeds(description);
    for (const agent of agents) {
        await mcp__claude_flow__agent_spawn({ type: agent });
    }
}

Git Integration

Pre-commit hook example:

#!/bin/bash
# .git/hooks/pre-commit

# Run RuVector pre-edit on staged files
FILES=$(git diff --cached --name-only --diff-filter=ACM)

for FILE in $FILES; do
    RESULT=$(npx ruvector hooks pre-edit --file "$FILE" --validate-syntax)
    CONTINUE=$(echo "$RESULT" | jq -r '.continue')

    if [ "$CONTINUE" = "false" ]; then
        echo "Pre-edit hook blocked: $FILE"
        echo "$RESULT" | jq -r '.reason'
        exit 1
    fi
done

Security Model

Command Injection Prevention

All user inputs are escaped:

use shell_escape::escape;

fn generate_hook_command(file_path: &str) -> String {
    let escaped = escape(file_path.into());
    format!(
        r#"/bin/bash -c 'npx ruvector hooks pre-edit --file {}'"#,
        escaped
    )
}

// Prevents: "; rm -rf /" attacks
// file_path = "test.ts; rm -rf /"
// escaped = "'test.ts; rm -rf /'"  (treated as literal string)

Timeout Protection

Hooks cannot hang indefinitely:

async function executeHookSafely(hook, timeout = 3000) {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), timeout);

    try {
        const result = await executeHook(hook, { signal: controller.signal });
        clearTimeout(timeoutId);
        return result;
    } catch (error) {
        if (error.name === 'AbortError') {
            console.warn('Hook timeout, continuing...');
            return { continue: true, reason: 'timeout' };
        }
        throw error;
    }
}

Graceful Failure

Hooks never block tool execution:

async function runPreHook(tool, context) {
    try {
        const result = await executeHookSafely(hook);
        return result.continue !== false;
    } catch (error) {
        console.warn(`Hook failed: ${error.message}`);
        return true;  // Continue on error
    }
}

Performance Optimization

Caching Strategy

class IntelligenceCache {
    constructor(maxAge = 300000) {  // 5 minutes
        this.cache = new Map();
        this.maxAge = maxAge;
    }

    get(key) {
        const entry = this.cache.get(key);
        if (!entry) return null;
        if (Date.now() - entry.timestamp > this.maxAge) {
            this.cache.delete(key);
            return null;
        }
        return entry.value;
    }

    set(key, value) {
        this.cache.set(key, { value, timestamp: Date.now() });
    }
}

// Cache agent routing decisions
const routingCache = new IntelligenceCache();

async function getAgent(file) {
    const cached = routingCache.get(file);
    if (cached) return cached;

    const agent = await computeAgent(file);
    routingCache.set(file, agent);
    return agent;
}

Async Operations

Non-blocking post-hooks:

// Fire-and-forget for training
function postEditHook(file, success) {
    // Synchronous: quick response
    const response = { continue: true, formatted: true };

    // Async: training (non-blocking)
    setImmediate(() => {
        trainPatterns(file, success).catch(console.warn);
        updateMemory(file).catch(console.warn);
        recordTrajectory(file, success).catch(console.warn);
    });

    return response;
}

Batch Operations

Reduce I/O with batching:

class BatchWriter {
    constructor(flushInterval = 5000) {
        this.queue = [];
        this.interval = setInterval(() => this.flush(), flushInterval);
    }

    add(item) {
        this.queue.push(item);
    }

    async flush() {
        if (this.queue.length === 0) return;

        const batch = this.queue.splice(0);
        await fs.appendFile(
            'trajectories.json',
            batch.map(JSON.stringify).join('\n') + '\n'
        );
    }
}

const trajectoryWriter = new BatchWriter();

Performance Targets

Operation Target Typical
Pre-edit hook <50ms 30ms
Post-edit hook <100ms 60ms
Session start <200ms 150ms
Memory search <10ms 5ms
Q-value lookup <1ms 0.1ms
Total overhead <100ms 70ms

See Also