ruvector 0.1.45 → 0.1.47
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 +676 -24
- 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,57 @@ 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(mkfs:*)',
|
|
2357
|
+
'Bash(dd if=/dev/zero:*)'
|
|
2335
2358
|
];
|
|
2336
|
-
console.log(chalk.blue(' ✓ Permissions configured'));
|
|
2359
|
+
console.log(chalk.blue(' ✓ Permissions configured (project-specific)'));
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2362
|
+
// MCP server configuration (unless --minimal or --no-mcp)
|
|
2363
|
+
if (!opts.minimal && opts.mcp !== false) {
|
|
2364
|
+
settings.mcpServers = settings.mcpServers || {};
|
|
2365
|
+
// Only add if not already configured
|
|
2366
|
+
if (!settings.mcpServers['claude-flow'] && !settings.enabledMcpjsonServers?.includes('claude-flow')) {
|
|
2367
|
+
settings.enabledMcpjsonServers = settings.enabledMcpjsonServers || [];
|
|
2368
|
+
if (!settings.enabledMcpjsonServers.includes('claude-flow')) {
|
|
2369
|
+
settings.enabledMcpjsonServers.push('claude-flow');
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
console.log(chalk.blue(' ✓ MCP servers configured'));
|
|
2373
|
+
}
|
|
2374
|
+
|
|
2375
|
+
// StatusLine configuration (unless --minimal or --no-statusline)
|
|
2376
|
+
if (!opts.minimal && opts.statusline !== false) {
|
|
2377
|
+
if (!settings.statusLine) {
|
|
2378
|
+
// Create a simple statusline script
|
|
2379
|
+
const statuslineScript = path.join(settingsDir, 'statusline.sh');
|
|
2380
|
+
const statuslineContent = `#!/bin/bash
|
|
2381
|
+
# RuVector Status Line - shows intelligence stats
|
|
2382
|
+
INTEL_FILE=".ruvector/intelligence.json"
|
|
2383
|
+
if [ -f "$INTEL_FILE" ]; then
|
|
2384
|
+
PATTERNS=$(jq -r '.patterns | length // 0' "$INTEL_FILE" 2>/dev/null || echo "0")
|
|
2385
|
+
MEMORIES=$(jq -r '.memories | length // 0' "$INTEL_FILE" 2>/dev/null || echo "0")
|
|
2386
|
+
echo "🧠 $PATTERNS patterns | 💾 $MEMORIES memories"
|
|
2387
|
+
else
|
|
2388
|
+
echo "🧠 RuVector"
|
|
2389
|
+
fi
|
|
2390
|
+
`;
|
|
2391
|
+
fs.writeFileSync(statuslineScript, statuslineContent);
|
|
2392
|
+
fs.chmodSync(statuslineScript, '755');
|
|
2393
|
+
settings.statusLine = {
|
|
2394
|
+
type: 'command',
|
|
2395
|
+
command: '.claude/statusline.sh'
|
|
2396
|
+
};
|
|
2397
|
+
console.log(chalk.blue(' ✓ StatusLine configured'));
|
|
2398
|
+
}
|
|
2337
2399
|
}
|
|
2338
2400
|
|
|
2339
2401
|
// Core hooks (always included)
|
|
@@ -2486,6 +2548,32 @@ npx ruvector hooks init --force # Overwrite existing
|
|
|
2486
2548
|
} else if (fs.existsSync(claudeMdPath) && !opts.force) {
|
|
2487
2549
|
console.log(chalk.yellow('ℹ️ CLAUDE.md already exists (use --force to overwrite)'));
|
|
2488
2550
|
}
|
|
2551
|
+
|
|
2552
|
+
// Update .gitignore (unless --no-gitignore)
|
|
2553
|
+
if (opts.gitignore !== false) {
|
|
2554
|
+
const gitignorePath = path.join(process.cwd(), '.gitignore');
|
|
2555
|
+
const entriesToAdd = ['.ruvector/', '.claude/statusline.sh'];
|
|
2556
|
+
let gitignoreContent = '';
|
|
2557
|
+
if (fs.existsSync(gitignorePath)) {
|
|
2558
|
+
gitignoreContent = fs.readFileSync(gitignorePath, 'utf-8');
|
|
2559
|
+
}
|
|
2560
|
+
const linesToAdd = entriesToAdd.filter(entry => !gitignoreContent.includes(entry));
|
|
2561
|
+
if (linesToAdd.length > 0) {
|
|
2562
|
+
const newContent = gitignoreContent.trim() + '\n\n# RuVector intelligence data\n' + linesToAdd.join('\n') + '\n';
|
|
2563
|
+
fs.writeFileSync(gitignorePath, newContent);
|
|
2564
|
+
console.log(chalk.blue(' ✓ .gitignore updated'));
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
|
|
2568
|
+
// Create .ruvector directory for intelligence data
|
|
2569
|
+
const ruvectorDir = path.join(process.cwd(), '.ruvector');
|
|
2570
|
+
if (!fs.existsSync(ruvectorDir)) {
|
|
2571
|
+
fs.mkdirSync(ruvectorDir, { recursive: true });
|
|
2572
|
+
console.log(chalk.blue(' ✓ .ruvector/ directory created'));
|
|
2573
|
+
}
|
|
2574
|
+
|
|
2575
|
+
console.log(chalk.green('\n✅ RuVector hooks initialization complete!'));
|
|
2576
|
+
console.log(chalk.dim(' Run `npx ruvector hooks verify` to test the setup'));
|
|
2489
2577
|
});
|
|
2490
2578
|
|
|
2491
2579
|
hooksCmd.command('stats').description('Show intelligence statistics').action(() => {
|
|
@@ -2622,4 +2710,568 @@ hooksCmd.command('track-notification').description('Track notification').action(
|
|
|
2622
2710
|
console.log(JSON.stringify({ tracked: true }));
|
|
2623
2711
|
});
|
|
2624
2712
|
|
|
2713
|
+
// Verify hooks are working
|
|
2714
|
+
hooksCmd.command('verify')
|
|
2715
|
+
.description('Verify hooks are working correctly')
|
|
2716
|
+
.option('--verbose', 'Show detailed output')
|
|
2717
|
+
.action((opts) => {
|
|
2718
|
+
console.log(chalk.bold.cyan('\n🔍 RuVector Hooks Verification\n'));
|
|
2719
|
+
const checks = [];
|
|
2720
|
+
|
|
2721
|
+
// Check 1: Settings file exists
|
|
2722
|
+
const settingsPath = path.join(process.cwd(), '.claude', 'settings.json');
|
|
2723
|
+
if (fs.existsSync(settingsPath)) {
|
|
2724
|
+
checks.push({ name: 'Settings file', status: 'pass', detail: '.claude/settings.json exists' });
|
|
2725
|
+
try {
|
|
2726
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
|
2727
|
+
// Check hooks
|
|
2728
|
+
const requiredHooks = ['PreToolUse', 'PostToolUse', 'SessionStart', 'Stop'];
|
|
2729
|
+
const missingHooks = requiredHooks.filter(h => !settings.hooks?.[h]);
|
|
2730
|
+
if (missingHooks.length === 0) {
|
|
2731
|
+
checks.push({ name: 'Required hooks', status: 'pass', detail: 'All core hooks configured' });
|
|
2732
|
+
} else {
|
|
2733
|
+
checks.push({ name: 'Required hooks', status: 'fail', detail: `Missing: ${missingHooks.join(', ')}` });
|
|
2734
|
+
}
|
|
2735
|
+
// Check advanced hooks
|
|
2736
|
+
const advancedHooks = ['UserPromptSubmit', 'PreCompact', 'Notification'];
|
|
2737
|
+
const hasAdvanced = advancedHooks.filter(h => settings.hooks?.[h]);
|
|
2738
|
+
if (hasAdvanced.length > 0) {
|
|
2739
|
+
checks.push({ name: 'Advanced hooks', status: 'pass', detail: `${hasAdvanced.length}/3 configured` });
|
|
2740
|
+
} else {
|
|
2741
|
+
checks.push({ name: 'Advanced hooks', status: 'warn', detail: 'None configured (optional)' });
|
|
2742
|
+
}
|
|
2743
|
+
// Check env
|
|
2744
|
+
if (settings.env?.RUVECTOR_INTELLIGENCE_ENABLED) {
|
|
2745
|
+
checks.push({ name: 'Environment vars', status: 'pass', detail: 'Intelligence enabled' });
|
|
2746
|
+
} else {
|
|
2747
|
+
checks.push({ name: 'Environment vars', status: 'warn', detail: 'Not configured' });
|
|
2748
|
+
}
|
|
2749
|
+
// Check permissions
|
|
2750
|
+
if (settings.permissions?.allow?.length > 0) {
|
|
2751
|
+
checks.push({ name: 'Permissions', status: 'pass', detail: `${settings.permissions.allow.length} allowed patterns` });
|
|
2752
|
+
} else {
|
|
2753
|
+
checks.push({ name: 'Permissions', status: 'warn', detail: 'Not configured' });
|
|
2754
|
+
}
|
|
2755
|
+
} catch (e) {
|
|
2756
|
+
checks.push({ name: 'Settings parse', status: 'fail', detail: 'Invalid JSON' });
|
|
2757
|
+
}
|
|
2758
|
+
} else {
|
|
2759
|
+
checks.push({ name: 'Settings file', status: 'fail', detail: 'Run `npx ruvector hooks init` first' });
|
|
2760
|
+
}
|
|
2761
|
+
|
|
2762
|
+
// Check 2: .ruvector directory
|
|
2763
|
+
const ruvectorDir = path.join(process.cwd(), '.ruvector');
|
|
2764
|
+
if (fs.existsSync(ruvectorDir)) {
|
|
2765
|
+
checks.push({ name: 'Data directory', status: 'pass', detail: '.ruvector/ exists' });
|
|
2766
|
+
const intelFile = path.join(ruvectorDir, 'intelligence.json');
|
|
2767
|
+
if (fs.existsSync(intelFile)) {
|
|
2768
|
+
const stats = fs.statSync(intelFile);
|
|
2769
|
+
checks.push({ name: 'Intelligence file', status: 'pass', detail: `${(stats.size / 1024).toFixed(1)}KB` });
|
|
2770
|
+
} else {
|
|
2771
|
+
checks.push({ name: 'Intelligence file', status: 'warn', detail: 'Will be created on first use' });
|
|
2772
|
+
}
|
|
2773
|
+
} else {
|
|
2774
|
+
checks.push({ name: 'Data directory', status: 'warn', detail: 'Will be created on first use' });
|
|
2775
|
+
}
|
|
2776
|
+
|
|
2777
|
+
// Check 3: Hook command execution
|
|
2778
|
+
try {
|
|
2779
|
+
const { execSync } = require('child_process');
|
|
2780
|
+
execSync('npx ruvector hooks stats', { stdio: 'pipe', timeout: 5000 });
|
|
2781
|
+
checks.push({ name: 'Command execution', status: 'pass', detail: 'Hooks commands work' });
|
|
2782
|
+
} catch (e) {
|
|
2783
|
+
checks.push({ name: 'Command execution', status: 'fail', detail: 'Commands failed to execute' });
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2786
|
+
// Display results
|
|
2787
|
+
let passCount = 0, warnCount = 0, failCount = 0;
|
|
2788
|
+
checks.forEach(c => {
|
|
2789
|
+
const icon = c.status === 'pass' ? chalk.green('✓') : c.status === 'warn' ? chalk.yellow('⚠') : chalk.red('✗');
|
|
2790
|
+
const statusColor = c.status === 'pass' ? chalk.green : c.status === 'warn' ? chalk.yellow : chalk.red;
|
|
2791
|
+
console.log(` ${icon} ${c.name}: ${statusColor(c.detail)}`);
|
|
2792
|
+
if (c.status === 'pass') passCount++;
|
|
2793
|
+
else if (c.status === 'warn') warnCount++;
|
|
2794
|
+
else failCount++;
|
|
2795
|
+
});
|
|
2796
|
+
|
|
2797
|
+
console.log('');
|
|
2798
|
+
if (failCount === 0) {
|
|
2799
|
+
console.log(chalk.green(`✅ Verification passed! ${passCount} checks passed, ${warnCount} warnings`));
|
|
2800
|
+
} else {
|
|
2801
|
+
console.log(chalk.red(`❌ Verification failed: ${failCount} issues found`));
|
|
2802
|
+
console.log(chalk.dim(' Run `npx ruvector hooks doctor` for detailed diagnostics'));
|
|
2803
|
+
}
|
|
2804
|
+
});
|
|
2805
|
+
|
|
2806
|
+
// Doctor - diagnose setup issues
|
|
2807
|
+
hooksCmd.command('doctor')
|
|
2808
|
+
.description('Diagnose and fix setup issues')
|
|
2809
|
+
.option('--fix', 'Automatically fix issues')
|
|
2810
|
+
.action((opts) => {
|
|
2811
|
+
console.log(chalk.bold.cyan('\n🩺 RuVector Hooks Doctor\n'));
|
|
2812
|
+
const issues = [];
|
|
2813
|
+
const fixes = [];
|
|
2814
|
+
|
|
2815
|
+
// Check settings file
|
|
2816
|
+
const settingsPath = path.join(process.cwd(), '.claude', 'settings.json');
|
|
2817
|
+
if (!fs.existsSync(settingsPath)) {
|
|
2818
|
+
issues.push({ severity: 'error', message: 'No .claude/settings.json found', fix: 'Run `npx ruvector hooks init`' });
|
|
2819
|
+
} else {
|
|
2820
|
+
try {
|
|
2821
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
|
2822
|
+
|
|
2823
|
+
// Check for invalid schema
|
|
2824
|
+
if (settings.$schema && !settings.$schema.includes('schemastore.org')) {
|
|
2825
|
+
issues.push({ severity: 'warning', message: 'Invalid schema URL', fix: 'Will be corrected' });
|
|
2826
|
+
if (opts.fix) {
|
|
2827
|
+
settings.$schema = 'https://json.schemastore.org/claude-code-settings.json';
|
|
2828
|
+
fixes.push('Fixed schema URL');
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
|
|
2832
|
+
// Check for old hook names
|
|
2833
|
+
if (settings.hooks?.Start || settings.hooks?.End) {
|
|
2834
|
+
issues.push({ severity: 'error', message: 'Invalid hook names (Start/End)', fix: 'Should be SessionStart/Stop' });
|
|
2835
|
+
if (opts.fix) {
|
|
2836
|
+
delete settings.hooks.Start;
|
|
2837
|
+
delete settings.hooks.End;
|
|
2838
|
+
fixes.push('Removed invalid hook names');
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2841
|
+
|
|
2842
|
+
// Check hook format
|
|
2843
|
+
const hookNames = ['PreToolUse', 'PostToolUse'];
|
|
2844
|
+
hookNames.forEach(name => {
|
|
2845
|
+
if (settings.hooks?.[name]) {
|
|
2846
|
+
settings.hooks[name].forEach((hook, i) => {
|
|
2847
|
+
if (typeof hook.matcher === 'object') {
|
|
2848
|
+
issues.push({ severity: 'error', message: `${name}[${i}].matcher should be string, not object`, fix: 'Will be corrected' });
|
|
2849
|
+
}
|
|
2850
|
+
});
|
|
2851
|
+
}
|
|
2852
|
+
});
|
|
2853
|
+
|
|
2854
|
+
// Check for npx vs direct command
|
|
2855
|
+
const checkCommands = (hooks) => {
|
|
2856
|
+
if (!hooks) return;
|
|
2857
|
+
hooks.forEach(h => {
|
|
2858
|
+
h.hooks?.forEach(hh => {
|
|
2859
|
+
if (hh.command && hh.command.includes('ruvector') && !hh.command.startsWith('npx ') && !hh.command.includes('/bin/')) {
|
|
2860
|
+
issues.push({ severity: 'warning', message: `Command should use 'npx ruvector' for portability`, fix: 'Update to use npx' });
|
|
2861
|
+
}
|
|
2862
|
+
});
|
|
2863
|
+
});
|
|
2864
|
+
};
|
|
2865
|
+
Object.values(settings.hooks || {}).forEach(checkCommands);
|
|
2866
|
+
|
|
2867
|
+
// Save fixes
|
|
2868
|
+
if (opts.fix && fixes.length > 0) {
|
|
2869
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
2870
|
+
}
|
|
2871
|
+
} catch (e) {
|
|
2872
|
+
issues.push({ severity: 'error', message: 'Invalid JSON in settings file', fix: 'Re-run `npx ruvector hooks init --force`' });
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
|
|
2876
|
+
// Check .gitignore
|
|
2877
|
+
const gitignorePath = path.join(process.cwd(), '.gitignore');
|
|
2878
|
+
if (fs.existsSync(gitignorePath)) {
|
|
2879
|
+
const content = fs.readFileSync(gitignorePath, 'utf-8');
|
|
2880
|
+
if (!content.includes('.ruvector/')) {
|
|
2881
|
+
issues.push({ severity: 'warning', message: '.ruvector/ not in .gitignore', fix: 'Add to prevent committing learning data' });
|
|
2882
|
+
if (opts.fix) {
|
|
2883
|
+
fs.appendFileSync(gitignorePath, '\n# RuVector intelligence data\n.ruvector/\n');
|
|
2884
|
+
fixes.push('Added .ruvector/ to .gitignore');
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
}
|
|
2888
|
+
|
|
2889
|
+
// Display results
|
|
2890
|
+
if (issues.length === 0) {
|
|
2891
|
+
console.log(chalk.green(' ✓ No issues found! Your setup looks healthy.'));
|
|
2892
|
+
} else {
|
|
2893
|
+
issues.forEach(i => {
|
|
2894
|
+
const icon = i.severity === 'error' ? chalk.red('✗') : chalk.yellow('⚠');
|
|
2895
|
+
console.log(` ${icon} ${i.message}`);
|
|
2896
|
+
console.log(chalk.dim(` Fix: ${i.fix}`));
|
|
2897
|
+
});
|
|
2898
|
+
|
|
2899
|
+
if (opts.fix && fixes.length > 0) {
|
|
2900
|
+
console.log(chalk.green(`\n✅ Applied ${fixes.length} fix(es):`));
|
|
2901
|
+
fixes.forEach(f => console.log(chalk.green(` • ${f}`)));
|
|
2902
|
+
} else if (issues.some(i => i.severity === 'error')) {
|
|
2903
|
+
console.log(chalk.yellow('\n💡 Run with --fix to automatically fix issues'));
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
});
|
|
2907
|
+
|
|
2908
|
+
// Export intelligence data
|
|
2909
|
+
hooksCmd.command('export')
|
|
2910
|
+
.description('Export intelligence data for backup')
|
|
2911
|
+
.option('-o, --output <file>', 'Output file path', 'ruvector-export.json')
|
|
2912
|
+
.option('--include-all', 'Include all data (patterns, memories, trajectories)')
|
|
2913
|
+
.action((opts) => {
|
|
2914
|
+
const intel = new Intelligence();
|
|
2915
|
+
const exportData = {
|
|
2916
|
+
version: '1.0',
|
|
2917
|
+
exported_at: new Date().toISOString(),
|
|
2918
|
+
patterns: intel.data?.patterns || {},
|
|
2919
|
+
memories: opts.includeAll ? (intel.data?.memories || []) : [],
|
|
2920
|
+
trajectories: opts.includeAll ? (intel.data?.trajectories || []) : [],
|
|
2921
|
+
errors: intel.data?.errors || {},
|
|
2922
|
+
stats: intel.stats()
|
|
2923
|
+
};
|
|
2924
|
+
|
|
2925
|
+
const outputPath = path.resolve(opts.output);
|
|
2926
|
+
fs.writeFileSync(outputPath, JSON.stringify(exportData, null, 2));
|
|
2927
|
+
|
|
2928
|
+
console.log(chalk.green(`✅ Exported intelligence data to ${outputPath}`));
|
|
2929
|
+
console.log(chalk.dim(` ${Object.keys(exportData.patterns).length} patterns`));
|
|
2930
|
+
console.log(chalk.dim(` ${exportData.memories.length} memories`));
|
|
2931
|
+
console.log(chalk.dim(` ${exportData.trajectories.length} trajectories`));
|
|
2932
|
+
});
|
|
2933
|
+
|
|
2934
|
+
// Import intelligence data
|
|
2935
|
+
hooksCmd.command('import')
|
|
2936
|
+
.description('Import intelligence data from backup')
|
|
2937
|
+
.argument('<file>', 'Import file path')
|
|
2938
|
+
.option('--merge', 'Merge with existing data (default: replace)')
|
|
2939
|
+
.option('--dry-run', 'Show what would be imported without making changes')
|
|
2940
|
+
.action((file, opts) => {
|
|
2941
|
+
const importPath = path.resolve(file);
|
|
2942
|
+
if (!fs.existsSync(importPath)) {
|
|
2943
|
+
console.error(chalk.red(`❌ File not found: ${importPath}`));
|
|
2944
|
+
process.exit(1);
|
|
2945
|
+
}
|
|
2946
|
+
|
|
2947
|
+
try {
|
|
2948
|
+
const importData = JSON.parse(fs.readFileSync(importPath, 'utf-8'));
|
|
2949
|
+
|
|
2950
|
+
if (!importData.version) {
|
|
2951
|
+
console.error(chalk.red('❌ Invalid export file (missing version)'));
|
|
2952
|
+
process.exit(1);
|
|
2953
|
+
}
|
|
2954
|
+
|
|
2955
|
+
console.log(chalk.cyan(`📦 Import file: ${file}`));
|
|
2956
|
+
console.log(chalk.dim(` Version: ${importData.version}`));
|
|
2957
|
+
console.log(chalk.dim(` Exported: ${importData.exported_at}`));
|
|
2958
|
+
console.log(chalk.dim(` Patterns: ${Object.keys(importData.patterns || {}).length}`));
|
|
2959
|
+
console.log(chalk.dim(` Memories: ${(importData.memories || []).length}`));
|
|
2960
|
+
console.log(chalk.dim(` Trajectories: ${(importData.trajectories || []).length}`));
|
|
2961
|
+
|
|
2962
|
+
if (opts.dryRun) {
|
|
2963
|
+
console.log(chalk.yellow('\n⚠️ Dry run - no changes made'));
|
|
2964
|
+
return;
|
|
2965
|
+
}
|
|
2966
|
+
|
|
2967
|
+
const intel = new Intelligence();
|
|
2968
|
+
|
|
2969
|
+
if (opts.merge) {
|
|
2970
|
+
// Merge patterns
|
|
2971
|
+
Object.assign(intel.data.patterns, importData.patterns || {});
|
|
2972
|
+
// Merge memories (deduplicate by content)
|
|
2973
|
+
const existingContent = new Set((intel.data.memories || []).map(m => m.content));
|
|
2974
|
+
(importData.memories || []).forEach(m => {
|
|
2975
|
+
if (!existingContent.has(m.content)) {
|
|
2976
|
+
intel.data.memories.push(m);
|
|
2977
|
+
}
|
|
2978
|
+
});
|
|
2979
|
+
// Merge trajectories
|
|
2980
|
+
intel.data.trajectories = (intel.data.trajectories || []).concat(importData.trajectories || []);
|
|
2981
|
+
// Merge errors
|
|
2982
|
+
Object.assign(intel.data.errors, importData.errors || {});
|
|
2983
|
+
console.log(chalk.green('✅ Merged intelligence data'));
|
|
2984
|
+
} else {
|
|
2985
|
+
intel.data.patterns = importData.patterns || {};
|
|
2986
|
+
intel.data.memories = importData.memories || [];
|
|
2987
|
+
intel.data.trajectories = importData.trajectories || [];
|
|
2988
|
+
intel.data.errors = importData.errors || {};
|
|
2989
|
+
console.log(chalk.green('✅ Replaced intelligence data'));
|
|
2990
|
+
}
|
|
2991
|
+
|
|
2992
|
+
intel.save();
|
|
2993
|
+
console.log(chalk.dim(' Data saved to .ruvector/intelligence.json'));
|
|
2994
|
+
} catch (e) {
|
|
2995
|
+
console.error(chalk.red(`❌ Failed to import: ${e.message}`));
|
|
2996
|
+
process.exit(1);
|
|
2997
|
+
}
|
|
2998
|
+
});
|
|
2999
|
+
|
|
3000
|
+
// Pretrain - analyze repo and bootstrap learning with agent swarm
|
|
3001
|
+
hooksCmd.command('pretrain')
|
|
3002
|
+
.description('Pretrain intelligence by analyzing the repository with agent swarm')
|
|
3003
|
+
.option('--depth <n>', 'Git history depth to analyze', '100')
|
|
3004
|
+
.option('--workers <n>', 'Number of parallel analysis workers', '4')
|
|
3005
|
+
.option('--skip-git', 'Skip git history analysis')
|
|
3006
|
+
.option('--skip-files', 'Skip file structure analysis')
|
|
3007
|
+
.option('--verbose', 'Show detailed progress')
|
|
3008
|
+
.action(async (opts) => {
|
|
3009
|
+
const { execSync, spawn } = require('child_process');
|
|
3010
|
+
console.log(chalk.bold.cyan('\n🧠 RuVector Pretrain - Repository Intelligence Bootstrap\n'));
|
|
3011
|
+
|
|
3012
|
+
const intel = new Intelligence();
|
|
3013
|
+
const startTime = Date.now();
|
|
3014
|
+
const stats = { files: 0, patterns: 0, memories: 0, coedits: 0 };
|
|
3015
|
+
|
|
3016
|
+
// Agent types for different file patterns
|
|
3017
|
+
const agentMapping = {
|
|
3018
|
+
// Rust
|
|
3019
|
+
'.rs': 'rust-developer',
|
|
3020
|
+
'Cargo.toml': 'rust-developer',
|
|
3021
|
+
'Cargo.lock': 'rust-developer',
|
|
3022
|
+
// JavaScript/TypeScript
|
|
3023
|
+
'.js': 'javascript-developer',
|
|
3024
|
+
'.jsx': 'react-developer',
|
|
3025
|
+
'.ts': 'typescript-developer',
|
|
3026
|
+
'.tsx': 'react-developer',
|
|
3027
|
+
'.mjs': 'javascript-developer',
|
|
3028
|
+
'.cjs': 'javascript-developer',
|
|
3029
|
+
'package.json': 'node-developer',
|
|
3030
|
+
// Python
|
|
3031
|
+
'.py': 'python-developer',
|
|
3032
|
+
'requirements.txt': 'python-developer',
|
|
3033
|
+
'pyproject.toml': 'python-developer',
|
|
3034
|
+
'setup.py': 'python-developer',
|
|
3035
|
+
// Go
|
|
3036
|
+
'.go': 'go-developer',
|
|
3037
|
+
'go.mod': 'go-developer',
|
|
3038
|
+
// Web
|
|
3039
|
+
'.html': 'frontend-developer',
|
|
3040
|
+
'.css': 'frontend-developer',
|
|
3041
|
+
'.scss': 'frontend-developer',
|
|
3042
|
+
'.vue': 'vue-developer',
|
|
3043
|
+
'.svelte': 'svelte-developer',
|
|
3044
|
+
// Config
|
|
3045
|
+
'.json': 'config-specialist',
|
|
3046
|
+
'.yaml': 'config-specialist',
|
|
3047
|
+
'.yml': 'config-specialist',
|
|
3048
|
+
'.toml': 'config-specialist',
|
|
3049
|
+
// Docs
|
|
3050
|
+
'.md': 'documentation-specialist',
|
|
3051
|
+
'.mdx': 'documentation-specialist',
|
|
3052
|
+
// Tests
|
|
3053
|
+
'.test.js': 'test-engineer',
|
|
3054
|
+
'.test.ts': 'test-engineer',
|
|
3055
|
+
'.spec.js': 'test-engineer',
|
|
3056
|
+
'.spec.ts': 'test-engineer',
|
|
3057
|
+
'_test.go': 'test-engineer',
|
|
3058
|
+
'_test.rs': 'test-engineer',
|
|
3059
|
+
// DevOps
|
|
3060
|
+
'Dockerfile': 'devops-engineer',
|
|
3061
|
+
'docker-compose.yml': 'devops-engineer',
|
|
3062
|
+
'.github/workflows': 'cicd-engineer',
|
|
3063
|
+
'Makefile': 'devops-engineer',
|
|
3064
|
+
// SQL
|
|
3065
|
+
'.sql': 'database-specialist',
|
|
3066
|
+
};
|
|
3067
|
+
|
|
3068
|
+
// Phase 1: Analyze file structure
|
|
3069
|
+
if (!opts.skipFiles) {
|
|
3070
|
+
console.log(chalk.yellow('📁 Phase 1: Analyzing file structure...\n'));
|
|
3071
|
+
|
|
3072
|
+
try {
|
|
3073
|
+
// Get all files in repo
|
|
3074
|
+
const files = execSync('git ls-files 2>/dev/null || find . -type f -not -path "./.git/*" -not -path "./node_modules/*" -not -path "./target/*"',
|
|
3075
|
+
{ encoding: 'utf-8', maxBuffer: 50 * 1024 * 1024 }).trim().split('\n').filter(f => f);
|
|
3076
|
+
|
|
3077
|
+
const filesByType = {};
|
|
3078
|
+
const dirPatterns = {};
|
|
3079
|
+
|
|
3080
|
+
files.forEach(file => {
|
|
3081
|
+
stats.files++;
|
|
3082
|
+
const ext = path.extname(file);
|
|
3083
|
+
const basename = path.basename(file);
|
|
3084
|
+
const dir = path.dirname(file);
|
|
3085
|
+
|
|
3086
|
+
// Determine agent for this file
|
|
3087
|
+
let agent = 'coder'; // default
|
|
3088
|
+
if (agentMapping[basename]) {
|
|
3089
|
+
agent = agentMapping[basename];
|
|
3090
|
+
} else if (agentMapping[ext]) {
|
|
3091
|
+
agent = agentMapping[ext];
|
|
3092
|
+
} else if (file.includes('.test.') || file.includes('.spec.') || file.includes('_test.')) {
|
|
3093
|
+
agent = 'test-engineer';
|
|
3094
|
+
} else if (file.includes('.github/workflows')) {
|
|
3095
|
+
agent = 'cicd-engineer';
|
|
3096
|
+
}
|
|
3097
|
+
|
|
3098
|
+
// Track file types
|
|
3099
|
+
filesByType[ext] = (filesByType[ext] || 0) + 1;
|
|
3100
|
+
|
|
3101
|
+
// Track directory patterns
|
|
3102
|
+
const parts = dir.split('/');
|
|
3103
|
+
if (parts[0]) {
|
|
3104
|
+
dirPatterns[parts[0]] = dirPatterns[parts[0]] || { count: 0, agents: {} };
|
|
3105
|
+
dirPatterns[parts[0]].count++;
|
|
3106
|
+
dirPatterns[parts[0]].agents[agent] = (dirPatterns[parts[0]].agents[agent] || 0) + 1;
|
|
3107
|
+
}
|
|
3108
|
+
|
|
3109
|
+
// Create Q-learning pattern for this file type
|
|
3110
|
+
const state = `edit:${ext || 'unknown'}`;
|
|
3111
|
+
if (!intel.data.patterns[state]) {
|
|
3112
|
+
intel.data.patterns[state] = {};
|
|
3113
|
+
}
|
|
3114
|
+
intel.data.patterns[state][agent] = (intel.data.patterns[state][agent] || 0) + 0.5;
|
|
3115
|
+
stats.patterns++;
|
|
3116
|
+
});
|
|
3117
|
+
|
|
3118
|
+
// Log summary
|
|
3119
|
+
if (opts.verbose) {
|
|
3120
|
+
console.log(chalk.dim(' File types found:'));
|
|
3121
|
+
Object.entries(filesByType).sort((a, b) => b[1] - a[1]).slice(0, 10).forEach(([ext, count]) => {
|
|
3122
|
+
console.log(chalk.dim(` ${ext || '(no ext)'}: ${count} files`));
|
|
3123
|
+
});
|
|
3124
|
+
}
|
|
3125
|
+
console.log(chalk.green(` ✓ Analyzed ${stats.files} files`));
|
|
3126
|
+
console.log(chalk.green(` ✓ Created ${Object.keys(intel.data.patterns).length} routing patterns`));
|
|
3127
|
+
|
|
3128
|
+
} catch (e) {
|
|
3129
|
+
console.log(chalk.yellow(` ⚠ File analysis skipped: ${e.message}`));
|
|
3130
|
+
}
|
|
3131
|
+
}
|
|
3132
|
+
|
|
3133
|
+
// Phase 2: Analyze git history for co-edit patterns
|
|
3134
|
+
if (!opts.skipGit) {
|
|
3135
|
+
console.log(chalk.yellow('\n📜 Phase 2: Analyzing git history for co-edit patterns...\n'));
|
|
3136
|
+
|
|
3137
|
+
try {
|
|
3138
|
+
// Get commits with files changed
|
|
3139
|
+
const gitLog = execSync(
|
|
3140
|
+
`git log --name-only --pretty=format:"COMMIT:%H" -n ${opts.depth} 2>/dev/null`,
|
|
3141
|
+
{ encoding: 'utf-8', maxBuffer: 50 * 1024 * 1024 }
|
|
3142
|
+
);
|
|
3143
|
+
|
|
3144
|
+
const commits = gitLog.split('COMMIT:').filter(c => c.trim());
|
|
3145
|
+
const coEditMap = {};
|
|
3146
|
+
|
|
3147
|
+
commits.forEach(commit => {
|
|
3148
|
+
const lines = commit.trim().split('\n').filter(l => l && !l.startsWith('COMMIT:'));
|
|
3149
|
+
const files = lines.slice(1).filter(f => f.trim()); // Skip the hash
|
|
3150
|
+
|
|
3151
|
+
// Track which files are edited together
|
|
3152
|
+
files.forEach(file1 => {
|
|
3153
|
+
files.forEach(file2 => {
|
|
3154
|
+
if (file1 !== file2) {
|
|
3155
|
+
const key = [file1, file2].sort().join('|');
|
|
3156
|
+
coEditMap[key] = (coEditMap[key] || 0) + 1;
|
|
3157
|
+
}
|
|
3158
|
+
});
|
|
3159
|
+
});
|
|
3160
|
+
});
|
|
3161
|
+
|
|
3162
|
+
// Find strong co-edit patterns (files edited together 3+ times)
|
|
3163
|
+
const strongPatterns = Object.entries(coEditMap)
|
|
3164
|
+
.filter(([, count]) => count >= 3)
|
|
3165
|
+
.sort((a, b) => b[1] - a[1]);
|
|
3166
|
+
|
|
3167
|
+
// Store as sequence patterns
|
|
3168
|
+
strongPatterns.slice(0, 100).forEach(([key, count]) => {
|
|
3169
|
+
const [file1, file2] = key.split('|');
|
|
3170
|
+
if (!intel.data.sequences) intel.data.sequences = {};
|
|
3171
|
+
if (!intel.data.sequences[file1]) intel.data.sequences[file1] = [];
|
|
3172
|
+
|
|
3173
|
+
const existing = intel.data.sequences[file1].find(s => s.file === file2);
|
|
3174
|
+
if (existing) {
|
|
3175
|
+
existing.score += count;
|
|
3176
|
+
} else {
|
|
3177
|
+
intel.data.sequences[file1].push({ file: file2, score: count });
|
|
3178
|
+
}
|
|
3179
|
+
stats.coedits++;
|
|
3180
|
+
});
|
|
3181
|
+
|
|
3182
|
+
console.log(chalk.green(` ✓ Analyzed ${commits.length} commits`));
|
|
3183
|
+
console.log(chalk.green(` ✓ Found ${strongPatterns.length} co-edit patterns`));
|
|
3184
|
+
|
|
3185
|
+
if (opts.verbose && strongPatterns.length > 0) {
|
|
3186
|
+
console.log(chalk.dim(' Top co-edit patterns:'));
|
|
3187
|
+
strongPatterns.slice(0, 5).forEach(([key, count]) => {
|
|
3188
|
+
const [f1, f2] = key.split('|');
|
|
3189
|
+
console.log(chalk.dim(` ${path.basename(f1)} ↔ ${path.basename(f2)}: ${count} times`));
|
|
3190
|
+
});
|
|
3191
|
+
}
|
|
3192
|
+
|
|
3193
|
+
} catch (e) {
|
|
3194
|
+
console.log(chalk.yellow(` ⚠ Git analysis skipped: ${e.message}`));
|
|
3195
|
+
}
|
|
3196
|
+
}
|
|
3197
|
+
|
|
3198
|
+
// Phase 3: Create vector memories from important files
|
|
3199
|
+
console.log(chalk.yellow('\n💾 Phase 3: Creating vector memories from key files...\n'));
|
|
3200
|
+
|
|
3201
|
+
try {
|
|
3202
|
+
const importantFiles = [
|
|
3203
|
+
'README.md', 'CLAUDE.md', 'package.json', 'Cargo.toml',
|
|
3204
|
+
'pyproject.toml', 'go.mod', '.claude/settings.json'
|
|
3205
|
+
];
|
|
3206
|
+
|
|
3207
|
+
for (const filename of importantFiles) {
|
|
3208
|
+
const filePath = path.join(process.cwd(), filename);
|
|
3209
|
+
if (fs.existsSync(filePath)) {
|
|
3210
|
+
try {
|
|
3211
|
+
const content = fs.readFileSync(filePath, 'utf-8').slice(0, 2000); // First 2KB
|
|
3212
|
+
intel.data.memories = intel.data.memories || [];
|
|
3213
|
+
intel.data.memories.push({
|
|
3214
|
+
content: `[${filename}] ${content.replace(/\n/g, ' ').slice(0, 500)}`,
|
|
3215
|
+
type: 'project',
|
|
3216
|
+
created: new Date().toISOString(),
|
|
3217
|
+
embedding: intel.simpleEmbed ? intel.simpleEmbed(content) : null
|
|
3218
|
+
});
|
|
3219
|
+
stats.memories++;
|
|
3220
|
+
if (opts.verbose) console.log(chalk.dim(` ✓ ${filename}`));
|
|
3221
|
+
} catch (e) { /* skip unreadable files */ }
|
|
3222
|
+
}
|
|
3223
|
+
}
|
|
3224
|
+
|
|
3225
|
+
console.log(chalk.green(` ✓ Created ${stats.memories} memory entries`));
|
|
3226
|
+
|
|
3227
|
+
} catch (e) {
|
|
3228
|
+
console.log(chalk.yellow(` ⚠ Memory creation skipped: ${e.message}`));
|
|
3229
|
+
}
|
|
3230
|
+
|
|
3231
|
+
// Phase 4: Analyze directory structure for agent recommendations
|
|
3232
|
+
console.log(chalk.yellow('\n🗂️ Phase 4: Building directory-agent mappings...\n'));
|
|
3233
|
+
|
|
3234
|
+
try {
|
|
3235
|
+
const dirs = execSync('find . -type d -maxdepth 2 -not -path "./.git*" -not -path "./node_modules*" -not -path "./target*" 2>/dev/null || echo "."',
|
|
3236
|
+
{ encoding: 'utf-8' }).trim().split('\n');
|
|
3237
|
+
|
|
3238
|
+
const dirAgentMap = {};
|
|
3239
|
+
dirs.forEach(dir => {
|
|
3240
|
+
const name = path.basename(dir);
|
|
3241
|
+
// Infer agent from directory name
|
|
3242
|
+
if (['src', 'lib', 'core'].includes(name)) dirAgentMap[dir] = 'coder';
|
|
3243
|
+
else if (['test', 'tests', '__tests__', 'spec'].includes(name)) dirAgentMap[dir] = 'test-engineer';
|
|
3244
|
+
else if (['docs', 'documentation'].includes(name)) dirAgentMap[dir] = 'documentation-specialist';
|
|
3245
|
+
else if (['scripts', 'bin'].includes(name)) dirAgentMap[dir] = 'devops-engineer';
|
|
3246
|
+
else if (['components', 'views', 'pages'].includes(name)) dirAgentMap[dir] = 'frontend-developer';
|
|
3247
|
+
else if (['api', 'routes', 'handlers'].includes(name)) dirAgentMap[dir] = 'backend-developer';
|
|
3248
|
+
else if (['models', 'entities', 'schemas'].includes(name)) dirAgentMap[dir] = 'database-specialist';
|
|
3249
|
+
else if (['.github', '.gitlab', 'ci'].includes(name)) dirAgentMap[dir] = 'cicd-engineer';
|
|
3250
|
+
});
|
|
3251
|
+
|
|
3252
|
+
// Store directory patterns
|
|
3253
|
+
intel.data.dirPatterns = dirAgentMap;
|
|
3254
|
+
console.log(chalk.green(` ✓ Mapped ${Object.keys(dirAgentMap).length} directories to agents`));
|
|
3255
|
+
|
|
3256
|
+
} catch (e) {
|
|
3257
|
+
console.log(chalk.yellow(` ⚠ Directory analysis skipped: ${e.message}`));
|
|
3258
|
+
}
|
|
3259
|
+
|
|
3260
|
+
// Save all learning data
|
|
3261
|
+
intel.data.pretrained = {
|
|
3262
|
+
date: new Date().toISOString(),
|
|
3263
|
+
stats: stats
|
|
3264
|
+
};
|
|
3265
|
+
intel.save();
|
|
3266
|
+
|
|
3267
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
3268
|
+
console.log(chalk.bold.green(`\n✅ Pretrain complete in ${elapsed}s!\n`));
|
|
3269
|
+
console.log(chalk.cyan('Summary:'));
|
|
3270
|
+
console.log(` 📁 ${stats.files} files analyzed`);
|
|
3271
|
+
console.log(` 🧠 ${stats.patterns} agent routing patterns`);
|
|
3272
|
+
console.log(` 🔗 ${stats.coedits} co-edit patterns`);
|
|
3273
|
+
console.log(` 💾 ${stats.memories} memory entries`);
|
|
3274
|
+
console.log(chalk.dim('\nThe intelligence layer will now provide better recommendations.'));
|
|
3275
|
+
});
|
|
3276
|
+
|
|
2625
3277
|
program.parse();
|