ruvector 0.1.45 → 0.1.46

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.
Files changed (2) hide show
  1. package/bin/cli.js +398 -24
  2. package/package.json +1 -1
package/bin/cli.js CHANGED
@@ -2267,6 +2267,40 @@ class Intelligence {
2267
2267
  // Hooks command group
2268
2268
  const hooksCmd = program.command('hooks').description('Self-learning intelligence hooks for Claude Code');
2269
2269
 
2270
+ // Helper: Detect project type
2271
+ function detectProjectType() {
2272
+ const cwd = process.cwd();
2273
+ const types = [];
2274
+ if (fs.existsSync(path.join(cwd, 'Cargo.toml'))) types.push('rust');
2275
+ if (fs.existsSync(path.join(cwd, 'package.json'))) types.push('node');
2276
+ if (fs.existsSync(path.join(cwd, 'requirements.txt')) || fs.existsSync(path.join(cwd, 'pyproject.toml'))) types.push('python');
2277
+ if (fs.existsSync(path.join(cwd, 'go.mod'))) types.push('go');
2278
+ if (fs.existsSync(path.join(cwd, 'Gemfile'))) types.push('ruby');
2279
+ if (fs.existsSync(path.join(cwd, 'pom.xml')) || fs.existsSync(path.join(cwd, 'build.gradle'))) types.push('java');
2280
+ return types.length > 0 ? types : ['generic'];
2281
+ }
2282
+
2283
+ // Helper: Get permissions for project type
2284
+ function getPermissionsForProjectType(types) {
2285
+ const basePermissions = [
2286
+ 'Bash(git status)', 'Bash(git diff:*)', 'Bash(git log:*)', 'Bash(git add:*)',
2287
+ 'Bash(git commit:*)', 'Bash(git push)', 'Bash(git branch:*)', 'Bash(git checkout:*)',
2288
+ 'Bash(ls:*)', 'Bash(pwd)', 'Bash(cat:*)', 'Bash(mkdir:*)', 'Bash(which:*)', 'Bash(ruvector:*)'
2289
+ ];
2290
+ const typePermissions = {
2291
+ rust: ['Bash(cargo:*)', 'Bash(rustc:*)', 'Bash(rustfmt:*)', 'Bash(clippy:*)', 'Bash(wasm-pack:*)'],
2292
+ node: ['Bash(npm:*)', 'Bash(npx:*)', 'Bash(node:*)', 'Bash(yarn:*)', 'Bash(pnpm:*)'],
2293
+ python: ['Bash(python:*)', 'Bash(pip:*)', 'Bash(pytest:*)', 'Bash(poetry:*)', 'Bash(uv:*)'],
2294
+ go: ['Bash(go:*)', 'Bash(gofmt:*)'],
2295
+ ruby: ['Bash(ruby:*)', 'Bash(gem:*)', 'Bash(bundle:*)', 'Bash(rails:*)'],
2296
+ java: ['Bash(mvn:*)', 'Bash(gradle:*)', 'Bash(java:*)', 'Bash(javac:*)'],
2297
+ generic: ['Bash(make:*)']
2298
+ };
2299
+ let perms = [...basePermissions];
2300
+ types.forEach(t => { if (typePermissions[t]) perms = perms.concat(typePermissions[t]); });
2301
+ return [...new Set(perms)];
2302
+ }
2303
+
2270
2304
  hooksCmd.command('init')
2271
2305
  .description('Initialize hooks in current project')
2272
2306
  .option('--force', 'Force overwrite existing settings')
@@ -2274,6 +2308,9 @@ hooksCmd.command('init')
2274
2308
  .option('--no-claude-md', 'Skip CLAUDE.md creation')
2275
2309
  .option('--no-permissions', 'Skip permissions configuration')
2276
2310
  .option('--no-env', 'Skip environment variables')
2311
+ .option('--no-gitignore', 'Skip .gitignore update')
2312
+ .option('--no-mcp', 'Skip MCP server configuration')
2313
+ .option('--no-statusline', 'Skip statusLine configuration')
2277
2314
  .action((opts) => {
2278
2315
  const settingsPath = path.join(process.cwd(), '.claude', 'settings.json');
2279
2316
  const settingsDir = path.dirname(settingsPath);
@@ -2294,6 +2331,10 @@ hooksCmd.command('init')
2294
2331
  if (settings.hooks.End) { delete settings.hooks.End; }
2295
2332
  }
2296
2333
 
2334
+ // Detect project type
2335
+ const projectTypes = detectProjectType();
2336
+ console.log(chalk.blue(` ✓ Detected project type(s): ${projectTypes.join(', ')}`));
2337
+
2297
2338
  // Environment variables for intelligence (unless --minimal or --no-env)
2298
2339
  if (!opts.minimal && opts.env !== false) {
2299
2340
  settings.env = settings.env || {};
@@ -2304,36 +2345,56 @@ hooksCmd.command('init')
2304
2345
  console.log(chalk.blue(' ✓ Environment variables configured'));
2305
2346
  }
2306
2347
 
2307
- // Permissions (unless --minimal or --no-permissions)
2348
+ // Permissions based on detected project type (unless --minimal or --no-permissions)
2308
2349
  if (!opts.minimal && opts.permissions !== false) {
2309
2350
  settings.permissions = settings.permissions || {};
2310
- settings.permissions.allow = settings.permissions.allow || [
2311
- 'Bash(npm run:*)',
2312
- 'Bash(npm test:*)',
2313
- 'Bash(npm install:*)',
2314
- 'Bash(npx:*)',
2315
- 'Bash(git status)',
2316
- 'Bash(git diff:*)',
2317
- 'Bash(git log:*)',
2318
- 'Bash(git add:*)',
2319
- 'Bash(git commit:*)',
2320
- 'Bash(git push)',
2321
- 'Bash(git branch:*)',
2322
- 'Bash(git checkout:*)',
2323
- 'Bash(ls:*)',
2324
- 'Bash(pwd)',
2325
- 'Bash(cat:*)',
2326
- 'Bash(mkdir:*)',
2327
- 'Bash(which:*)',
2328
- 'Bash(node:*)',
2329
- 'Bash(ruvector:*)'
2330
- ];
2351
+ settings.permissions.allow = settings.permissions.allow || getPermissionsForProjectType(projectTypes);
2331
2352
  settings.permissions.deny = settings.permissions.deny || [
2332
2353
  'Bash(rm -rf /)',
2333
2354
  'Bash(sudo rm:*)',
2334
- 'Bash(chmod 777:*)'
2355
+ 'Bash(chmod 777:*)',
2356
+ 'Bash(:(){ :|:& };:)'
2335
2357
  ];
2336
- console.log(chalk.blue(' ✓ Permissions configured'));
2358
+ console.log(chalk.blue(' ✓ Permissions configured (project-specific)'));
2359
+ }
2360
+
2361
+ // MCP server configuration (unless --minimal or --no-mcp)
2362
+ if (!opts.minimal && opts.mcp !== false) {
2363
+ settings.mcpServers = settings.mcpServers || {};
2364
+ // Only add if not already configured
2365
+ if (!settings.mcpServers['claude-flow'] && !settings.enabledMcpjsonServers?.includes('claude-flow')) {
2366
+ settings.enabledMcpjsonServers = settings.enabledMcpjsonServers || [];
2367
+ if (!settings.enabledMcpjsonServers.includes('claude-flow')) {
2368
+ settings.enabledMcpjsonServers.push('claude-flow');
2369
+ }
2370
+ }
2371
+ console.log(chalk.blue(' ✓ MCP servers configured'));
2372
+ }
2373
+
2374
+ // StatusLine configuration (unless --minimal or --no-statusline)
2375
+ if (!opts.minimal && opts.statusline !== false) {
2376
+ if (!settings.statusLine) {
2377
+ // Create a simple statusline script
2378
+ const statuslineScript = path.join(settingsDir, 'statusline.sh');
2379
+ const statuslineContent = `#!/bin/bash
2380
+ # RuVector Status Line - shows intelligence stats
2381
+ INTEL_FILE=".ruvector/intelligence.json"
2382
+ if [ -f "$INTEL_FILE" ]; then
2383
+ PATTERNS=$(jq -r '.patterns | length // 0' "$INTEL_FILE" 2>/dev/null || echo "0")
2384
+ MEMORIES=$(jq -r '.memories | length // 0' "$INTEL_FILE" 2>/dev/null || echo "0")
2385
+ echo "🧠 $PATTERNS patterns | 💾 $MEMORIES memories"
2386
+ else
2387
+ echo "🧠 RuVector"
2388
+ fi
2389
+ `;
2390
+ fs.writeFileSync(statuslineScript, statuslineContent);
2391
+ fs.chmodSync(statuslineScript, '755');
2392
+ settings.statusLine = {
2393
+ type: 'command',
2394
+ command: '.claude/statusline.sh'
2395
+ };
2396
+ console.log(chalk.blue(' ✓ StatusLine configured'));
2397
+ }
2337
2398
  }
2338
2399
 
2339
2400
  // Core hooks (always included)
@@ -2486,6 +2547,32 @@ npx ruvector hooks init --force # Overwrite existing
2486
2547
  } else if (fs.existsSync(claudeMdPath) && !opts.force) {
2487
2548
  console.log(chalk.yellow('ℹ️ CLAUDE.md already exists (use --force to overwrite)'));
2488
2549
  }
2550
+
2551
+ // Update .gitignore (unless --no-gitignore)
2552
+ if (opts.gitignore !== false) {
2553
+ const gitignorePath = path.join(process.cwd(), '.gitignore');
2554
+ const entriesToAdd = ['.ruvector/', '.claude/statusline.sh'];
2555
+ let gitignoreContent = '';
2556
+ if (fs.existsSync(gitignorePath)) {
2557
+ gitignoreContent = fs.readFileSync(gitignorePath, 'utf-8');
2558
+ }
2559
+ const linesToAdd = entriesToAdd.filter(entry => !gitignoreContent.includes(entry));
2560
+ if (linesToAdd.length > 0) {
2561
+ const newContent = gitignoreContent.trim() + '\n\n# RuVector intelligence data\n' + linesToAdd.join('\n') + '\n';
2562
+ fs.writeFileSync(gitignorePath, newContent);
2563
+ console.log(chalk.blue(' ✓ .gitignore updated'));
2564
+ }
2565
+ }
2566
+
2567
+ // Create .ruvector directory for intelligence data
2568
+ const ruvectorDir = path.join(process.cwd(), '.ruvector');
2569
+ if (!fs.existsSync(ruvectorDir)) {
2570
+ fs.mkdirSync(ruvectorDir, { recursive: true });
2571
+ console.log(chalk.blue(' ✓ .ruvector/ directory created'));
2572
+ }
2573
+
2574
+ console.log(chalk.green('\n✅ RuVector hooks initialization complete!'));
2575
+ console.log(chalk.dim(' Run `npx ruvector hooks verify` to test the setup'));
2489
2576
  });
2490
2577
 
2491
2578
  hooksCmd.command('stats').description('Show intelligence statistics').action(() => {
@@ -2622,4 +2709,291 @@ hooksCmd.command('track-notification').description('Track notification').action(
2622
2709
  console.log(JSON.stringify({ tracked: true }));
2623
2710
  });
2624
2711
 
2712
+ // Verify hooks are working
2713
+ hooksCmd.command('verify')
2714
+ .description('Verify hooks are working correctly')
2715
+ .option('--verbose', 'Show detailed output')
2716
+ .action((opts) => {
2717
+ console.log(chalk.bold.cyan('\n🔍 RuVector Hooks Verification\n'));
2718
+ const checks = [];
2719
+
2720
+ // Check 1: Settings file exists
2721
+ const settingsPath = path.join(process.cwd(), '.claude', 'settings.json');
2722
+ if (fs.existsSync(settingsPath)) {
2723
+ checks.push({ name: 'Settings file', status: 'pass', detail: '.claude/settings.json exists' });
2724
+ try {
2725
+ const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
2726
+ // Check hooks
2727
+ const requiredHooks = ['PreToolUse', 'PostToolUse', 'SessionStart', 'Stop'];
2728
+ const missingHooks = requiredHooks.filter(h => !settings.hooks?.[h]);
2729
+ if (missingHooks.length === 0) {
2730
+ checks.push({ name: 'Required hooks', status: 'pass', detail: 'All core hooks configured' });
2731
+ } else {
2732
+ checks.push({ name: 'Required hooks', status: 'fail', detail: `Missing: ${missingHooks.join(', ')}` });
2733
+ }
2734
+ // Check advanced hooks
2735
+ const advancedHooks = ['UserPromptSubmit', 'PreCompact', 'Notification'];
2736
+ const hasAdvanced = advancedHooks.filter(h => settings.hooks?.[h]);
2737
+ if (hasAdvanced.length > 0) {
2738
+ checks.push({ name: 'Advanced hooks', status: 'pass', detail: `${hasAdvanced.length}/3 configured` });
2739
+ } else {
2740
+ checks.push({ name: 'Advanced hooks', status: 'warn', detail: 'None configured (optional)' });
2741
+ }
2742
+ // Check env
2743
+ if (settings.env?.RUVECTOR_INTELLIGENCE_ENABLED) {
2744
+ checks.push({ name: 'Environment vars', status: 'pass', detail: 'Intelligence enabled' });
2745
+ } else {
2746
+ checks.push({ name: 'Environment vars', status: 'warn', detail: 'Not configured' });
2747
+ }
2748
+ // Check permissions
2749
+ if (settings.permissions?.allow?.length > 0) {
2750
+ checks.push({ name: 'Permissions', status: 'pass', detail: `${settings.permissions.allow.length} allowed patterns` });
2751
+ } else {
2752
+ checks.push({ name: 'Permissions', status: 'warn', detail: 'Not configured' });
2753
+ }
2754
+ } catch (e) {
2755
+ checks.push({ name: 'Settings parse', status: 'fail', detail: 'Invalid JSON' });
2756
+ }
2757
+ } else {
2758
+ checks.push({ name: 'Settings file', status: 'fail', detail: 'Run `npx ruvector hooks init` first' });
2759
+ }
2760
+
2761
+ // Check 2: .ruvector directory
2762
+ const ruvectorDir = path.join(process.cwd(), '.ruvector');
2763
+ if (fs.existsSync(ruvectorDir)) {
2764
+ checks.push({ name: 'Data directory', status: 'pass', detail: '.ruvector/ exists' });
2765
+ const intelFile = path.join(ruvectorDir, 'intelligence.json');
2766
+ if (fs.existsSync(intelFile)) {
2767
+ const stats = fs.statSync(intelFile);
2768
+ checks.push({ name: 'Intelligence file', status: 'pass', detail: `${(stats.size / 1024).toFixed(1)}KB` });
2769
+ } else {
2770
+ checks.push({ name: 'Intelligence file', status: 'warn', detail: 'Will be created on first use' });
2771
+ }
2772
+ } else {
2773
+ checks.push({ name: 'Data directory', status: 'warn', detail: 'Will be created on first use' });
2774
+ }
2775
+
2776
+ // Check 3: Hook command execution
2777
+ try {
2778
+ const { execSync } = require('child_process');
2779
+ execSync('npx ruvector hooks stats', { stdio: 'pipe', timeout: 5000 });
2780
+ checks.push({ name: 'Command execution', status: 'pass', detail: 'Hooks commands work' });
2781
+ } catch (e) {
2782
+ checks.push({ name: 'Command execution', status: 'fail', detail: 'Commands failed to execute' });
2783
+ }
2784
+
2785
+ // Display results
2786
+ let passCount = 0, warnCount = 0, failCount = 0;
2787
+ checks.forEach(c => {
2788
+ const icon = c.status === 'pass' ? chalk.green('✓') : c.status === 'warn' ? chalk.yellow('⚠') : chalk.red('✗');
2789
+ const statusColor = c.status === 'pass' ? chalk.green : c.status === 'warn' ? chalk.yellow : chalk.red;
2790
+ console.log(` ${icon} ${c.name}: ${statusColor(c.detail)}`);
2791
+ if (c.status === 'pass') passCount++;
2792
+ else if (c.status === 'warn') warnCount++;
2793
+ else failCount++;
2794
+ });
2795
+
2796
+ console.log('');
2797
+ if (failCount === 0) {
2798
+ console.log(chalk.green(`✅ Verification passed! ${passCount} checks passed, ${warnCount} warnings`));
2799
+ } else {
2800
+ console.log(chalk.red(`❌ Verification failed: ${failCount} issues found`));
2801
+ console.log(chalk.dim(' Run `npx ruvector hooks doctor` for detailed diagnostics'));
2802
+ }
2803
+ });
2804
+
2805
+ // Doctor - diagnose setup issues
2806
+ hooksCmd.command('doctor')
2807
+ .description('Diagnose and fix setup issues')
2808
+ .option('--fix', 'Automatically fix issues')
2809
+ .action((opts) => {
2810
+ console.log(chalk.bold.cyan('\n🩺 RuVector Hooks Doctor\n'));
2811
+ const issues = [];
2812
+ const fixes = [];
2813
+
2814
+ // Check settings file
2815
+ const settingsPath = path.join(process.cwd(), '.claude', 'settings.json');
2816
+ if (!fs.existsSync(settingsPath)) {
2817
+ issues.push({ severity: 'error', message: 'No .claude/settings.json found', fix: 'Run `npx ruvector hooks init`' });
2818
+ } else {
2819
+ try {
2820
+ const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
2821
+
2822
+ // Check for invalid schema
2823
+ if (settings.$schema && !settings.$schema.includes('schemastore.org')) {
2824
+ issues.push({ severity: 'warning', message: 'Invalid schema URL', fix: 'Will be corrected' });
2825
+ if (opts.fix) {
2826
+ settings.$schema = 'https://json.schemastore.org/claude-code-settings.json';
2827
+ fixes.push('Fixed schema URL');
2828
+ }
2829
+ }
2830
+
2831
+ // Check for old hook names
2832
+ if (settings.hooks?.Start || settings.hooks?.End) {
2833
+ issues.push({ severity: 'error', message: 'Invalid hook names (Start/End)', fix: 'Should be SessionStart/Stop' });
2834
+ if (opts.fix) {
2835
+ delete settings.hooks.Start;
2836
+ delete settings.hooks.End;
2837
+ fixes.push('Removed invalid hook names');
2838
+ }
2839
+ }
2840
+
2841
+ // Check hook format
2842
+ const hookNames = ['PreToolUse', 'PostToolUse'];
2843
+ hookNames.forEach(name => {
2844
+ if (settings.hooks?.[name]) {
2845
+ settings.hooks[name].forEach((hook, i) => {
2846
+ if (typeof hook.matcher === 'object') {
2847
+ issues.push({ severity: 'error', message: `${name}[${i}].matcher should be string, not object`, fix: 'Will be corrected' });
2848
+ }
2849
+ });
2850
+ }
2851
+ });
2852
+
2853
+ // Check for npx vs direct command
2854
+ const checkCommands = (hooks) => {
2855
+ if (!hooks) return;
2856
+ hooks.forEach(h => {
2857
+ h.hooks?.forEach(hh => {
2858
+ if (hh.command && hh.command.includes('ruvector') && !hh.command.startsWith('npx ') && !hh.command.includes('/bin/')) {
2859
+ issues.push({ severity: 'warning', message: `Command should use 'npx ruvector' for portability`, fix: 'Update to use npx' });
2860
+ }
2861
+ });
2862
+ });
2863
+ };
2864
+ Object.values(settings.hooks || {}).forEach(checkCommands);
2865
+
2866
+ // Save fixes
2867
+ if (opts.fix && fixes.length > 0) {
2868
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
2869
+ }
2870
+ } catch (e) {
2871
+ issues.push({ severity: 'error', message: 'Invalid JSON in settings file', fix: 'Re-run `npx ruvector hooks init --force`' });
2872
+ }
2873
+ }
2874
+
2875
+ // Check .gitignore
2876
+ const gitignorePath = path.join(process.cwd(), '.gitignore');
2877
+ if (fs.existsSync(gitignorePath)) {
2878
+ const content = fs.readFileSync(gitignorePath, 'utf-8');
2879
+ if (!content.includes('.ruvector/')) {
2880
+ issues.push({ severity: 'warning', message: '.ruvector/ not in .gitignore', fix: 'Add to prevent committing learning data' });
2881
+ if (opts.fix) {
2882
+ fs.appendFileSync(gitignorePath, '\n# RuVector intelligence data\n.ruvector/\n');
2883
+ fixes.push('Added .ruvector/ to .gitignore');
2884
+ }
2885
+ }
2886
+ }
2887
+
2888
+ // Display results
2889
+ if (issues.length === 0) {
2890
+ console.log(chalk.green(' ✓ No issues found! Your setup looks healthy.'));
2891
+ } else {
2892
+ issues.forEach(i => {
2893
+ const icon = i.severity === 'error' ? chalk.red('✗') : chalk.yellow('⚠');
2894
+ console.log(` ${icon} ${i.message}`);
2895
+ console.log(chalk.dim(` Fix: ${i.fix}`));
2896
+ });
2897
+
2898
+ if (opts.fix && fixes.length > 0) {
2899
+ console.log(chalk.green(`\n✅ Applied ${fixes.length} fix(es):`));
2900
+ fixes.forEach(f => console.log(chalk.green(` • ${f}`)));
2901
+ } else if (issues.some(i => i.severity === 'error')) {
2902
+ console.log(chalk.yellow('\n💡 Run with --fix to automatically fix issues'));
2903
+ }
2904
+ }
2905
+ });
2906
+
2907
+ // Export intelligence data
2908
+ hooksCmd.command('export')
2909
+ .description('Export intelligence data for backup')
2910
+ .option('-o, --output <file>', 'Output file path', 'ruvector-export.json')
2911
+ .option('--include-all', 'Include all data (patterns, memories, trajectories)')
2912
+ .action((opts) => {
2913
+ const intel = new Intelligence();
2914
+ const exportData = {
2915
+ version: '1.0',
2916
+ exported_at: new Date().toISOString(),
2917
+ patterns: intel.data?.patterns || {},
2918
+ memories: opts.includeAll ? (intel.data?.memories || []) : [],
2919
+ trajectories: opts.includeAll ? (intel.data?.trajectories || []) : [],
2920
+ errors: intel.data?.errors || {},
2921
+ stats: intel.stats()
2922
+ };
2923
+
2924
+ const outputPath = path.resolve(opts.output);
2925
+ fs.writeFileSync(outputPath, JSON.stringify(exportData, null, 2));
2926
+
2927
+ console.log(chalk.green(`✅ Exported intelligence data to ${outputPath}`));
2928
+ console.log(chalk.dim(` ${Object.keys(exportData.patterns).length} patterns`));
2929
+ console.log(chalk.dim(` ${exportData.memories.length} memories`));
2930
+ console.log(chalk.dim(` ${exportData.trajectories.length} trajectories`));
2931
+ });
2932
+
2933
+ // Import intelligence data
2934
+ hooksCmd.command('import')
2935
+ .description('Import intelligence data from backup')
2936
+ .argument('<file>', 'Import file path')
2937
+ .option('--merge', 'Merge with existing data (default: replace)')
2938
+ .option('--dry-run', 'Show what would be imported without making changes')
2939
+ .action((file, opts) => {
2940
+ const importPath = path.resolve(file);
2941
+ if (!fs.existsSync(importPath)) {
2942
+ console.error(chalk.red(`❌ File not found: ${importPath}`));
2943
+ process.exit(1);
2944
+ }
2945
+
2946
+ try {
2947
+ const importData = JSON.parse(fs.readFileSync(importPath, 'utf-8'));
2948
+
2949
+ if (!importData.version) {
2950
+ console.error(chalk.red('❌ Invalid export file (missing version)'));
2951
+ process.exit(1);
2952
+ }
2953
+
2954
+ console.log(chalk.cyan(`📦 Import file: ${file}`));
2955
+ console.log(chalk.dim(` Version: ${importData.version}`));
2956
+ console.log(chalk.dim(` Exported: ${importData.exported_at}`));
2957
+ console.log(chalk.dim(` Patterns: ${Object.keys(importData.patterns || {}).length}`));
2958
+ console.log(chalk.dim(` Memories: ${(importData.memories || []).length}`));
2959
+ console.log(chalk.dim(` Trajectories: ${(importData.trajectories || []).length}`));
2960
+
2961
+ if (opts.dryRun) {
2962
+ console.log(chalk.yellow('\n⚠️ Dry run - no changes made'));
2963
+ return;
2964
+ }
2965
+
2966
+ const intel = new Intelligence();
2967
+
2968
+ if (opts.merge) {
2969
+ // Merge patterns
2970
+ Object.assign(intel.data.patterns, importData.patterns || {});
2971
+ // Merge memories (deduplicate by content)
2972
+ const existingContent = new Set((intel.data.memories || []).map(m => m.content));
2973
+ (importData.memories || []).forEach(m => {
2974
+ if (!existingContent.has(m.content)) {
2975
+ intel.data.memories.push(m);
2976
+ }
2977
+ });
2978
+ // Merge trajectories
2979
+ intel.data.trajectories = (intel.data.trajectories || []).concat(importData.trajectories || []);
2980
+ // Merge errors
2981
+ Object.assign(intel.data.errors, importData.errors || {});
2982
+ console.log(chalk.green('✅ Merged intelligence data'));
2983
+ } else {
2984
+ intel.data.patterns = importData.patterns || {};
2985
+ intel.data.memories = importData.memories || [];
2986
+ intel.data.trajectories = importData.trajectories || [];
2987
+ intel.data.errors = importData.errors || {};
2988
+ console.log(chalk.green('✅ Replaced intelligence data'));
2989
+ }
2990
+
2991
+ intel.save();
2992
+ console.log(chalk.dim(' Data saved to .ruvector/intelligence.json'));
2993
+ } catch (e) {
2994
+ console.error(chalk.red(`❌ Failed to import: ${e.message}`));
2995
+ process.exit(1);
2996
+ }
2997
+ });
2998
+
2625
2999
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ruvector",
3
- "version": "0.1.45",
3
+ "version": "0.1.46",
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",