ruvector 0.1.64 → 0.1.65

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/bin/cli.js CHANGED
@@ -2844,6 +2844,14 @@ fi
2844
2844
  }]
2845
2845
  }];
2846
2846
  console.log(chalk.blue(' ✓ Advanced hooks (UserPromptSubmit, PreCompact, Notification)'));
2847
+
2848
+ // Extended environment variables for new capabilities
2849
+ settings.env.RUVECTOR_AST_ENABLED = settings.env.RUVECTOR_AST_ENABLED || 'true';
2850
+ settings.env.RUVECTOR_DIFF_EMBEDDINGS = settings.env.RUVECTOR_DIFF_EMBEDDINGS || 'true';
2851
+ settings.env.RUVECTOR_COVERAGE_ROUTING = settings.env.RUVECTOR_COVERAGE_ROUTING || 'true';
2852
+ settings.env.RUVECTOR_GRAPH_ALGORITHMS = settings.env.RUVECTOR_GRAPH_ALGORITHMS || 'true';
2853
+ settings.env.RUVECTOR_SECURITY_SCAN = settings.env.RUVECTOR_SECURITY_SCAN || 'true';
2854
+ console.log(chalk.blue(' ✓ Extended capabilities (AST, Diff, Coverage, Graph, Security)'));
2847
2855
  }
2848
2856
 
2849
2857
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
@@ -2854,19 +2862,27 @@ fi
2854
2862
  if (opts.claudeMd !== false && (!fs.existsSync(claudeMdPath) || opts.force)) {
2855
2863
  const claudeMdContent = `# Claude Code Project Configuration
2856
2864
 
2857
- ## RuVector Self-Learning Intelligence
2865
+ ## RuVector Self-Learning Intelligence v2.0
2858
2866
 
2859
- This project uses RuVector's self-learning intelligence hooks for enhanced AI-assisted development with Q-learning, vector memory, and automatic agent routing.
2867
+ This project uses RuVector's self-learning intelligence hooks with advanced capabilities:
2868
+ - **Q-learning** for agent routing optimization
2869
+ - **Vector memory** with HNSW indexing (150x faster search)
2870
+ - **AST parsing** for code complexity analysis
2871
+ - **Diff embeddings** for change classification and risk scoring
2872
+ - **Coverage routing** for test-aware agent selection
2873
+ - **Graph algorithms** for code structure analysis
2874
+ - **Security scanning** for vulnerability detection
2875
+ - **10 attention mechanisms** including hyperbolic and graph attention
2860
2876
 
2861
2877
  ### Active Hooks
2862
2878
 
2863
2879
  | Hook | Trigger | Purpose |
2864
2880
  |------|---------|---------|
2865
- | **PreToolUse** | Before Edit/Write/Bash | Agent routing, file analysis, command risk assessment |
2866
- | **PostToolUse** | After Edit/Write/Bash | Q-learning update, pattern recording, outcome tracking |
2881
+ | **PreToolUse** | Before Edit/Write/Bash | Agent routing, AST analysis, command risk assessment |
2882
+ | **PostToolUse** | After Edit/Write/Bash | Q-learning update, diff embeddings, outcome tracking |
2867
2883
  | **SessionStart** | Conversation begins | Load intelligence state, display learning stats |
2868
2884
  | **Stop** | Conversation ends | Save learning data, export metrics |
2869
- | **UserPromptSubmit** | User sends message | Context suggestions, pattern recommendations |
2885
+ | **UserPromptSubmit** | User sends message | RAG context suggestions, pattern recommendations |
2870
2886
  | **PreCompact** | Before context compaction | Preserve important context and memories |
2871
2887
  | **Notification** | Any notification | Track events for learning |
2872
2888
 
@@ -2878,8 +2894,13 @@ This project uses RuVector's self-learning intelligence hooks for enhanced AI-as
2878
2894
  | \`RUVECTOR_LEARNING_RATE\` | \`0.1\` | Q-learning rate (0.0-1.0) |
2879
2895
  | \`RUVECTOR_MEMORY_BACKEND\` | \`rvlite\` | Memory storage backend |
2880
2896
  | \`INTELLIGENCE_MODE\` | \`treatment\` | A/B testing mode (treatment/control) |
2897
+ | \`RUVECTOR_AST_ENABLED\` | \`true\` | Enable AST parsing and complexity analysis |
2898
+ | \`RUVECTOR_DIFF_EMBEDDINGS\` | \`true\` | Enable diff embeddings and risk scoring |
2899
+ | \`RUVECTOR_COVERAGE_ROUTING\` | \`true\` | Enable test coverage-aware routing |
2900
+ | \`RUVECTOR_GRAPH_ALGORITHMS\` | \`true\` | Enable graph algorithms (MinCut, Louvain) |
2901
+ | \`RUVECTOR_SECURITY_SCAN\` | \`true\` | Enable security vulnerability scanning |
2881
2902
 
2882
- ### Commands
2903
+ ### Core Commands
2883
2904
 
2884
2905
  \`\`\`bash
2885
2906
  # Initialize hooks in a project
@@ -2891,48 +2912,146 @@ npx ruvector hooks stats
2891
2912
  # Route a task to best agent
2892
2913
  npx ruvector hooks route "implement feature X"
2893
2914
 
2915
+ # Enhanced routing with AST/coverage/diff signals
2916
+ npx ruvector hooks route-enhanced "fix bug" --file src/api.ts
2917
+
2894
2918
  # Store context in vector memory
2895
2919
  npx ruvector hooks remember "important context" -t project
2896
2920
 
2897
2921
  # Recall from memory (semantic search)
2898
2922
  npx ruvector hooks recall "context query"
2923
+ \`\`\`
2924
+
2925
+ ### AST Analysis Commands
2926
+
2927
+ \`\`\`bash
2928
+ # Analyze file structure, symbols, imports, complexity
2929
+ npx ruvector hooks ast-analyze src/index.ts
2930
+
2931
+ # Get complexity metrics for multiple files
2932
+ npx ruvector hooks ast-complexity src/*.ts --threshold 15
2933
+ \`\`\`
2934
+
2935
+ ### Diff & Risk Analysis Commands
2936
+
2937
+ \`\`\`bash
2938
+ # Analyze commit with semantic embeddings and risk scoring
2939
+ npx ruvector hooks diff-analyze HEAD
2940
+
2941
+ # Classify change type (feature, bugfix, refactor, etc.)
2942
+ npx ruvector hooks diff-classify
2943
+
2944
+ # Find similar past commits
2945
+ npx ruvector hooks diff-similar -k 5
2946
+
2947
+ # Get risk score only
2948
+ npx ruvector hooks diff-analyze --risk-only
2949
+ \`\`\`
2950
+
2951
+ ### Coverage & Testing Commands
2899
2952
 
2900
- # Manual session management
2901
- npx ruvector hooks session-start
2902
- npx ruvector hooks session-end
2953
+ \`\`\`bash
2954
+ # Get coverage-aware routing for a file
2955
+ npx ruvector hooks coverage-route src/api.ts
2956
+
2957
+ # Suggest tests for files based on coverage
2958
+ npx ruvector hooks coverage-suggest src/*.ts
2959
+ \`\`\`
2960
+
2961
+ ### Graph Analysis Commands
2962
+
2963
+ \`\`\`bash
2964
+ # Find optimal code boundaries (MinCut algorithm)
2965
+ npx ruvector hooks graph-mincut src/*.ts
2966
+
2967
+ # Detect code communities (Louvain/Spectral clustering)
2968
+ npx ruvector hooks graph-cluster src/*.ts --method louvain
2969
+ \`\`\`
2970
+
2971
+ ### Security & RAG Commands
2972
+
2973
+ \`\`\`bash
2974
+ # Parallel security vulnerability scan
2975
+ npx ruvector hooks security-scan src/*.ts
2976
+
2977
+ # RAG-enhanced context retrieval
2978
+ npx ruvector hooks rag-context "how does auth work"
2979
+
2980
+ # Git churn analysis (hot spots)
2981
+ npx ruvector hooks git-churn --days 30
2903
2982
  \`\`\`
2904
2983
 
2984
+ ### MCP Tools (via Claude Code)
2985
+
2986
+ When using the RuVector MCP server, these tools are available:
2987
+
2988
+ | Tool | Description |
2989
+ |------|-------------|
2990
+ | \`hooks_stats\` | Get intelligence statistics |
2991
+ | \`hooks_route\` | Route task to best agent |
2992
+ | \`hooks_route_enhanced\` | Enhanced routing with AST/coverage signals |
2993
+ | \`hooks_remember\` / \`hooks_recall\` | Vector memory operations |
2994
+ | \`hooks_ast_analyze\` | Parse AST and extract symbols |
2995
+ | \`hooks_ast_complexity\` | Get complexity metrics |
2996
+ | \`hooks_diff_analyze\` | Analyze changes with embeddings |
2997
+ | \`hooks_diff_classify\` | Classify change types |
2998
+ | \`hooks_coverage_route\` | Coverage-aware routing |
2999
+ | \`hooks_coverage_suggest\` | Suggest needed tests |
3000
+ | \`hooks_graph_mincut\` | Find code boundaries |
3001
+ | \`hooks_graph_cluster\` | Detect communities |
3002
+ | \`hooks_security_scan\` | Security vulnerability scan |
3003
+ | \`hooks_rag_context\` | RAG context retrieval |
3004
+ | \`hooks_git_churn\` | Hot spot analysis |
3005
+ | \`hooks_attention_info\` | Available attention mechanisms |
3006
+ | \`hooks_gnn_info\` | GNN layer capabilities |
3007
+
3008
+ ### Attention Mechanisms
3009
+
3010
+ RuVector includes 10 attention mechanisms:
3011
+
3012
+ 1. **DotProductAttention** - Scaled dot-product attention
3013
+ 2. **MultiHeadAttention** - Parallel attention heads
3014
+ 3. **FlashAttention** - Memory-efficient tiled attention
3015
+ 4. **HyperbolicAttention** - Poincaré ball hyperbolic space
3016
+ 5. **LinearAttention** - O(n) linear complexity
3017
+ 6. **MoEAttention** - Mixture-of-Experts sparse attention
3018
+ 7. **GraphRoPeAttention** - Rotary position for graphs
3019
+ 8. **EdgeFeaturedAttention** - Edge-aware graph attention
3020
+ 9. **DualSpaceAttention** - Euclidean + Hyperbolic hybrid
3021
+ 10. **LocalGlobalAttention** - Sliding window + global tokens
3022
+
2905
3023
  ### How It Works
2906
3024
 
2907
- 1. **Pre-edit hooks** analyze files and suggest the best agent based on learned patterns
2908
- 2. **Post-edit hooks** record outcomes to improve future suggestions via Q-learning
2909
- 3. **Memory hooks** store and retrieve context using vector embeddings (cosine similarity)
2910
- 4. **Session hooks** manage learning state across conversations
2911
- 5. **UserPromptSubmit** provides context suggestions on each message
2912
- 6. **PreCompact** preserves critical context before conversation compaction
2913
- 7. **Notification** tracks all events for continuous learning
3025
+ 1. **Pre-edit hooks** analyze files via AST and suggest agents based on Q-learned patterns
3026
+ 2. **Post-edit hooks** generate diff embeddings to improve future routing
3027
+ 3. **Coverage routing** adjusts agent weights based on test coverage
3028
+ 4. **Graph algorithms** detect code communities for module boundaries
3029
+ 5. **Security scanning** identifies common vulnerability patterns
3030
+ 6. **RAG context** retrieves relevant memories using HNSW search
3031
+ 7. **Attention mechanisms** provide advanced embedding transformations
2914
3032
 
2915
3033
  ### Learning Data
2916
3034
 
2917
3035
  Stored in \`.ruvector/intelligence.json\`:
2918
3036
  - **Q-table patterns**: State-action values for agent routing
2919
- - **Vector memories**: Embeddings for semantic recall
2920
- - **Trajectories**: Learning history for improvement tracking
3037
+ - **Vector memories**: ONNX embeddings with HNSW indexing
3038
+ - **Trajectories**: SONA trajectory tracking for meta-learning
3039
+ - **Co-edit patterns**: File relationship graphs
2921
3040
  - **Error patterns**: Known issues and suggested fixes
3041
+ - **Diff embeddings**: Change classification patterns
2922
3042
 
2923
3043
  ### Init Options
2924
3044
 
2925
3045
  \`\`\`bash
2926
- npx ruvector hooks init # Full configuration
3046
+ npx ruvector hooks init # Full configuration with all capabilities
2927
3047
  npx ruvector hooks init --minimal # Basic hooks only
2928
- npx ruvector hooks init --no-env # Skip environment variables
2929
- npx ruvector hooks init --no-permissions # Skip permissions
2930
- npx ruvector hooks init --no-claude-md # Skip this file
2931
- npx ruvector hooks init --force # Overwrite existing
3048
+ npx ruvector hooks init --pretrain # Initialize + pretrain from git history
3049
+ npx ruvector hooks init --build-agents quality # Generate optimized agents
3050
+ npx ruvector hooks init --force # Overwrite existing configuration
2932
3051
  \`\`\`
2933
3052
 
2934
3053
  ---
2935
- *Powered by [RuVector](https://github.com/ruvnet/ruvector) self-learning intelligence*
3054
+ *Powered by [RuVector](https://github.com/ruvnet/ruvector) self-learning intelligence v2.0*
2936
3055
  `;
2937
3056
  fs.writeFileSync(claudeMdPath, claudeMdContent);
2938
3057
  console.log(chalk.green('✅ CLAUDE.md created in project root'));
@@ -3294,6 +3413,513 @@ hooksCmd.command('force-learn')
3294
3413
  console.log(JSON.stringify({ success: true, result: 'Learning cycle triggered', stats: intel.stats() }));
3295
3414
  });
3296
3415
 
3416
+ // ============================================
3417
+ // NEW CAPABILITY COMMANDS (AST, Diff, Coverage, Graph, Security, RAG)
3418
+ // ============================================
3419
+
3420
+ // Lazy load new modules
3421
+ let ASTParser, DiffEmbeddings, CoverageRouter, GraphAlgorithms, ExtendedWorkerPool;
3422
+ let newModulesLoaded = false;
3423
+
3424
+ function loadNewModules() {
3425
+ if (newModulesLoaded) return true;
3426
+ try {
3427
+ const core = require('../dist/core/index.js');
3428
+ // CodeParser is exported as both CodeParser and ASTParser
3429
+ ASTParser = core.CodeParser || core.ASTParser;
3430
+ DiffEmbeddings = core.default?.parseDiff ? core : require('../dist/core/diff-embeddings.js');
3431
+ CoverageRouter = core.default?.parseIstanbulCoverage ? core : require('../dist/core/coverage-router.js');
3432
+ GraphAlgorithms = core.default?.minCut ? core : require('../dist/core/graph-algorithms.js');
3433
+ ExtendedWorkerPool = core.ExtendedWorkerPool;
3434
+ newModulesLoaded = true;
3435
+ return true;
3436
+ } catch (e) {
3437
+ console.error('loadNewModules error:', e.message);
3438
+ return false;
3439
+ }
3440
+ }
3441
+
3442
+ // AST Analysis Commands
3443
+ hooksCmd.command('ast-analyze')
3444
+ .description('Parse file AST and extract symbols, imports, complexity')
3445
+ .argument('<file>', 'File path to analyze')
3446
+ .option('--json', 'Output as JSON')
3447
+ .option('--symbols', 'Show only symbols')
3448
+ .option('--imports', 'Show only imports')
3449
+ .action(async (file, opts) => {
3450
+ if (!loadNewModules() || !ASTParser) {
3451
+ console.log(JSON.stringify({ success: false, error: 'AST parser not available. Run npm run build.' }));
3452
+ return;
3453
+ }
3454
+ try {
3455
+ const parser = new ASTParser();
3456
+ // CodeParser uses analyze() which returns FileAnalysis
3457
+ const analysis = await parser.analyze(file);
3458
+
3459
+ // Get symbols list
3460
+ const symbols = await parser.getSymbols(file);
3461
+
3462
+ if (opts.json) {
3463
+ console.log(JSON.stringify({
3464
+ success: true,
3465
+ file,
3466
+ language: analysis.language,
3467
+ symbols: symbols.map(s => ({ name: s })),
3468
+ imports: analysis.imports,
3469
+ complexity: { cyclomatic: analysis.complexity, lines: analysis.lines },
3470
+ functions: analysis.functions.length,
3471
+ classes: analysis.classes.length
3472
+ }));
3473
+ } else if (opts.symbols) {
3474
+ console.log(chalk.bold.cyan(`\n📊 Symbols in ${path.basename(file)}:\n`));
3475
+ analysis.functions.forEach(f => console.log(` function: ${f.name} (line ${f.startLine})`));
3476
+ analysis.classes.forEach(c => console.log(` class: ${c.name} (line ${c.startLine})`));
3477
+ analysis.types.forEach(t => console.log(` type: ${t}`));
3478
+ } else if (opts.imports) {
3479
+ console.log(chalk.bold.cyan(`\n📦 Imports in ${path.basename(file)}:\n`));
3480
+ analysis.imports.forEach(i => console.log(` ${i.source} (${i.type})`));
3481
+ } else {
3482
+ console.log(chalk.bold.cyan(`\n📊 AST Analysis: ${path.basename(file)}\n`));
3483
+ console.log(` Language: ${analysis.language}`);
3484
+ console.log(` Functions: ${analysis.functions.length}`);
3485
+ console.log(` Classes: ${analysis.classes.length}`);
3486
+ console.log(` Imports: ${analysis.imports.length}`);
3487
+ console.log(` Complexity: ${analysis.complexity}`);
3488
+ console.log(` Lines: ${analysis.lines}`);
3489
+ console.log(` Parse time: ${analysis.parseTime.toFixed(2)}ms`);
3490
+ }
3491
+ } catch (e) {
3492
+ console.log(JSON.stringify({ success: false, error: e.message }));
3493
+ }
3494
+ });
3495
+
3496
+ hooksCmd.command('ast-complexity')
3497
+ .description('Get complexity metrics for files')
3498
+ .argument('<files...>', 'Files to analyze')
3499
+ .option('--threshold <n>', 'Warn if complexity exceeds threshold', '10')
3500
+ .action(async (files, opts) => {
3501
+ if (!loadNewModules() || !ASTParser) {
3502
+ console.log(JSON.stringify({ success: false, error: 'AST parser not available' }));
3503
+ return;
3504
+ }
3505
+ const parser = new ASTParser();
3506
+ const threshold = parseInt(opts.threshold);
3507
+ const results = [];
3508
+
3509
+ for (const file of files) {
3510
+ try {
3511
+ if (!fs.existsSync(file)) continue;
3512
+ const analysis = await parser.analyze(file);
3513
+ const warning = analysis.complexity > threshold;
3514
+ results.push({
3515
+ file,
3516
+ cyclomatic: analysis.complexity,
3517
+ lines: analysis.lines,
3518
+ functions: analysis.functions.length,
3519
+ classes: analysis.classes.length,
3520
+ warning
3521
+ });
3522
+ } catch (e) {
3523
+ results.push({ file, error: e.message });
3524
+ }
3525
+ }
3526
+
3527
+ console.log(JSON.stringify({ success: true, results, threshold }));
3528
+ });
3529
+
3530
+ // Diff Embedding Commands
3531
+ hooksCmd.command('diff-analyze')
3532
+ .description('Analyze git diff with semantic embeddings and risk scoring')
3533
+ .argument('[commit]', 'Commit hash (defaults to staged changes)')
3534
+ .option('--json', 'Output as JSON')
3535
+ .option('--risk-only', 'Show only risk score')
3536
+ .action(async (commit, opts) => {
3537
+ if (!loadNewModules()) {
3538
+ console.log(JSON.stringify({ success: false, error: 'Diff embeddings not available' }));
3539
+ return;
3540
+ }
3541
+ try {
3542
+ const diffMod = require('../dist/core/diff-embeddings.js');
3543
+ let analysis;
3544
+ if (commit) {
3545
+ analysis = await diffMod.analyzeCommit(commit);
3546
+ } else {
3547
+ const stagedDiff = diffMod.getStagedDiff();
3548
+ if (!stagedDiff) {
3549
+ console.log(JSON.stringify({ success: false, error: 'No staged changes' }));
3550
+ return;
3551
+ }
3552
+ const hunks = diffMod.parseDiff(stagedDiff);
3553
+ const files = [...new Set(hunks.map(h => h.file))];
3554
+ analysis = {
3555
+ hash: 'staged',
3556
+ message: 'Staged changes',
3557
+ files: await Promise.all(files.map(f => diffMod.analyzeFileDiff(f, stagedDiff))),
3558
+ totalAdditions: hunks.reduce((s, h) => s + h.additions.length, 0),
3559
+ totalDeletions: hunks.reduce((s, h) => s + h.deletions.length, 0),
3560
+ riskScore: 0
3561
+ };
3562
+ analysis.riskScore = analysis.files.reduce((s, f) => s + f.riskScore, 0) / Math.max(1, analysis.files.length);
3563
+ }
3564
+
3565
+ if (opts.json) {
3566
+ console.log(JSON.stringify({ success: true, ...analysis }));
3567
+ } else if (opts.riskOnly) {
3568
+ const risk = analysis.riskScore;
3569
+ const level = risk > 0.7 ? 'HIGH' : risk > 0.4 ? 'MEDIUM' : 'LOW';
3570
+ console.log(JSON.stringify({ success: true, riskScore: risk, riskLevel: level }));
3571
+ } else {
3572
+ console.log(chalk.bold.cyan(`\n📊 Diff Analysis: ${analysis.hash}\n`));
3573
+ console.log(` Message: ${analysis.message || 'N/A'}`);
3574
+ console.log(` Files: ${analysis.files.length}`);
3575
+ console.log(` Changes: +${analysis.totalAdditions} -${analysis.totalDeletions}`);
3576
+ const risk = analysis.riskScore;
3577
+ const riskColor = risk > 0.7 ? chalk.red : risk > 0.4 ? chalk.yellow : chalk.green;
3578
+ console.log(` Risk: ${riskColor((risk * 100).toFixed(0) + '%')}`);
3579
+ analysis.files.forEach(f => {
3580
+ console.log(` ${f.file}: ${f.category} (+${f.totalAdditions}/-${f.totalDeletions})`);
3581
+ });
3582
+ }
3583
+ } catch (e) {
3584
+ console.log(JSON.stringify({ success: false, error: e.message }));
3585
+ }
3586
+ });
3587
+
3588
+ hooksCmd.command('diff-classify')
3589
+ .description('Classify a change type (feature, bugfix, refactor, etc.)')
3590
+ .argument('[commit]', 'Commit hash')
3591
+ .action(async (commit) => {
3592
+ if (!loadNewModules()) {
3593
+ console.log(JSON.stringify({ success: false, error: 'Diff embeddings not available' }));
3594
+ return;
3595
+ }
3596
+ try {
3597
+ const diffMod = require('../dist/core/diff-embeddings.js');
3598
+ const analysis = await diffMod.analyzeCommit(commit || 'HEAD');
3599
+ const categories = {};
3600
+ analysis.files.forEach(f => {
3601
+ categories[f.category] = (categories[f.category] || 0) + 1;
3602
+ });
3603
+ const primary = Object.entries(categories).sort((a, b) => b[1] - a[1])[0];
3604
+ console.log(JSON.stringify({
3605
+ success: true,
3606
+ commit: analysis.hash,
3607
+ message: analysis.message,
3608
+ primaryCategory: primary ? primary[0] : 'unknown',
3609
+ categories
3610
+ }));
3611
+ } catch (e) {
3612
+ console.log(JSON.stringify({ success: false, error: e.message }));
3613
+ }
3614
+ });
3615
+
3616
+ hooksCmd.command('diff-similar')
3617
+ .description('Find similar past commits based on diff embeddings')
3618
+ .option('-k, --top-k <n>', 'Number of results', '5')
3619
+ .option('--commits <n>', 'How many recent commits to search', '50')
3620
+ .action(async (opts) => {
3621
+ if (!loadNewModules()) {
3622
+ console.log(JSON.stringify({ success: false, error: 'Diff embeddings not available' }));
3623
+ return;
3624
+ }
3625
+ try {
3626
+ const diffMod = require('../dist/core/diff-embeddings.js');
3627
+ const stagedDiff = diffMod.getStagedDiff() || diffMod.getUnstagedDiff();
3628
+ if (!stagedDiff) {
3629
+ console.log(JSON.stringify({ success: false, error: 'No current changes to compare' }));
3630
+ return;
3631
+ }
3632
+ const similar = await diffMod.findSimilarCommits(stagedDiff, parseInt(opts.commits), parseInt(opts.topK));
3633
+ console.log(JSON.stringify({ success: true, similar }));
3634
+ } catch (e) {
3635
+ console.log(JSON.stringify({ success: false, error: e.message }));
3636
+ }
3637
+ });
3638
+
3639
+ // Coverage Routing Commands
3640
+ hooksCmd.command('coverage-route')
3641
+ .description('Get coverage-aware agent routing for a file')
3642
+ .argument('<file>', 'File to analyze')
3643
+ .action((file) => {
3644
+ if (!loadNewModules()) {
3645
+ console.log(JSON.stringify({ success: false, error: 'Coverage router not available' }));
3646
+ return;
3647
+ }
3648
+ try {
3649
+ const covMod = require('../dist/core/coverage-router.js');
3650
+ const reportPath = covMod.findCoverageReport();
3651
+ const summary = reportPath ? covMod.parseIstanbulCoverage(reportPath) : null;
3652
+ const routing = covMod.shouldRouteToTester(file, summary);
3653
+ const weights = covMod.getCoverageRoutingWeight(file, summary);
3654
+ console.log(JSON.stringify({
3655
+ success: true,
3656
+ file,
3657
+ coverageReport: reportPath || 'not found',
3658
+ routeToTester: routing.route,
3659
+ reason: routing.reason,
3660
+ coverage: routing.coverage,
3661
+ agentWeights: weights
3662
+ }));
3663
+ } catch (e) {
3664
+ console.log(JSON.stringify({ success: false, error: e.message }));
3665
+ }
3666
+ });
3667
+
3668
+ hooksCmd.command('coverage-suggest')
3669
+ .description('Suggest tests for files based on coverage data')
3670
+ .argument('<files...>', 'Files to analyze')
3671
+ .action((files) => {
3672
+ if (!loadNewModules()) {
3673
+ console.log(JSON.stringify({ success: false, error: 'Coverage router not available' }));
3674
+ return;
3675
+ }
3676
+ try {
3677
+ const covMod = require('../dist/core/coverage-router.js');
3678
+ const suggestions = covMod.suggestTests(files);
3679
+ console.log(JSON.stringify({ success: true, suggestions }));
3680
+ } catch (e) {
3681
+ console.log(JSON.stringify({ success: false, error: e.message }));
3682
+ }
3683
+ });
3684
+
3685
+ // Graph Algorithm Commands
3686
+ hooksCmd.command('graph-mincut')
3687
+ .description('Find optimal code boundaries using MinCut algorithm')
3688
+ .argument('<files...>', 'Files to analyze')
3689
+ .option('--partitions <n>', 'Number of partitions', '2')
3690
+ .action(async (files, opts) => {
3691
+ if (!loadNewModules()) {
3692
+ console.log(JSON.stringify({ success: false, error: 'Graph algorithms not available' }));
3693
+ return;
3694
+ }
3695
+ try {
3696
+ const graphMod = require('../dist/core/graph-algorithms.js');
3697
+ // Build dependency graph from files
3698
+ const nodes = files.map(f => path.basename(f, path.extname(f)));
3699
+ const edges = [];
3700
+ // Simple edge detection based on imports
3701
+ for (const file of files) {
3702
+ if (!fs.existsSync(file)) continue;
3703
+ const content = fs.readFileSync(file, 'utf-8');
3704
+ const imports = content.match(/from ['"]\.\/([^'"]+)['"]/g) || [];
3705
+ imports.forEach(imp => {
3706
+ const target = imp.match(/from ['"]\.\/([^'"]+)['"]/)?.[1];
3707
+ if (target && nodes.includes(target)) {
3708
+ edges.push({ source: path.basename(file, path.extname(file)), target, weight: 1 });
3709
+ }
3710
+ });
3711
+ }
3712
+ const result = graphMod.minCut(nodes, edges);
3713
+ console.log(JSON.stringify({ success: true, nodes: nodes.length, edges: edges.length, ...result }));
3714
+ } catch (e) {
3715
+ console.log(JSON.stringify({ success: false, error: e.message }));
3716
+ }
3717
+ });
3718
+
3719
+ hooksCmd.command('graph-cluster')
3720
+ .description('Detect code communities using spectral/Louvain clustering')
3721
+ .argument('<files...>', 'Files to analyze')
3722
+ .option('--method <type>', 'Clustering method: spectral, louvain', 'louvain')
3723
+ .option('--clusters <n>', 'Number of clusters (spectral only)', '3')
3724
+ .action(async (files, opts) => {
3725
+ if (!loadNewModules()) {
3726
+ console.log(JSON.stringify({ success: false, error: 'Graph algorithms not available' }));
3727
+ return;
3728
+ }
3729
+ try {
3730
+ const graphMod = require('../dist/core/graph-algorithms.js');
3731
+ const nodes = files.map(f => path.basename(f, path.extname(f)));
3732
+ const edges = [];
3733
+ for (const file of files) {
3734
+ if (!fs.existsSync(file)) continue;
3735
+ const content = fs.readFileSync(file, 'utf-8');
3736
+ const imports = content.match(/from ['"]\.\/([^'"]+)['"]/g) || [];
3737
+ imports.forEach(imp => {
3738
+ const target = imp.match(/from ['"]\.\/([^'"]+)['"]/)?.[1];
3739
+ if (target && nodes.includes(target)) {
3740
+ edges.push({ source: path.basename(file, path.extname(file)), target, weight: 1 });
3741
+ }
3742
+ });
3743
+ }
3744
+ let result;
3745
+ if (opts.method === 'spectral') {
3746
+ result = graphMod.spectralClustering(nodes, edges, parseInt(opts.clusters));
3747
+ } else {
3748
+ result = graphMod.louvainCommunities(nodes, edges);
3749
+ }
3750
+ console.log(JSON.stringify({ success: true, method: opts.method, ...result }));
3751
+ } catch (e) {
3752
+ console.log(JSON.stringify({ success: false, error: e.message }));
3753
+ }
3754
+ });
3755
+
3756
+ // Security Scan Command
3757
+ hooksCmd.command('security-scan')
3758
+ .description('Parallel security vulnerability scan')
3759
+ .argument('<files...>', 'Files to scan')
3760
+ .option('--json', 'Output as JSON')
3761
+ .action(async (files, opts) => {
3762
+ if (!loadNewModules() || !ExtendedWorkerPool) {
3763
+ // Fallback to basic pattern matching
3764
+ const patterns = [
3765
+ { pattern: /eval\s*\(/g, severity: 'high', message: 'eval() usage detected' },
3766
+ { pattern: /innerHTML\s*=/g, severity: 'medium', message: 'innerHTML assignment (XSS risk)' },
3767
+ { pattern: /document\.write/g, severity: 'medium', message: 'document.write usage' },
3768
+ { pattern: /password\s*=\s*['"][^'"]+['"]/gi, severity: 'critical', message: 'Hardcoded password' },
3769
+ { pattern: /api[_-]?key\s*=\s*['"][^'"]+['"]/gi, severity: 'critical', message: 'Hardcoded API key' },
3770
+ { pattern: /exec\s*\(/g, severity: 'high', message: 'exec() usage (command injection risk)' },
3771
+ { pattern: /dangerouslySetInnerHTML/g, severity: 'medium', message: 'React dangerouslySetInnerHTML' },
3772
+ { pattern: /SELECT.*FROM.*WHERE.*\+/gi, severity: 'high', message: 'SQL injection risk' },
3773
+ ];
3774
+
3775
+ const findings = [];
3776
+ for (const file of files) {
3777
+ if (!fs.existsSync(file)) continue;
3778
+ try {
3779
+ const content = fs.readFileSync(file, 'utf-8');
3780
+ const lines = content.split('\n');
3781
+ patterns.forEach(p => {
3782
+ let match;
3783
+ lines.forEach((line, idx) => {
3784
+ if (p.pattern.test(line)) {
3785
+ findings.push({ file, line: idx + 1, severity: p.severity, message: p.message });
3786
+ }
3787
+ p.pattern.lastIndex = 0;
3788
+ });
3789
+ });
3790
+ } catch (e) {}
3791
+ }
3792
+ console.log(JSON.stringify({ success: true, findings, scanned: files.length }));
3793
+ return;
3794
+ }
3795
+ // Use parallel worker if available
3796
+ try {
3797
+ const pool = new ExtendedWorkerPool();
3798
+ const results = await pool.securityScan(files);
3799
+ console.log(JSON.stringify({ success: true, ...results }));
3800
+ } catch (e) {
3801
+ console.log(JSON.stringify({ success: false, error: e.message }));
3802
+ }
3803
+ });
3804
+
3805
+ // RAG Context Command
3806
+ hooksCmd.command('rag-context')
3807
+ .description('Get RAG-enhanced context for a query')
3808
+ .argument('<query...>', 'Query for context')
3809
+ .option('-k, --top-k <n>', 'Number of results', '5')
3810
+ .option('--rerank', 'Rerank results by relevance')
3811
+ .action(async (query, opts) => {
3812
+ const intel = new Intelligence();
3813
+ const queryStr = query.join(' ');
3814
+
3815
+ // Use async recall with engine (VectorDB + HNSW)
3816
+ const memories = await intel.recallAsync(queryStr, parseInt(opts.topK));
3817
+
3818
+ // Rerank if requested
3819
+ let results = memories;
3820
+ if (opts.rerank && ExtendedWorkerPool) {
3821
+ try {
3822
+ const pool = new ExtendedWorkerPool();
3823
+ results = await pool.rankContext(queryStr, memories.map(m => m.content || m));
3824
+ } catch (e) {}
3825
+ }
3826
+
3827
+ console.log(JSON.stringify({ success: true, query: queryStr, results }));
3828
+ });
3829
+
3830
+ // Git Churn Analysis Command
3831
+ hooksCmd.command('git-churn')
3832
+ .description('Analyze git churn to find hot spots')
3833
+ .option('--days <n>', 'Number of days to analyze', '30')
3834
+ .option('--top <n>', 'Top N files', '10')
3835
+ .action((opts) => {
3836
+ try {
3837
+ const { execSync } = require('child_process');
3838
+ const since = new Date(Date.now() - parseInt(opts.days) * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
3839
+ const log = execSync(`git log --since="${since}" --name-only --format="" 2>/dev/null`, { encoding: 'utf-8' });
3840
+ const files = log.trim().split('\n').filter(Boolean);
3841
+ const counts = {};
3842
+ files.forEach(f => { counts[f] = (counts[f] || 0) + 1; });
3843
+ const sorted = Object.entries(counts).sort((a, b) => b[1] - a[1]).slice(0, parseInt(opts.top));
3844
+ const hotSpots = sorted.map(([file, count]) => ({ file, changes: count }));
3845
+ console.log(JSON.stringify({ success: true, days: parseInt(opts.days), hotSpots }));
3846
+ } catch (e) {
3847
+ console.log(JSON.stringify({ success: false, error: e.message }));
3848
+ }
3849
+ });
3850
+
3851
+ // Enhanced route command that uses new capabilities
3852
+ hooksCmd.command('route-enhanced')
3853
+ .description('Enhanced routing using AST, coverage, and diff analysis')
3854
+ .argument('<task...>', 'Task description')
3855
+ .option('--file <file>', 'File context')
3856
+ .action(async (task, opts) => {
3857
+ const intel = new Intelligence();
3858
+ const taskStr = task.join(' ');
3859
+
3860
+ // Base routing
3861
+ const baseRoute = await intel.routeAsync(taskStr, opts.file, null, 'edit');
3862
+
3863
+ // Enhance with coverage if available
3864
+ let coverageWeight = null;
3865
+ if (opts.file && loadNewModules()) {
3866
+ try {
3867
+ const covMod = require('../dist/core/coverage-router.js');
3868
+ const reportPath = covMod.findCoverageReport();
3869
+ if (reportPath) {
3870
+ coverageWeight = covMod.getCoverageRoutingWeight(opts.file);
3871
+ }
3872
+ } catch (e) {}
3873
+ }
3874
+
3875
+ // Enhance with AST complexity if available
3876
+ let complexity = null;
3877
+ if (opts.file && loadNewModules() && ASTParser) {
3878
+ try {
3879
+ const parser = new ASTParser();
3880
+ const code = fs.readFileSync(opts.file, 'utf-8');
3881
+ const ext = path.extname(opts.file).slice(1);
3882
+ const result = parser.parse(code, ext);
3883
+ complexity = parser.calculateComplexity(result);
3884
+ } catch (e) {}
3885
+ }
3886
+
3887
+ // Adjust routing based on signals
3888
+ let finalAgent = baseRoute.agent;
3889
+ let adjustedConfidence = baseRoute.confidence;
3890
+ const signals = [];
3891
+
3892
+ if (coverageWeight && coverageWeight.tester > 0.4) {
3893
+ signals.push('low coverage detected');
3894
+ if (coverageWeight.tester > adjustedConfidence * 0.5) {
3895
+ finalAgent = 'tester';
3896
+ adjustedConfidence = coverageWeight.tester;
3897
+ }
3898
+ }
3899
+
3900
+ if (complexity && complexity.cyclomatic > 15) {
3901
+ signals.push('high complexity detected');
3902
+ if (finalAgent === 'coder') {
3903
+ finalAgent = 'reviewer';
3904
+ adjustedConfidence = Math.max(adjustedConfidence, 0.7);
3905
+ }
3906
+ }
3907
+
3908
+ console.log(JSON.stringify({
3909
+ success: true,
3910
+ agent: finalAgent,
3911
+ confidence: adjustedConfidence,
3912
+ reason: baseRoute.reason,
3913
+ signals,
3914
+ coverageWeight,
3915
+ complexity
3916
+ }));
3917
+ });
3918
+
3919
+ // ============================================
3920
+ // END NEW CAPABILITY COMMANDS
3921
+ // ============================================
3922
+
3297
3923
  // Verify hooks are working
3298
3924
  hooksCmd.command('verify')
3299
3925
  .description('Verify hooks are working correctly')
@@ -3841,9 +4467,187 @@ hooksCmd.command('pretrain')
3841
4467
  console.log(chalk.yellow(` ⚠ Directory analysis skipped: ${e.message}`));
3842
4468
  }
3843
4469
 
4470
+ // Phase 5: Analyze code complexity with AST
4471
+ console.log(chalk.yellow('\n📊 Phase 5: Analyzing code complexity via AST...\n'));
4472
+
4473
+ try {
4474
+ if (loadNewModules() && ASTParser) {
4475
+ const parser = new ASTParser();
4476
+ const codeFiles = (intel.data.fileList || []).filter(f =>
4477
+ ['.ts', '.js', '.tsx', '.jsx', '.py', '.rs', '.go'].includes(path.extname(f))
4478
+ ).slice(0, 50); // Analyze up to 50 files
4479
+
4480
+ let complexityStats = { high: 0, medium: 0, low: 0, total: 0 };
4481
+
4482
+ for (const file of codeFiles) {
4483
+ try {
4484
+ if (!fs.existsSync(file)) continue;
4485
+ const code = fs.readFileSync(file, 'utf-8');
4486
+ const ext = path.extname(file).slice(1);
4487
+ const lang = { ts: 'typescript', tsx: 'typescript', js: 'javascript', py: 'python', rs: 'rust', go: 'go' }[ext];
4488
+ if (!lang) continue;
4489
+
4490
+ const result = parser.parse(code, lang);
4491
+ const complexity = parser.calculateComplexity(result);
4492
+
4493
+ // Store complexity data
4494
+ intel.data.complexity = intel.data.complexity || {};
4495
+ intel.data.complexity[file] = complexity;
4496
+
4497
+ if (complexity.cyclomatic > 15) complexityStats.high++;
4498
+ else if (complexity.cyclomatic > 8) complexityStats.medium++;
4499
+ else complexityStats.low++;
4500
+ complexityStats.total++;
4501
+ } catch (e) { /* skip errors */ }
4502
+ }
4503
+
4504
+ stats.complexity = complexityStats;
4505
+ console.log(chalk.green(` ✓ Analyzed ${complexityStats.total} files`));
4506
+ console.log(chalk.green(` ✓ Complexity: ${complexityStats.high} high, ${complexityStats.medium} medium, ${complexityStats.low} low`));
4507
+ } else {
4508
+ console.log(chalk.dim(' ⏭️ AST parser not available, skipping'));
4509
+ }
4510
+ } catch (e) {
4511
+ console.log(chalk.yellow(` ⚠ Complexity analysis skipped: ${e.message}`));
4512
+ }
4513
+
4514
+ // Phase 6: Analyze diff patterns from recent commits
4515
+ console.log(chalk.yellow('\n🔄 Phase 6: Analyzing diff patterns for change classification...\n'));
4516
+
4517
+ try {
4518
+ const diffMod = require('../dist/core/diff-embeddings.js');
4519
+ const recentCommits = execSync(`git log --format="%H" -n 20 2>/dev/null`, { encoding: 'utf-8' }).trim().split('\n').filter(h => h);
4520
+
4521
+ let changeTypes = { feature: 0, bugfix: 0, refactor: 0, docs: 0, test: 0, config: 0, unknown: 0 };
4522
+
4523
+ for (const hash of recentCommits.slice(0, 10)) {
4524
+ try {
4525
+ const analysis = await diffMod.analyzeCommit(hash);
4526
+ analysis.files.forEach(f => {
4527
+ changeTypes[f.category] = (changeTypes[f.category] || 0) + 1;
4528
+ });
4529
+ } catch (e) { /* skip */ }
4530
+ }
4531
+
4532
+ intel.data.changePatterns = changeTypes;
4533
+ stats.changePatterns = changeTypes;
4534
+ console.log(chalk.green(` ✓ Analyzed ${recentCommits.length} commits`));
4535
+ console.log(chalk.green(` ✓ Change types: ${Object.entries(changeTypes).filter(([,v]) => v > 0).map(([k,v]) => `${k}:${v}`).join(', ')}`));
4536
+ } catch (e) {
4537
+ console.log(chalk.yellow(` ⚠ Diff analysis skipped: ${e.message}`));
4538
+ }
4539
+
4540
+ // Phase 7: Check test coverage if available
4541
+ console.log(chalk.yellow('\n🧪 Phase 7: Checking test coverage data...\n'));
4542
+
4543
+ try {
4544
+ const covMod = require('../dist/core/coverage-router.js');
4545
+ const reportPath = covMod.findCoverageReport();
4546
+
4547
+ if (reportPath) {
4548
+ const summary = covMod.parseIstanbulCoverage(reportPath);
4549
+ intel.data.coverage = {
4550
+ overall: summary.overall,
4551
+ lowCoverageFiles: summary.lowCoverageFiles.slice(0, 20),
4552
+ uncoveredFiles: summary.uncoveredFiles.slice(0, 10)
4553
+ };
4554
+ stats.coverage = summary.overall;
4555
+ console.log(chalk.green(` ✓ Found coverage report: ${reportPath}`));
4556
+ console.log(chalk.green(` ✓ Overall: Lines ${summary.overall.lines.toFixed(1)}%, Functions ${summary.overall.functions.toFixed(1)}%`));
4557
+ console.log(chalk.green(` ✓ ${summary.lowCoverageFiles.length} low-coverage files, ${summary.uncoveredFiles.length} uncovered`));
4558
+ } else {
4559
+ console.log(chalk.dim(' ⏭️ No coverage report found'));
4560
+ }
4561
+ } catch (e) {
4562
+ console.log(chalk.yellow(` ⚠ Coverage check skipped: ${e.message}`));
4563
+ }
4564
+
4565
+ // Phase 8: Detect available attention/GNN capabilities
4566
+ console.log(chalk.yellow('\n🧠 Phase 8: Detecting neural capabilities...\n'));
4567
+
4568
+ try {
4569
+ let capabilities = { attention: false, gnn: false, mechanisms: [] };
4570
+
4571
+ try {
4572
+ const attention = require('@ruvector/attention');
4573
+ capabilities.attention = true;
4574
+ capabilities.mechanisms = [
4575
+ 'DotProductAttention', 'MultiHeadAttention', 'FlashAttention',
4576
+ 'HyperbolicAttention', 'LinearAttention', 'MoEAttention',
4577
+ 'GraphRoPeAttention', 'DualSpaceAttention', 'LocalGlobalAttention'
4578
+ ];
4579
+ console.log(chalk.green(` ✓ Attention: 10 mechanisms available`));
4580
+ } catch (e) {
4581
+ console.log(chalk.dim(' ⏭️ @ruvector/attention not installed'));
4582
+ }
4583
+
4584
+ try {
4585
+ const gnn = require('@ruvector/gnn');
4586
+ capabilities.gnn = true;
4587
+ console.log(chalk.green(` ✓ GNN: RuvectorLayer, TensorCompress available`));
4588
+ } catch (e) {
4589
+ console.log(chalk.dim(' ⏭️ @ruvector/gnn not installed'));
4590
+ }
4591
+
4592
+ intel.data.neuralCapabilities = capabilities;
4593
+ stats.neural = capabilities;
4594
+ } catch (e) {
4595
+ console.log(chalk.yellow(` ⚠ Neural detection skipped: ${e.message}`));
4596
+ }
4597
+
4598
+ // Phase 9: Build code graph for community detection
4599
+ console.log(chalk.yellow('\n🔗 Phase 9: Building code relationship graph...\n'));
4600
+
4601
+ try {
4602
+ const graphMod = require('../dist/core/graph-algorithms.js');
4603
+ const codeFiles = execSync('git ls-files "*.ts" "*.js" 2>/dev/null || echo ""', { encoding: 'utf-8' }).trim().split('\n').filter(f => f);
4604
+
4605
+ if (codeFiles.length > 5 && codeFiles.length < 200) {
4606
+ const nodes = codeFiles.map(f => path.basename(f, path.extname(f)));
4607
+ const edges = [];
4608
+
4609
+ for (const file of codeFiles.slice(0, 100)) {
4610
+ try {
4611
+ if (!fs.existsSync(file)) continue;
4612
+ const content = fs.readFileSync(file, 'utf-8');
4613
+ const imports = content.match(/from ['"]\.\/([^'"]+)['"]/g) || [];
4614
+ imports.forEach(imp => {
4615
+ const target = imp.match(/from ['"]\.\/([^'"]+)['"]/)?.[1];
4616
+ if (target) {
4617
+ const targetBase = path.basename(target, path.extname(target));
4618
+ if (nodes.includes(targetBase)) {
4619
+ edges.push({ source: path.basename(file, path.extname(file)), target: targetBase, weight: 1 });
4620
+ }
4621
+ }
4622
+ });
4623
+ } catch (e) { /* skip */ }
4624
+ }
4625
+
4626
+ if (edges.length > 0) {
4627
+ const communities = graphMod.louvainCommunities(nodes, edges);
4628
+ intel.data.codeGraph = {
4629
+ nodes: nodes.length,
4630
+ edges: edges.length,
4631
+ communities: communities.numCommunities,
4632
+ modularity: communities.modularity
4633
+ };
4634
+ stats.graph = intel.data.codeGraph;
4635
+ console.log(chalk.green(` ✓ Built graph: ${nodes.length} nodes, ${edges.length} edges`));
4636
+ console.log(chalk.green(` ✓ Found ${communities.numCommunities} communities (modularity: ${communities.modularity.toFixed(3)})`));
4637
+ } else {
4638
+ console.log(chalk.dim(' ⏭️ Not enough import relationships found'));
4639
+ }
4640
+ } else {
4641
+ console.log(chalk.dim(` ⏭️ Skipped (${codeFiles.length} files - need 5-200)`));
4642
+ }
4643
+ } catch (e) {
4644
+ console.log(chalk.yellow(` ⚠ Graph analysis skipped: ${e.message}`));
4645
+ }
4646
+
3844
4647
  // Save all learning data
3845
4648
  intel.data.pretrained = {
3846
4649
  date: new Date().toISOString(),
4650
+ version: '2.0',
3847
4651
  stats: stats
3848
4652
  };
3849
4653
  intel.save();
@@ -3855,6 +4659,11 @@ hooksCmd.command('pretrain')
3855
4659
  console.log(` 🧠 ${stats.patterns} agent routing patterns`);
3856
4660
  console.log(` 🔗 ${stats.coedits} co-edit patterns`);
3857
4661
  console.log(` 💾 ${stats.memories} memory entries`);
4662
+ if (stats.complexity) console.log(` 📊 ${stats.complexity.total} files analyzed for complexity`);
4663
+ if (stats.changePatterns) console.log(` 🔄 Change patterns detected`);
4664
+ if (stats.coverage) console.log(` 🧪 Coverage: ${stats.coverage.lines.toFixed(1)}% lines`);
4665
+ if (stats.neural?.attention) console.log(` 🧠 10 attention mechanisms available`);
4666
+ if (stats.graph) console.log(` 🔗 ${stats.graph.communities} code communities detected`);
3858
4667
  console.log(chalk.dim('\nThe intelligence layer will now provide better recommendations.'));
3859
4668
  });
3860
4669