ruvector 0.1.99 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/bin/cli.js +820 -47
- package/bin/mcp-server.js +71 -18
- package/package.json +28 -4
- package/HOOKS.md +0 -221
- package/PACKAGE_SUMMARY.md +0 -409
- package/examples/api-usage.js +0 -211
- package/examples/cli-demo.sh +0 -85
package/bin/mcp-server.js
CHANGED
|
@@ -36,18 +36,38 @@ function validateRvfPath(filePath) {
|
|
|
36
36
|
if (typeof filePath !== 'string' || filePath.length === 0) {
|
|
37
37
|
throw new Error('Path must be a non-empty string');
|
|
38
38
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
throw new Error('Path traversal detected');
|
|
39
|
+
// Block null bytes
|
|
40
|
+
if (filePath.includes('\0')) {
|
|
41
|
+
throw new Error('Path contains null bytes');
|
|
43
42
|
}
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
// Resolve to absolute, then canonicalize via realpath if it exists
|
|
44
|
+
let resolved = path.resolve(filePath);
|
|
45
|
+
try {
|
|
46
|
+
// Resolve symlinks for existing paths to prevent symlink-based escapes
|
|
47
|
+
resolved = fs.realpathSync(resolved);
|
|
48
|
+
} catch {
|
|
49
|
+
// Path doesn't exist yet — resolve the parent directory
|
|
50
|
+
const parentDir = path.dirname(resolved);
|
|
51
|
+
try {
|
|
52
|
+
const realParent = fs.realpathSync(parentDir);
|
|
53
|
+
resolved = path.join(realParent, path.basename(resolved));
|
|
54
|
+
} catch {
|
|
55
|
+
// Parent doesn't exist either — keep the resolved path for the block check
|
|
49
56
|
}
|
|
50
57
|
}
|
|
58
|
+
// Confine to the current working directory
|
|
59
|
+
const cwd = process.cwd();
|
|
60
|
+
if (!resolved.startsWith(cwd + path.sep) && resolved !== cwd) {
|
|
61
|
+
// Also block sensitive system paths regardless
|
|
62
|
+
const blocked = ['/etc', '/proc', '/sys', '/dev', '/boot', '/root', '/var/run', '/var/log', '/tmp'];
|
|
63
|
+
for (const prefix of blocked) {
|
|
64
|
+
if (resolved.startsWith(prefix)) {
|
|
65
|
+
throw new Error(`Access denied: path resolves to '${resolved}' which is outside the working directory and in restricted area '${prefix}'`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Allow paths outside cwd only if they're not in blocked directories
|
|
69
|
+
// (for tools that reference project files by absolute path)
|
|
70
|
+
}
|
|
51
71
|
return resolved;
|
|
52
72
|
}
|
|
53
73
|
|
|
@@ -57,14 +77,24 @@ function validateRvfPath(filePath) {
|
|
|
57
77
|
*/
|
|
58
78
|
function sanitizeShellArg(arg) {
|
|
59
79
|
if (typeof arg !== 'string') return '';
|
|
60
|
-
// Remove null bytes, backticks, $(), and other shell metacharacters
|
|
80
|
+
// Remove null bytes, backticks, $(), quotes, newlines, and other shell metacharacters
|
|
61
81
|
return arg
|
|
62
82
|
.replace(/\0/g, '')
|
|
63
|
-
.replace(/[
|
|
83
|
+
.replace(/[\r\n]/g, '')
|
|
84
|
+
.replace(/[`$(){}|;&<>!'"\\]/g, '')
|
|
64
85
|
.replace(/\.\./g, '')
|
|
65
86
|
.slice(0, 4096);
|
|
66
87
|
}
|
|
67
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Validate a numeric argument (returns integer or default).
|
|
91
|
+
* Prevents injection via numeric-looking fields.
|
|
92
|
+
*/
|
|
93
|
+
function sanitizeNumericArg(arg, defaultVal) {
|
|
94
|
+
const n = parseInt(arg, 10);
|
|
95
|
+
return Number.isFinite(n) && n > 0 ? n : (defaultVal || 0);
|
|
96
|
+
}
|
|
97
|
+
|
|
68
98
|
// Try to load the full IntelligenceEngine
|
|
69
99
|
let IntelligenceEngine = null;
|
|
70
100
|
let engineAvailable = false;
|
|
@@ -1319,7 +1349,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1319
1349
|
let cmd = 'npx ruvector hooks init';
|
|
1320
1350
|
if (args.force) cmd += ' --force';
|
|
1321
1351
|
if (args.pretrain) cmd += ' --pretrain';
|
|
1322
|
-
if (args.build_agents) cmd += ` --build-agents ${args.build_agents}`;
|
|
1352
|
+
if (args.build_agents) cmd += ` --build-agents ${sanitizeShellArg(args.build_agents)}`;
|
|
1323
1353
|
|
|
1324
1354
|
try {
|
|
1325
1355
|
const output = execSync(cmd, { encoding: 'utf-8', timeout: 60000 });
|
|
@@ -1341,7 +1371,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1341
1371
|
|
|
1342
1372
|
case 'hooks_pretrain': {
|
|
1343
1373
|
let cmd = 'npx ruvector hooks pretrain';
|
|
1344
|
-
if (args.depth) cmd += ` --depth ${args.depth}`;
|
|
1374
|
+
if (args.depth) cmd += ` --depth ${sanitizeNumericArg(args.depth, 3)}`;
|
|
1345
1375
|
if (args.skip_git) cmd += ' --skip-git';
|
|
1346
1376
|
if (args.verbose) cmd += ' --verbose';
|
|
1347
1377
|
|
|
@@ -1371,7 +1401,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1371
1401
|
|
|
1372
1402
|
case 'hooks_build_agents': {
|
|
1373
1403
|
let cmd = 'npx ruvector hooks build-agents';
|
|
1374
|
-
if (args.focus) cmd += ` --focus ${args.focus}`;
|
|
1404
|
+
if (args.focus) cmd += ` --focus ${sanitizeShellArg(args.focus)}`;
|
|
1375
1405
|
if (args.include_prompts) cmd += ' --include-prompts';
|
|
1376
1406
|
|
|
1377
1407
|
try {
|
|
@@ -1484,21 +1514,44 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1484
1514
|
const data = args.data;
|
|
1485
1515
|
const merge = args.merge !== false;
|
|
1486
1516
|
|
|
1487
|
-
|
|
1517
|
+
// Validate imported data structure to prevent prototype pollution and injection
|
|
1518
|
+
if (typeof data !== 'object' || data === null || Array.isArray(data)) {
|
|
1519
|
+
throw new Error('Import data must be a non-null object');
|
|
1520
|
+
}
|
|
1521
|
+
const allowedKeys = ['patterns', 'memories', 'errors', 'agents', 'edges', 'trajectories'];
|
|
1522
|
+
for (const key of Object.keys(data)) {
|
|
1523
|
+
if (!allowedKeys.includes(key)) {
|
|
1524
|
+
throw new Error(`Unknown import key: '${key}'. Allowed: ${allowedKeys.join(', ')}`);
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
// Prevent prototype pollution via __proto__, constructor, prototype keys
|
|
1528
|
+
const dangerousKeys = ['__proto__', 'constructor', 'prototype'];
|
|
1529
|
+
function checkForProtoPollution(obj, path) {
|
|
1530
|
+
if (typeof obj !== 'object' || obj === null) return;
|
|
1531
|
+
for (const key of Object.keys(obj)) {
|
|
1532
|
+
if (dangerousKeys.includes(key)) {
|
|
1533
|
+
throw new Error(`Dangerous key '${key}' detected at ${path}.${key}`);
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
if (data.patterns) checkForProtoPollution(data.patterns, 'patterns');
|
|
1538
|
+
if (data.errors) checkForProtoPollution(data.errors, 'errors');
|
|
1539
|
+
|
|
1540
|
+
if (data.patterns && typeof data.patterns === 'object') {
|
|
1488
1541
|
if (merge) {
|
|
1489
1542
|
Object.assign(intel.data.patterns, data.patterns);
|
|
1490
1543
|
} else {
|
|
1491
1544
|
intel.data.patterns = data.patterns;
|
|
1492
1545
|
}
|
|
1493
1546
|
}
|
|
1494
|
-
if (data.memories) {
|
|
1547
|
+
if (data.memories && Array.isArray(data.memories)) {
|
|
1495
1548
|
if (merge) {
|
|
1496
1549
|
intel.data.memories = [...(intel.data.memories || []), ...data.memories];
|
|
1497
1550
|
} else {
|
|
1498
1551
|
intel.data.memories = data.memories;
|
|
1499
1552
|
}
|
|
1500
1553
|
}
|
|
1501
|
-
if (data.errors) {
|
|
1554
|
+
if (data.errors && typeof data.errors === 'object') {
|
|
1502
1555
|
if (merge) {
|
|
1503
1556
|
Object.assign(intel.data.errors, data.errors);
|
|
1504
1557
|
} else {
|
|
@@ -2426,7 +2479,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2426
2479
|
|
|
2427
2480
|
case 'workers_status': {
|
|
2428
2481
|
try {
|
|
2429
|
-
const cmdArgs = args.workerId ? `workers status ${args.workerId}` : 'workers status';
|
|
2482
|
+
const cmdArgs = args.workerId ? `workers status ${sanitizeShellArg(args.workerId)}` : 'workers status';
|
|
2430
2483
|
const result = execSync(`npx agentic-flow@alpha ${cmdArgs}`, {
|
|
2431
2484
|
encoding: 'utf-8',
|
|
2432
2485
|
timeout: 15000,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ruvector",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "High-performance vector database for Node.js with automatic native/WASM fallback",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "tsc && cp src/core/onnx/pkg/package.json dist/core/onnx/pkg/",
|
|
12
12
|
"prepublishOnly": "npm run build",
|
|
13
|
-
"test": "node test/integration.js"
|
|
13
|
+
"test": "node test/integration.js && node test/cli-commands.js"
|
|
14
14
|
},
|
|
15
15
|
"keywords": [
|
|
16
16
|
"vector",
|
|
@@ -41,7 +41,15 @@
|
|
|
41
41
|
"continual-learning",
|
|
42
42
|
"onnx",
|
|
43
43
|
"semantic-embeddings",
|
|
44
|
-
"minilm"
|
|
44
|
+
"minilm",
|
|
45
|
+
"brain",
|
|
46
|
+
"shared-intelligence",
|
|
47
|
+
"mcp",
|
|
48
|
+
"edge-computing",
|
|
49
|
+
"pi-brain",
|
|
50
|
+
"identity",
|
|
51
|
+
"pi-key",
|
|
52
|
+
"distributed-compute"
|
|
45
53
|
],
|
|
46
54
|
"author": "ruv.io Team <info@ruv.io> (https://ruv.io)",
|
|
47
55
|
"homepage": "https://ruv.io",
|
|
@@ -71,7 +79,23 @@
|
|
|
71
79
|
"@types/node": "^20.10.5",
|
|
72
80
|
"typescript": "^5.3.3"
|
|
73
81
|
},
|
|
82
|
+
"files": [
|
|
83
|
+
"bin/",
|
|
84
|
+
"dist/",
|
|
85
|
+
"README.md",
|
|
86
|
+
"LICENSE"
|
|
87
|
+
],
|
|
88
|
+
"peerDependencies": {
|
|
89
|
+
"@ruvector/pi-brain": ">=0.1.0",
|
|
90
|
+
"@ruvector/ruvllm": ">=2.0.0",
|
|
91
|
+
"@ruvector/router": ">=0.1.0"
|
|
92
|
+
},
|
|
93
|
+
"peerDependenciesMeta": {
|
|
94
|
+
"@ruvector/pi-brain": { "optional": true },
|
|
95
|
+
"@ruvector/ruvllm": { "optional": true },
|
|
96
|
+
"@ruvector/router": { "optional": true }
|
|
97
|
+
},
|
|
74
98
|
"engines": {
|
|
75
|
-
"node": ">=
|
|
99
|
+
"node": ">=18.0.0"
|
|
76
100
|
}
|
|
77
101
|
}
|
package/HOOKS.md
DELETED
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
# RuVector Hooks for Claude Code
|
|
2
|
-
|
|
3
|
-
Self-learning intelligence hooks that enhance Claude Code with Q-learning, vector memory, and automatic agent routing.
|
|
4
|
-
|
|
5
|
-
## Quick Start
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
# Full setup: hooks + pretrain + optimized agents
|
|
9
|
-
npx ruvector hooks init --pretrain --build-agents quality
|
|
10
|
-
|
|
11
|
-
# Or step by step:
|
|
12
|
-
npx ruvector hooks init # Setup hooks
|
|
13
|
-
npx ruvector hooks pretrain # Analyze repository
|
|
14
|
-
npx ruvector hooks build-agents # Generate agent configs
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## What It Does
|
|
18
|
-
|
|
19
|
-
RuVector hooks integrate with Claude Code to provide:
|
|
20
|
-
|
|
21
|
-
| Feature | Description |
|
|
22
|
-
|---------|-------------|
|
|
23
|
-
| **Agent Routing** | Suggests the best agent for each file type based on learned patterns |
|
|
24
|
-
| **Co-edit Patterns** | Predicts "likely next files" from git history |
|
|
25
|
-
| **Vector Memory** | Semantic recall of project context |
|
|
26
|
-
| **Command Analysis** | Risk assessment for bash commands |
|
|
27
|
-
| **Self-Learning** | Q-learning improves suggestions over time |
|
|
28
|
-
|
|
29
|
-
## Commands
|
|
30
|
-
|
|
31
|
-
### Initialization
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
# Full configuration
|
|
35
|
-
npx ruvector hooks init
|
|
36
|
-
|
|
37
|
-
# With pretrain and agent building
|
|
38
|
-
npx ruvector hooks init --pretrain --build-agents security
|
|
39
|
-
|
|
40
|
-
# Minimal (basic hooks only)
|
|
41
|
-
npx ruvector hooks init --minimal
|
|
42
|
-
|
|
43
|
-
# Options
|
|
44
|
-
--force # Overwrite existing settings
|
|
45
|
-
--minimal # Basic hooks only
|
|
46
|
-
--pretrain # Run pretrain after init
|
|
47
|
-
--build-agents # Generate optimized agents (quality|speed|security|testing|fullstack)
|
|
48
|
-
--no-claude-md # Skip CLAUDE.md creation
|
|
49
|
-
--no-permissions # Skip permissions config
|
|
50
|
-
--no-env # Skip environment variables
|
|
51
|
-
--no-gitignore # Skip .gitignore update
|
|
52
|
-
--no-mcp # Skip MCP server config
|
|
53
|
-
--no-statusline # Skip status line config
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### Pretrain
|
|
57
|
-
|
|
58
|
-
Analyze your repository to bootstrap intelligence:
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
npx ruvector hooks pretrain
|
|
62
|
-
|
|
63
|
-
# Options
|
|
64
|
-
--depth <n> # Git history depth (default: 100)
|
|
65
|
-
--verbose # Show detailed progress
|
|
66
|
-
--skip-git # Skip git history analysis
|
|
67
|
-
--skip-files # Skip file structure analysis
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
**What it learns:**
|
|
71
|
-
- File type → Agent mapping (`.rs` → rust-developer)
|
|
72
|
-
- Co-edit patterns from git history
|
|
73
|
-
- Directory → Agent mapping
|
|
74
|
-
- Project context memories
|
|
75
|
-
|
|
76
|
-
### Build Agents
|
|
77
|
-
|
|
78
|
-
Generate optimized `.claude/agents/` configurations:
|
|
79
|
-
|
|
80
|
-
```bash
|
|
81
|
-
npx ruvector hooks build-agents --focus quality
|
|
82
|
-
|
|
83
|
-
# Focus modes
|
|
84
|
-
--focus quality # Code quality, best practices (default)
|
|
85
|
-
--focus speed # Rapid development, prototyping
|
|
86
|
-
--focus security # OWASP, input validation, encryption
|
|
87
|
-
--focus testing # TDD, comprehensive coverage
|
|
88
|
-
--focus fullstack # Balanced frontend/backend/database
|
|
89
|
-
|
|
90
|
-
# Options
|
|
91
|
-
--output <dir> # Output directory (default: .claude/agents)
|
|
92
|
-
--format <fmt> # yaml, json, or md (default: yaml)
|
|
93
|
-
--include-prompts # Include system prompts in agent configs
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### Verification & Diagnostics
|
|
97
|
-
|
|
98
|
-
```bash
|
|
99
|
-
# Check if hooks are working
|
|
100
|
-
npx ruvector hooks verify
|
|
101
|
-
|
|
102
|
-
# Diagnose and fix issues
|
|
103
|
-
npx ruvector hooks doctor
|
|
104
|
-
npx ruvector hooks doctor --fix
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Data Management
|
|
108
|
-
|
|
109
|
-
```bash
|
|
110
|
-
# View statistics
|
|
111
|
-
npx ruvector hooks stats
|
|
112
|
-
|
|
113
|
-
# Export intelligence data
|
|
114
|
-
npx ruvector hooks export -o backup.json
|
|
115
|
-
npx ruvector hooks export --include-all
|
|
116
|
-
|
|
117
|
-
# Import intelligence data
|
|
118
|
-
npx ruvector hooks import backup.json
|
|
119
|
-
npx ruvector hooks import backup.json --merge
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
### Memory Operations
|
|
123
|
-
|
|
124
|
-
```bash
|
|
125
|
-
# Store context in vector memory
|
|
126
|
-
npx ruvector hooks remember "API uses JWT auth" -t project
|
|
127
|
-
|
|
128
|
-
# Semantic search memory
|
|
129
|
-
npx ruvector hooks recall "authentication"
|
|
130
|
-
|
|
131
|
-
# Route a task to best agent
|
|
132
|
-
npx ruvector hooks route "implement user login"
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
## Hook Events
|
|
136
|
-
|
|
137
|
-
| Event | Trigger | RuVector Action |
|
|
138
|
-
|-------|---------|-----------------|
|
|
139
|
-
| **PreToolUse** | Before Edit/Write/Bash | Agent routing, file analysis, command risk |
|
|
140
|
-
| **PostToolUse** | After Edit/Write/Bash | Q-learning update, pattern recording |
|
|
141
|
-
| **SessionStart** | Conversation begins | Load intelligence, display stats |
|
|
142
|
-
| **Stop** | Conversation ends | Save learning data |
|
|
143
|
-
| **UserPromptSubmit** | User sends message | Context suggestions |
|
|
144
|
-
| **PreCompact** | Before context compaction | Preserve important context |
|
|
145
|
-
| **Notification** | Any notification | Track events for learning |
|
|
146
|
-
|
|
147
|
-
## Generated Files
|
|
148
|
-
|
|
149
|
-
After running `hooks init`:
|
|
150
|
-
|
|
151
|
-
```
|
|
152
|
-
your-project/
|
|
153
|
-
├── .claude/
|
|
154
|
-
│ ├── settings.json # Hooks configuration
|
|
155
|
-
│ ├── statusline.sh # Status bar script
|
|
156
|
-
│ └── agents/ # Generated agents (with --build-agents)
|
|
157
|
-
│ ├── rust-specialist.yaml
|
|
158
|
-
│ ├── typescript-specialist.yaml
|
|
159
|
-
│ ├── test-architect.yaml
|
|
160
|
-
│ └── project-coordinator.yaml
|
|
161
|
-
├── .ruvector/
|
|
162
|
-
│ └── intelligence.json # Learning data
|
|
163
|
-
├── CLAUDE.md # Project documentation
|
|
164
|
-
└── .gitignore # Updated with .ruvector/
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
## Environment Variables
|
|
168
|
-
|
|
169
|
-
| Variable | Default | Description |
|
|
170
|
-
|----------|---------|-------------|
|
|
171
|
-
| `RUVECTOR_INTELLIGENCE_ENABLED` | `true` | Enable/disable intelligence |
|
|
172
|
-
| `RUVECTOR_LEARNING_RATE` | `0.1` | Q-learning rate (0.0-1.0) |
|
|
173
|
-
| `RUVECTOR_MEMORY_BACKEND` | `rvlite` | Memory storage backend |
|
|
174
|
-
| `INTELLIGENCE_MODE` | `treatment` | A/B testing mode |
|
|
175
|
-
|
|
176
|
-
## Example Output
|
|
177
|
-
|
|
178
|
-
### Agent Routing
|
|
179
|
-
```
|
|
180
|
-
🧠 Intelligence Analysis:
|
|
181
|
-
📁 src/api/routes.ts
|
|
182
|
-
🤖 Recommended: typescript-developer (85% confidence)
|
|
183
|
-
→ learned from 127 .ts files in repo
|
|
184
|
-
📎 Likely next files:
|
|
185
|
-
- src/api/handlers.ts (12 co-edits)
|
|
186
|
-
- src/types/api.ts (8 co-edits)
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
### Command Analysis
|
|
190
|
-
```
|
|
191
|
-
🧠 Command Analysis:
|
|
192
|
-
📦 Category: rust
|
|
193
|
-
🏷️ Type: test
|
|
194
|
-
✅ Risk: LOW
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
## Best Practices
|
|
198
|
-
|
|
199
|
-
1. **Run pretrain on existing repos** — Bootstrap intelligence before starting work
|
|
200
|
-
2. **Use focus modes** — Match agent generation to your current task
|
|
201
|
-
3. **Export before major changes** — Backup learning data
|
|
202
|
-
4. **Let it learn** — Intelligence improves with each edit
|
|
203
|
-
|
|
204
|
-
## Troubleshooting
|
|
205
|
-
|
|
206
|
-
```bash
|
|
207
|
-
# Check setup
|
|
208
|
-
npx ruvector hooks verify
|
|
209
|
-
|
|
210
|
-
# Fix common issues
|
|
211
|
-
npx ruvector hooks doctor --fix
|
|
212
|
-
|
|
213
|
-
# Reset and reinitialize
|
|
214
|
-
npx ruvector hooks init --force --pretrain
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
## Links
|
|
218
|
-
|
|
219
|
-
- [RuVector GitHub](https://github.com/ruvnet/ruvector)
|
|
220
|
-
- [npm Package](https://www.npmjs.com/package/ruvector)
|
|
221
|
-
- [Claude Code Documentation](https://docs.anthropic.com/claude-code)
|