400 lines
13 KiB
JavaScript
Executable File
400 lines
13 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
/**
|
|
* RuVector Intelligence CLI
|
|
*
|
|
* Commands:
|
|
* remember <type> <content> - Store in vector memory
|
|
* recall <query> - Search memory semantically
|
|
* learn <state> <action> <reward> - Record learning trajectory
|
|
* suggest <state> <actions...> - Get best action suggestion
|
|
* route <task> [--file <f>] [--crate <c>] - Route to best agent
|
|
* stats - Show intelligence stats
|
|
* pre-edit <file> - Pre-edit intelligence hook
|
|
* post-edit <file> <success> - Post-edit learning hook
|
|
*/
|
|
|
|
import RuVectorIntelligence from './index.js';
|
|
import SwarmOptimizer from './swarm.js';
|
|
import { basename, extname } from 'path';
|
|
|
|
const intel = new RuVectorIntelligence();
|
|
const swarm = new SwarmOptimizer();
|
|
|
|
async function main() {
|
|
const args = process.argv.slice(2);
|
|
const command = args[0];
|
|
|
|
if (!command) {
|
|
console.log(`
|
|
🧠 RuVector Intelligence CLI
|
|
|
|
Commands:
|
|
remember <type> <content> Store in semantic memory
|
|
recall <query> Search memory
|
|
learn <state> <action> <reward> Record trajectory
|
|
suggest <state> <action1,action2,...> Get best action
|
|
route <task> --file <f> --crate <c> Route to agent
|
|
stats Show system stats
|
|
|
|
Hooks:
|
|
pre-edit <file> Pre-edit intelligence
|
|
post-edit <file> <success> Post-edit learning
|
|
pre-command <cmd> Pre-command intelligence
|
|
post-command <cmd> <success> [stderr] Post-command learning
|
|
|
|
v3 Features:
|
|
record-error <cmd> <stderr> Record error for pattern learning
|
|
suggest-fix <error-code> Get suggested fixes for error
|
|
suggest-next <file> Suggest next files to edit
|
|
should-test <file> Check if tests should run
|
|
|
|
Swarm (Hive-Mind):
|
|
swarm-register <id> <type> Register agent in swarm
|
|
swarm-coordinate <src> <dst> Record agent coordination
|
|
swarm-optimize <tasks...> Optimize task distribution
|
|
swarm-recommend <type> Get best agent for task
|
|
swarm-heal <agent-id> Handle agent failure
|
|
swarm-stats Show swarm statistics
|
|
`);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
switch (command) {
|
|
case 'remember': {
|
|
const [, type, ...contentParts] = args;
|
|
const content = contentParts.join(' ');
|
|
const id = await intel.remember(type, content);
|
|
console.log(JSON.stringify({ success: true, id }));
|
|
break;
|
|
}
|
|
|
|
case 'recall': {
|
|
const query = args.slice(1).join(' ');
|
|
const results = await intel.recall(query, 5);
|
|
console.log(JSON.stringify({
|
|
query,
|
|
results: results.map(r => ({
|
|
type: r.type,
|
|
content: r.content?.slice(0, 200),
|
|
score: r.score?.toFixed(3),
|
|
timestamp: r.metadata?.timestamp
|
|
}))
|
|
}, null, 2));
|
|
break;
|
|
}
|
|
|
|
case 'learn': {
|
|
const [, state, action, rewardStr] = args;
|
|
const reward = parseFloat(rewardStr) || 0;
|
|
const id = intel.learn(state, action, 'recorded', reward);
|
|
console.log(JSON.stringify({ success: true, id, state, action, reward }));
|
|
break;
|
|
}
|
|
|
|
case 'suggest': {
|
|
const [, state, actionsStr] = args;
|
|
const actions = actionsStr?.split(',') || ['coder', 'reviewer', 'tester'];
|
|
const suggestion = intel.suggest(state, actions);
|
|
console.log(JSON.stringify(suggestion, null, 2));
|
|
break;
|
|
}
|
|
|
|
case 'route': {
|
|
const task = [];
|
|
let file = null, crate = null, operation = 'edit';
|
|
|
|
for (let i = 1; i < args.length; i++) {
|
|
if (args[i] === '--file' && args[i + 1]) {
|
|
file = args[++i];
|
|
} else if (args[i] === '--crate' && args[i + 1]) {
|
|
crate = args[++i];
|
|
} else if (args[i] === '--op' && args[i + 1]) {
|
|
operation = args[++i];
|
|
} else {
|
|
task.push(args[i]);
|
|
}
|
|
}
|
|
|
|
const fileType = file ? extname(file).slice(1) : 'unknown';
|
|
const routing = await intel.route(task.join(' '), { file, fileType, crate, operation });
|
|
console.log(JSON.stringify(routing, null, 2));
|
|
break;
|
|
}
|
|
|
|
case 'stats': {
|
|
const stats = intel.stats();
|
|
console.log(JSON.stringify(stats, null, 2));
|
|
break;
|
|
}
|
|
|
|
// === HOOK INTEGRATIONS ===
|
|
|
|
case 'pre-edit': {
|
|
const file = args[1];
|
|
if (!file) {
|
|
console.log('{}');
|
|
break;
|
|
}
|
|
|
|
const fileType = extname(file).slice(1);
|
|
const fileName = basename(file);
|
|
const crateMatch = file.match(/crates\/([^/]+)/);
|
|
const crate = crateMatch ? crateMatch[1] : null;
|
|
|
|
// Build context for routing - use underscore format to match pretrained Q-table
|
|
const state = `edit_${fileType}_in_${crate || 'project'}`;
|
|
|
|
// Get routing suggestion
|
|
const routing = await intel.route(
|
|
`edit ${fileName}`,
|
|
{ file, fileType, crate, operation: 'edit' }
|
|
);
|
|
|
|
// Recall similar past edits
|
|
const similar = await intel.recall(`edit ${fileType} ${crate || ''} ${fileName}`, 3);
|
|
|
|
// Get learned suggestion
|
|
const actions = ['check-first', 'edit-directly', 'test-first', 'review-first'];
|
|
const suggestion = intel.suggest(state, actions);
|
|
|
|
const output = {
|
|
file,
|
|
fileType,
|
|
crate,
|
|
routing: {
|
|
agent: routing.recommended,
|
|
confidence: routing.confidence,
|
|
reason: routing.reasoning
|
|
},
|
|
suggestion: {
|
|
approach: suggestion.action,
|
|
confidence: suggestion.confidence
|
|
},
|
|
context: similar.length > 0 ? {
|
|
similarEdits: similar.slice(0, 2).map(s => ({
|
|
what: s.content?.slice(0, 80),
|
|
when: s.metadata?.timestamp
|
|
}))
|
|
} : null
|
|
};
|
|
|
|
// Pretty output for hook
|
|
console.log('🧠 Intelligence Analysis:');
|
|
console.log(` 📁 ${crate || 'project'}/${fileName}`);
|
|
console.log(` 🤖 Recommended: ${routing.recommended} (${(routing.confidence * 100).toFixed(0)}% confidence)`);
|
|
if (suggestion.confidence > 0) {
|
|
console.log(` 💡 Approach: ${suggestion.action}`);
|
|
}
|
|
if (similar.length > 0) {
|
|
console.log(` 📚 ${similar.length} similar past edits found`);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case 'post-edit': {
|
|
const [, file, successStr] = args;
|
|
const success = successStr === 'true' || successStr === '1';
|
|
const reward = success ? 1.0 : -0.5;
|
|
|
|
const fileType = extname(file || '').slice(1);
|
|
const crateMatch = (file || '').match(/crates\/([^/]+)/);
|
|
const crate = crateMatch ? crateMatch[1] : null;
|
|
|
|
const state = `edit_${fileType}_in_${crate || 'project'}`;
|
|
const action = success ? 'successful-edit' : 'failed-edit';
|
|
|
|
// Record trajectory for learning
|
|
intel.learn(state, action, success ? 'completed' : 'failed', reward);
|
|
|
|
// v3: Record file edit for sequence learning
|
|
intel.recordFileEdit(file);
|
|
|
|
// Store in memory
|
|
await intel.remember(
|
|
'edit',
|
|
`${success ? 'successful' : 'failed'} edit of ${fileType} in ${crate || 'project'}`,
|
|
{ file, success, crate }
|
|
);
|
|
|
|
// v3: Check if tests should be suggested
|
|
const testSuggestion = intel.shouldSuggestTests(file);
|
|
|
|
console.log(`📊 Learning recorded: ${success ? '✅' : '❌'} ${basename(file || 'unknown')}`);
|
|
|
|
// v3: Suggest next files
|
|
const nextFiles = intel.suggestNextFiles(file, 2);
|
|
if (nextFiles.length > 0) {
|
|
console.log(` 📁 Often edit next: ${nextFiles.map(f => f.file.split('/').pop()).join(', ')}`);
|
|
}
|
|
|
|
// v3: Suggest running tests
|
|
if (testSuggestion.suggest) {
|
|
console.log(` 🧪 Consider: ${testSuggestion.command}`);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 'pre-command': {
|
|
const cmd = args.slice(1).join(' ');
|
|
|
|
// Classify command type
|
|
let cmdType = 'other';
|
|
if (cmd.startsWith('cargo')) cmdType = 'cargo';
|
|
else if (cmd.startsWith('npm')) cmdType = 'npm';
|
|
else if (cmd.startsWith('git')) cmdType = 'git';
|
|
else if (cmd.startsWith('wasm-pack')) cmdType = 'wasm';
|
|
|
|
const state = `${cmdType}_in_general`;
|
|
const actions = ['command-succeeded', 'command-failed'];
|
|
const suggestion = intel.suggest(state, actions);
|
|
|
|
// Recall similar commands
|
|
const similar = await intel.recall(`command ${cmdType} ${cmd.slice(0, 50)}`, 2);
|
|
|
|
console.log(`🧠 Command: ${cmdType}`);
|
|
if (suggestion.confidence > 0.3) {
|
|
console.log(` 💡 Suggestion: ${suggestion.action}`);
|
|
}
|
|
if (similar.length > 0 && similar[0].score > 0.6) {
|
|
const lastOutcome = similar[0].metadata?.success ? '✅' : '❌';
|
|
console.log(` 📚 Similar command ran before: ${lastOutcome}`);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 'post-command': {
|
|
// Parse: post-command <cmd> <success> [stderr]
|
|
// Find success flag (true/false/1/0) in args
|
|
let successIdx = args.findIndex((a, i) => i > 0 && (a === 'true' || a === 'false' || a === '1' || a === '0'));
|
|
if (successIdx === -1) successIdx = args.length - 1;
|
|
|
|
const success = args[successIdx] === 'true' || args[successIdx] === '1';
|
|
const cmd = args.slice(1, successIdx).join(' ');
|
|
const stderr = args.slice(successIdx + 1).join(' ');
|
|
|
|
let cmdType = 'other';
|
|
if (cmd.startsWith('cargo')) cmdType = 'cargo';
|
|
else if (cmd.startsWith('npm')) cmdType = 'npm';
|
|
else if (cmd.startsWith('git')) cmdType = 'git';
|
|
else if (cmd.startsWith('wasm-pack')) cmdType = 'wasm';
|
|
|
|
const state = `${cmdType}_in_general`;
|
|
const reward = success ? 1.0 : -0.5;
|
|
|
|
intel.learn(state, success ? 'command-succeeded' : 'command-failed', cmd.slice(0, 100), reward);
|
|
|
|
// v3: Record error patterns if command failed
|
|
if (!success && stderr) {
|
|
const crateMatch = cmd.match(/-p\s+(\S+)/) || cmd.match(/crates\/([^/\s]+)/);
|
|
const crate = crateMatch ? crateMatch[1] : null;
|
|
const errors = intel.recordError(cmd, stderr, null, crate);
|
|
if (errors.length > 0) {
|
|
console.log(`📊 Command ❌ recorded (${errors.length} error patterns learned)`);
|
|
for (const e of errors.slice(0, 2)) {
|
|
const fix = intel.suggestFix(`${e.type}:${e.code}`);
|
|
if (fix.recentFixes.length > 0) {
|
|
console.log(` 💡 ${e.code}: ${fix.recentFixes[0]}`);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
await intel.remember(
|
|
'command',
|
|
`${cmdType}: ${cmd.slice(0, 100)}`,
|
|
{ success, cmdType }
|
|
);
|
|
|
|
console.log(`📊 Command ${success ? '✅' : '❌'} recorded`);
|
|
break;
|
|
}
|
|
|
|
// === SWARM / HIVE-MIND COMMANDS ===
|
|
|
|
case 'swarm-register': {
|
|
const [, id, type, ...caps] = args;
|
|
const result = swarm.registerAgent(id, type, caps);
|
|
console.log(JSON.stringify(result, null, 2));
|
|
break;
|
|
}
|
|
|
|
case 'swarm-coordinate': {
|
|
const [, src, dst, weight] = args;
|
|
const result = swarm.recordCoordination(src, dst, parseFloat(weight) || 1);
|
|
console.log(JSON.stringify(result, null, 2));
|
|
break;
|
|
}
|
|
|
|
case 'swarm-optimize': {
|
|
const tasks = args.slice(1);
|
|
const result = swarm.optimizeTaskDistribution(tasks);
|
|
console.log(JSON.stringify(result, null, 2));
|
|
break;
|
|
}
|
|
|
|
case 'swarm-recommend': {
|
|
const [, taskType, ...caps] = args;
|
|
const result = swarm.recommendForTask(taskType, caps);
|
|
console.log(JSON.stringify(result, null, 2));
|
|
break;
|
|
}
|
|
|
|
case 'swarm-heal': {
|
|
const [, agentId] = args;
|
|
const result = swarm.handleFailure(agentId);
|
|
console.log(JSON.stringify(result, null, 2));
|
|
break;
|
|
}
|
|
|
|
case 'swarm-stats': {
|
|
const stats = swarm.getStats();
|
|
console.log(JSON.stringify(stats, null, 2));
|
|
break;
|
|
}
|
|
|
|
// === V3 FEATURES ===
|
|
|
|
case 'record-error': {
|
|
const cmd = args[1] || '';
|
|
const stderr = args.slice(2).join(' ');
|
|
const errors = intel.recordError(cmd, stderr);
|
|
console.log(JSON.stringify({ recorded: errors.length, errors }, null, 2));
|
|
break;
|
|
}
|
|
|
|
case 'suggest-fix': {
|
|
const errorCode = args[1];
|
|
const suggestion = intel.suggestFix(errorCode);
|
|
console.log(JSON.stringify(suggestion, null, 2));
|
|
break;
|
|
}
|
|
|
|
case 'suggest-next': {
|
|
const file = args[1];
|
|
const suggestions = intel.suggestNextFiles(file);
|
|
console.log(JSON.stringify(suggestions, null, 2));
|
|
break;
|
|
}
|
|
|
|
case 'should-test': {
|
|
const file = args[1];
|
|
const suggestion = intel.shouldSuggestTests(file);
|
|
console.log(JSON.stringify(suggestion, null, 2));
|
|
break;
|
|
}
|
|
|
|
default:
|
|
console.error(`Unknown command: ${command}`);
|
|
process.exit(1);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error:', error.message);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
main();
|