ruvector 0.1.63 → 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 +833 -24
- package/bin/mcp-server.js +357 -0
- package/dist/core/ast-parser.d.ts +108 -0
- package/dist/core/ast-parser.d.ts.map +1 -0
- package/dist/core/ast-parser.js +602 -0
- package/dist/core/cluster-wrapper.d.ts +148 -0
- package/dist/core/cluster-wrapper.d.ts.map +1 -0
- package/dist/core/cluster-wrapper.js +271 -0
- package/dist/core/coverage-router.d.ts +88 -0
- package/dist/core/coverage-router.d.ts.map +1 -0
- package/dist/core/coverage-router.js +315 -0
- package/dist/core/diff-embeddings.d.ts +93 -0
- package/dist/core/diff-embeddings.d.ts.map +1 -0
- package/dist/core/diff-embeddings.js +334 -0
- package/dist/core/graph-algorithms.d.ts +83 -0
- package/dist/core/graph-algorithms.d.ts.map +1 -0
- package/dist/core/graph-algorithms.js +514 -0
- package/dist/core/graph-wrapper.d.ts +147 -0
- package/dist/core/graph-wrapper.d.ts.map +1 -0
- package/dist/core/graph-wrapper.js +299 -0
- package/dist/core/index.d.ts +12 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +19 -1
- package/dist/core/router-wrapper.d.ts +62 -0
- package/dist/core/router-wrapper.d.ts.map +1 -0
- package/dist/core/router-wrapper.js +209 -0
- package/package.json +1 -1
- package/ruvector.db +0 -0
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
|
|
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,
|
|
2866
|
-
| **PostToolUse** | After Edit/Write/Bash | Q-learning update,
|
|
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 |
|
|
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
|
-
|
|
2901
|
-
|
|
2902
|
-
npx ruvector hooks
|
|
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
|
|
2908
|
-
2. **Post-edit hooks**
|
|
2909
|
-
3. **
|
|
2910
|
-
4. **
|
|
2911
|
-
5. **
|
|
2912
|
-
6. **
|
|
2913
|
-
7. **
|
|
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**:
|
|
2920
|
-
- **Trajectories**:
|
|
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 --
|
|
2929
|
-
npx ruvector hooks init --
|
|
2930
|
-
npx ruvector hooks init --
|
|
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
|
|