@vibecheckai/cli 3.5.0 → 3.5.2
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/registry.js +214 -237
- package/bin/runners/cli-utils.js +33 -2
- package/bin/runners/context/analyzer.js +52 -1
- package/bin/runners/context/generators/cursor.js +2 -49
- package/bin/runners/context/git-context.js +3 -1
- package/bin/runners/context/team-conventions.js +33 -7
- package/bin/runners/lib/analysis-core.js +25 -5
- package/bin/runners/lib/analyzers.js +431 -481
- package/bin/runners/lib/default-config.js +127 -0
- package/bin/runners/lib/doctor/modules/security.js +3 -1
- package/bin/runners/lib/engine/ast-cache.js +210 -0
- package/bin/runners/lib/engine/auth-extractor.js +211 -0
- package/bin/runners/lib/engine/billing-extractor.js +112 -0
- package/bin/runners/lib/engine/enforcement-extractor.js +100 -0
- package/bin/runners/lib/engine/env-extractor.js +207 -0
- package/bin/runners/lib/engine/express-extractor.js +208 -0
- package/bin/runners/lib/engine/extractors.js +849 -0
- package/bin/runners/lib/engine/index.js +207 -0
- package/bin/runners/lib/engine/repo-index.js +514 -0
- package/bin/runners/lib/engine/types.js +124 -0
- package/bin/runners/lib/engines/accessibility-engine.js +18 -218
- package/bin/runners/lib/engines/api-consistency-engine.js +30 -335
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +27 -292
- package/bin/runners/lib/engines/empty-catch-engine.js +17 -127
- package/bin/runners/lib/engines/mock-data-engine.js +10 -53
- package/bin/runners/lib/engines/performance-issues-engine.js +36 -176
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +54 -382
- package/bin/runners/lib/engines/type-aware-engine.js +39 -263
- package/bin/runners/lib/engines/vibecheck-engines/index.js +13 -122
- package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +73 -373
- package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
- package/bin/runners/lib/entitlements-v2.js +73 -97
- package/bin/runners/lib/error-handler.js +44 -3
- package/bin/runners/lib/error-messages.js +289 -0
- package/bin/runners/lib/evidence-pack.js +7 -1
- package/bin/runners/lib/finding-id.js +69 -0
- package/bin/runners/lib/finding-sorter.js +89 -0
- package/bin/runners/lib/html-proof-report.js +700 -350
- package/bin/runners/lib/missions/plan.js +6 -46
- package/bin/runners/lib/missions/templates.js +0 -232
- package/bin/runners/lib/next-action.js +560 -0
- package/bin/runners/lib/prerequisites.js +149 -0
- package/bin/runners/lib/route-detection.js +137 -68
- package/bin/runners/lib/scan-output.js +91 -76
- package/bin/runners/lib/scan-runner.js +135 -0
- package/bin/runners/lib/schemas/ajv-validator.js +464 -0
- package/bin/runners/lib/schemas/error-envelope.schema.json +105 -0
- package/bin/runners/lib/schemas/finding-v3.schema.json +151 -0
- package/bin/runners/lib/schemas/report-artifact.schema.json +120 -0
- package/bin/runners/lib/schemas/run-request.schema.json +108 -0
- package/bin/runners/lib/schemas/validator.js +27 -0
- package/bin/runners/lib/schemas/verdict.schema.json +140 -0
- package/bin/runners/lib/ship-output-enterprise.js +23 -23
- package/bin/runners/lib/ship-output.js +75 -31
- package/bin/runners/lib/terminal-ui.js +6 -113
- package/bin/runners/lib/truth.js +351 -10
- package/bin/runners/lib/unified-cli-output.js +430 -603
- package/bin/runners/lib/unified-output.js +13 -9
- package/bin/runners/runAIAgent.js +10 -5
- package/bin/runners/runAgent.js +0 -3
- package/bin/runners/runAllowlist.js +389 -0
- package/bin/runners/runApprove.js +0 -33
- package/bin/runners/runAuth.js +73 -45
- package/bin/runners/runCheckpoint.js +51 -11
- package/bin/runners/runClassify.js +85 -21
- package/bin/runners/runContext.js +0 -3
- package/bin/runners/runDoctor.js +41 -28
- package/bin/runners/runEvidencePack.js +362 -0
- package/bin/runners/runFirewall.js +0 -3
- package/bin/runners/runFirewallHook.js +0 -3
- package/bin/runners/runFix.js +66 -76
- package/bin/runners/runGuard.js +18 -411
- package/bin/runners/runInit.js +113 -30
- package/bin/runners/runLabs.js +424 -0
- package/bin/runners/runMcp.js +19 -25
- package/bin/runners/runPolish.js +64 -240
- package/bin/runners/runPromptFirewall.js +12 -5
- package/bin/runners/runProve.js +57 -22
- package/bin/runners/runQuickstart.js +531 -0
- package/bin/runners/runReality.js +59 -68
- package/bin/runners/runReport.js +38 -33
- package/bin/runners/runRuntime.js +8 -5
- package/bin/runners/runScan.js +1413 -190
- package/bin/runners/runShip.js +113 -719
- package/bin/runners/runTruth.js +0 -3
- package/bin/runners/runValidate.js +13 -9
- package/bin/runners/runWatch.js +23 -14
- package/bin/scan.js +6 -1
- package/bin/vibecheck.js +204 -185
- package/mcp-server/deprecation-middleware.js +282 -0
- package/mcp-server/handlers/index.ts +15 -0
- package/mcp-server/handlers/tool-handler.ts +554 -0
- package/mcp-server/index-v1.js +698 -0
- package/mcp-server/index.js +210 -238
- package/mcp-server/lib/cache-wrapper.cjs +383 -0
- package/mcp-server/lib/error-envelope.js +138 -0
- package/mcp-server/lib/executor.ts +499 -0
- package/mcp-server/lib/index.ts +19 -0
- package/mcp-server/lib/rate-limiter.js +166 -0
- package/mcp-server/lib/sandbox.test.ts +519 -0
- package/mcp-server/lib/sandbox.ts +395 -0
- package/mcp-server/lib/types.ts +267 -0
- package/mcp-server/package.json +12 -3
- package/mcp-server/registry/tool-registry.js +794 -0
- package/mcp-server/registry/tools.json +605 -0
- package/mcp-server/registry.test.ts +334 -0
- package/mcp-server/tests/tier-gating.test.js +297 -0
- package/mcp-server/tier-auth.js +378 -45
- package/mcp-server/tools-v3.js +353 -442
- package/mcp-server/tsconfig.json +37 -0
- package/mcp-server/vibecheck-2.0-tools.js +14 -1
- package/package.json +1 -1
- package/bin/runners/lib/agent-firewall/learning/learning-engine.js +0 -849
- package/bin/runners/lib/audit-logger.js +0 -532
- package/bin/runners/lib/authority/authorities/architecture.js +0 -364
- package/bin/runners/lib/authority/authorities/compliance.js +0 -341
- package/bin/runners/lib/authority/authorities/human.js +0 -343
- package/bin/runners/lib/authority/authorities/quality.js +0 -420
- package/bin/runners/lib/authority/authorities/security.js +0 -228
- package/bin/runners/lib/authority/index.js +0 -293
- package/bin/runners/lib/bundle/bundle-intelligence.js +0 -846
- package/bin/runners/lib/cli-charts.js +0 -368
- package/bin/runners/lib/cli-config-display.js +0 -405
- package/bin/runners/lib/cli-demo.js +0 -275
- package/bin/runners/lib/cli-errors.js +0 -438
- package/bin/runners/lib/cli-help-formatter.js +0 -439
- package/bin/runners/lib/cli-interactive-menu.js +0 -509
- package/bin/runners/lib/cli-prompts.js +0 -441
- package/bin/runners/lib/cli-scan-cards.js +0 -362
- package/bin/runners/lib/compliance-reporter.js +0 -710
- package/bin/runners/lib/conductor/index.js +0 -671
- package/bin/runners/lib/easy/README.md +0 -123
- package/bin/runners/lib/easy/index.js +0 -140
- package/bin/runners/lib/easy/interactive-wizard.js +0 -788
- package/bin/runners/lib/easy/one-click-firewall.js +0 -564
- package/bin/runners/lib/easy/zero-config-reality.js +0 -714
- package/bin/runners/lib/engines/async-patterns-engine.js +0 -444
- package/bin/runners/lib/engines/bundle-size-engine.js +0 -433
- package/bin/runners/lib/engines/confidence-scoring.js +0 -276
- package/bin/runners/lib/engines/context-detection.js +0 -264
- package/bin/runners/lib/engines/database-patterns-engine.js +0 -429
- package/bin/runners/lib/engines/duplicate-code-engine.js +0 -354
- package/bin/runners/lib/engines/env-variables-engine.js +0 -458
- package/bin/runners/lib/engines/error-handling-engine.js +0 -437
- package/bin/runners/lib/engines/false-positive-prevention.js +0 -630
- package/bin/runners/lib/engines/framework-adapters/index.js +0 -607
- package/bin/runners/lib/engines/framework-detection.js +0 -508
- package/bin/runners/lib/engines/import-order-engine.js +0 -429
- package/bin/runners/lib/engines/naming-conventions-engine.js +0 -544
- package/bin/runners/lib/engines/noise-reduction-engine.js +0 -452
- package/bin/runners/lib/engines/orchestrator.js +0 -334
- package/bin/runners/lib/engines/react-patterns-engine.js +0 -457
- package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +0 -806
- package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +0 -577
- package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +0 -543
- package/bin/runners/lib/engines/vibecheck-engines.js +0 -514
- package/bin/runners/lib/enhanced-features/index.js +0 -305
- package/bin/runners/lib/enhanced-output.js +0 -631
- package/bin/runners/lib/enterprise.js +0 -300
- package/bin/runners/lib/firewall/command-validator.js +0 -351
- package/bin/runners/lib/firewall/config.js +0 -341
- package/bin/runners/lib/firewall/content-validator.js +0 -519
- package/bin/runners/lib/firewall/index.js +0 -101
- package/bin/runners/lib/firewall/path-validator.js +0 -256
- package/bin/runners/lib/intelligence/cross-repo-intelligence.js +0 -817
- package/bin/runners/lib/mcp-utils.js +0 -425
- package/bin/runners/lib/output/index.js +0 -1022
- package/bin/runners/lib/policy-engine.js +0 -652
- package/bin/runners/lib/polish/autofix/accessibility-fixes.js +0 -333
- package/bin/runners/lib/polish/autofix/async-handlers.js +0 -273
- package/bin/runners/lib/polish/autofix/dead-code.js +0 -280
- package/bin/runners/lib/polish/autofix/imports-optimizer.js +0 -344
- package/bin/runners/lib/polish/autofix/index.js +0 -200
- package/bin/runners/lib/polish/autofix/remove-consoles.js +0 -209
- package/bin/runners/lib/polish/autofix/strengthen-types.js +0 -245
- package/bin/runners/lib/polish/backend-checks.js +0 -148
- package/bin/runners/lib/polish/documentation-checks.js +0 -111
- package/bin/runners/lib/polish/frontend-checks.js +0 -168
- package/bin/runners/lib/polish/index.js +0 -71
- package/bin/runners/lib/polish/infrastructure-checks.js +0 -131
- package/bin/runners/lib/polish/library-detection.js +0 -175
- package/bin/runners/lib/polish/performance-checks.js +0 -100
- package/bin/runners/lib/polish/security-checks.js +0 -148
- package/bin/runners/lib/polish/utils.js +0 -203
- package/bin/runners/lib/prompt-builder.js +0 -540
- package/bin/runners/lib/proof-certificate.js +0 -634
- package/bin/runners/lib/reality/accessibility-audit.js +0 -946
- package/bin/runners/lib/reality/api-contract-validator.js +0 -1012
- package/bin/runners/lib/reality/chaos-engineering.js +0 -1084
- package/bin/runners/lib/reality/performance-tracker.js +0 -1077
- package/bin/runners/lib/reality/scenario-generator.js +0 -1404
- package/bin/runners/lib/reality/visual-regression.js +0 -852
- package/bin/runners/lib/reality-profiler.js +0 -717
- package/bin/runners/lib/replay/flight-recorder-viewer.js +0 -1160
- package/bin/runners/lib/review/ai-code-review.js +0 -832
- package/bin/runners/lib/rules/custom-rule-engine.js +0 -985
- package/bin/runners/lib/sbom-generator.js +0 -641
- package/bin/runners/lib/scan-output-enhanced.js +0 -512
- package/bin/runners/lib/security/owasp-scanner.js +0 -939
- package/bin/runners/lib/validators/contract-validator.js +0 -283
- package/bin/runners/lib/validators/dead-export-detector.js +0 -279
- package/bin/runners/lib/validators/dep-audit.js +0 -245
- package/bin/runners/lib/validators/env-validator.js +0 -319
- package/bin/runners/lib/validators/index.js +0 -120
- package/bin/runners/lib/validators/license-checker.js +0 -252
- package/bin/runners/lib/validators/route-validator.js +0 -290
- package/bin/runners/runAuthority.js +0 -528
- package/bin/runners/runConductor.js +0 -772
- package/bin/runners/runContainer.js +0 -366
- package/bin/runners/runEasy.js +0 -410
- package/bin/runners/runIaC.js +0 -372
- package/bin/runners/runVibe.js +0 -791
- package/mcp-server/tools.js +0 -495
|
@@ -1,849 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Learning Firewall Engine
|
|
3
|
-
*
|
|
4
|
-
* ═══════════════════════════════════════════════════════════════════════════════
|
|
5
|
-
* COMPETITIVE MOAT FEATURE - Self-Improving Violation Detection
|
|
6
|
-
* ═══════════════════════════════════════════════════════════════════════════════
|
|
7
|
-
*
|
|
8
|
-
* This engine learns from user feedback to improve detection accuracy over time.
|
|
9
|
-
* It tracks:
|
|
10
|
-
* - False positives that users dismiss
|
|
11
|
-
* - True positives that users fix
|
|
12
|
-
* - Patterns that lead to violations
|
|
13
|
-
* - Context that causes false detections
|
|
14
|
-
*
|
|
15
|
-
* The learning system:
|
|
16
|
-
* 1. Adjusts confidence scores based on historical accuracy
|
|
17
|
-
* 2. Learns code patterns that are commonly dismissed
|
|
18
|
-
* 3. Adapts to team/project-specific conventions
|
|
19
|
-
* 4. Shares anonymized learnings across the ecosystem (opt-in)
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
"use strict";
|
|
23
|
-
|
|
24
|
-
const fs = require("fs");
|
|
25
|
-
const path = require("path");
|
|
26
|
-
const crypto = require("crypto");
|
|
27
|
-
|
|
28
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
29
|
-
// LEARNING DATABASE SCHEMA
|
|
30
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
31
|
-
|
|
32
|
-
const DEFAULT_LEARNING_DB = {
|
|
33
|
-
version: "1.0.0",
|
|
34
|
-
created: new Date().toISOString(),
|
|
35
|
-
updated: new Date().toISOString(),
|
|
36
|
-
|
|
37
|
-
// Feedback on individual violations
|
|
38
|
-
feedback: [],
|
|
39
|
-
|
|
40
|
-
// Aggregated pattern statistics
|
|
41
|
-
patterns: {
|
|
42
|
-
// Pattern hash -> { occurrences, dismissed, fixed, confidence_adjustment }
|
|
43
|
-
},
|
|
44
|
-
|
|
45
|
-
// Rule-level statistics
|
|
46
|
-
rules: {
|
|
47
|
-
// Rule ID -> { total, false_positives, true_positives, accuracy }
|
|
48
|
-
},
|
|
49
|
-
|
|
50
|
-
// File path pattern statistics
|
|
51
|
-
pathPatterns: {
|
|
52
|
-
// Pattern -> { violations, dismissals, fixes }
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
// Team-specific conventions learned
|
|
56
|
-
conventions: {
|
|
57
|
-
// Convention hash -> { pattern, context, approved, reason }
|
|
58
|
-
},
|
|
59
|
-
|
|
60
|
-
// Model performance tracking
|
|
61
|
-
performance: {
|
|
62
|
-
totalPredictions: 0,
|
|
63
|
-
correctPredictions: 0,
|
|
64
|
-
falsePositives: 0,
|
|
65
|
-
falseNegatives: 0,
|
|
66
|
-
accuracy: 0,
|
|
67
|
-
precision: 0,
|
|
68
|
-
recall: 0,
|
|
69
|
-
f1Score: 0
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
74
|
-
// FEEDBACK TYPES
|
|
75
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
76
|
-
|
|
77
|
-
const FEEDBACK_TYPE = {
|
|
78
|
-
DISMISS: "dismiss", // User dismissed as false positive
|
|
79
|
-
FIX: "fix", // User fixed the violation
|
|
80
|
-
OVERRIDE: "override", // User approved despite violation
|
|
81
|
-
BLOCK_CORRECT: "block_ok", // User confirms block was correct
|
|
82
|
-
ALLOW_INCORRECT: "allow_bad" // User reports missed violation
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
const FEEDBACK_WEIGHT = {
|
|
86
|
-
[FEEDBACK_TYPE.DISMISS]: -0.1, // Reduce confidence
|
|
87
|
-
[FEEDBACK_TYPE.FIX]: 0.05, // Slightly increase (was correct)
|
|
88
|
-
[FEEDBACK_TYPE.OVERRIDE]: -0.15, // Stronger reduction
|
|
89
|
-
[FEEDBACK_TYPE.BLOCK_CORRECT]: 0.1, // Increase confidence
|
|
90
|
-
[FEEDBACK_TYPE.ALLOW_INCORRECT]: 0.2 // Should have blocked
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
94
|
-
// LEARNING ENGINE CLASS
|
|
95
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
96
|
-
|
|
97
|
-
class LearningEngine {
|
|
98
|
-
constructor(options = {}) {
|
|
99
|
-
this.projectRoot = options.projectRoot || process.cwd();
|
|
100
|
-
this.dbPath = options.dbPath || path.join(this.projectRoot, ".vibecheck", "learning", "firewall-learning.json");
|
|
101
|
-
this.enableCloudSync = options.enableCloudSync || false;
|
|
102
|
-
this.minSamplesForAdjustment = options.minSamplesForAdjustment || 5;
|
|
103
|
-
this.maxConfidenceAdjustment = options.maxConfidenceAdjustment || 0.3;
|
|
104
|
-
this.decayFactor = options.decayFactor || 0.95; // Older feedback matters less
|
|
105
|
-
|
|
106
|
-
this.db = this.loadDatabase();
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Load or initialize the learning database
|
|
111
|
-
*/
|
|
112
|
-
loadDatabase() {
|
|
113
|
-
try {
|
|
114
|
-
if (fs.existsSync(this.dbPath)) {
|
|
115
|
-
const data = JSON.parse(fs.readFileSync(this.dbPath, "utf8"));
|
|
116
|
-
return this.migrateDatabase(data);
|
|
117
|
-
}
|
|
118
|
-
} catch (error) {
|
|
119
|
-
// Log but don't crash - start fresh
|
|
120
|
-
}
|
|
121
|
-
return { ...DEFAULT_LEARNING_DB };
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Migrate database to latest version
|
|
126
|
-
*/
|
|
127
|
-
migrateDatabase(data) {
|
|
128
|
-
// Add any missing fields from newer versions
|
|
129
|
-
return {
|
|
130
|
-
...DEFAULT_LEARNING_DB,
|
|
131
|
-
...data,
|
|
132
|
-
patterns: { ...DEFAULT_LEARNING_DB.patterns, ...data.patterns },
|
|
133
|
-
rules: { ...DEFAULT_LEARNING_DB.rules, ...data.rules },
|
|
134
|
-
pathPatterns: { ...DEFAULT_LEARNING_DB.pathPatterns, ...data.pathPatterns },
|
|
135
|
-
conventions: { ...DEFAULT_LEARNING_DB.conventions, ...data.conventions },
|
|
136
|
-
performance: { ...DEFAULT_LEARNING_DB.performance, ...data.performance }
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Save the learning database
|
|
142
|
-
*/
|
|
143
|
-
saveDatabase() {
|
|
144
|
-
try {
|
|
145
|
-
const dir = path.dirname(this.dbPath);
|
|
146
|
-
if (!fs.existsSync(dir)) {
|
|
147
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
this.db.updated = new Date().toISOString();
|
|
151
|
-
fs.writeFileSync(this.dbPath, JSON.stringify(this.db, null, 2), "utf8");
|
|
152
|
-
} catch (error) {
|
|
153
|
-
// Log but don't crash
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
158
|
-
// FEEDBACK RECORDING
|
|
159
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Record feedback for a violation
|
|
163
|
-
*/
|
|
164
|
-
recordFeedback({
|
|
165
|
-
violationId,
|
|
166
|
-
ruleId,
|
|
167
|
-
feedbackType,
|
|
168
|
-
filePath,
|
|
169
|
-
codePattern,
|
|
170
|
-
context,
|
|
171
|
-
reason,
|
|
172
|
-
userId
|
|
173
|
-
}) {
|
|
174
|
-
const feedback = {
|
|
175
|
-
id: this.generateId(),
|
|
176
|
-
violationId,
|
|
177
|
-
ruleId,
|
|
178
|
-
feedbackType,
|
|
179
|
-
filePath,
|
|
180
|
-
codePattern: this.normalizePattern(codePattern),
|
|
181
|
-
patternHash: this.hashPattern(codePattern),
|
|
182
|
-
context,
|
|
183
|
-
reason,
|
|
184
|
-
userId: userId ? this.hashUserId(userId) : null,
|
|
185
|
-
timestamp: new Date().toISOString()
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
// Store feedback
|
|
189
|
-
this.db.feedback.push(feedback);
|
|
190
|
-
|
|
191
|
-
// Update pattern statistics
|
|
192
|
-
this.updatePatternStats(feedback);
|
|
193
|
-
|
|
194
|
-
// Update rule statistics
|
|
195
|
-
this.updateRuleStats(feedback);
|
|
196
|
-
|
|
197
|
-
// Update path pattern statistics
|
|
198
|
-
this.updatePathStats(feedback);
|
|
199
|
-
|
|
200
|
-
// Recalculate performance metrics
|
|
201
|
-
this.updatePerformanceMetrics(feedback);
|
|
202
|
-
|
|
203
|
-
// Check if this creates a new convention
|
|
204
|
-
this.detectConvention(feedback);
|
|
205
|
-
|
|
206
|
-
// Persist changes
|
|
207
|
-
this.saveDatabase();
|
|
208
|
-
|
|
209
|
-
// Optionally sync to cloud
|
|
210
|
-
if (this.enableCloudSync) {
|
|
211
|
-
this.syncToCloud(feedback);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
return feedback;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Update pattern-level statistics
|
|
219
|
-
*/
|
|
220
|
-
updatePatternStats(feedback) {
|
|
221
|
-
const { patternHash, feedbackType } = feedback;
|
|
222
|
-
|
|
223
|
-
if (!this.db.patterns[patternHash]) {
|
|
224
|
-
this.db.patterns[patternHash] = {
|
|
225
|
-
pattern: feedback.codePattern,
|
|
226
|
-
occurrences: 0,
|
|
227
|
-
dismissed: 0,
|
|
228
|
-
fixed: 0,
|
|
229
|
-
overridden: 0,
|
|
230
|
-
confidenceAdjustment: 0,
|
|
231
|
-
firstSeen: feedback.timestamp,
|
|
232
|
-
lastSeen: feedback.timestamp
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const stats = this.db.patterns[patternHash];
|
|
237
|
-
stats.occurrences++;
|
|
238
|
-
stats.lastSeen = feedback.timestamp;
|
|
239
|
-
|
|
240
|
-
switch (feedbackType) {
|
|
241
|
-
case FEEDBACK_TYPE.DISMISS:
|
|
242
|
-
stats.dismissed++;
|
|
243
|
-
break;
|
|
244
|
-
case FEEDBACK_TYPE.FIX:
|
|
245
|
-
stats.fixed++;
|
|
246
|
-
break;
|
|
247
|
-
case FEEDBACK_TYPE.OVERRIDE:
|
|
248
|
-
stats.overridden++;
|
|
249
|
-
break;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// Calculate confidence adjustment
|
|
253
|
-
stats.confidenceAdjustment = this.calculateConfidenceAdjustment(stats);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Update rule-level statistics
|
|
258
|
-
*/
|
|
259
|
-
updateRuleStats(feedback) {
|
|
260
|
-
const { ruleId, feedbackType } = feedback;
|
|
261
|
-
|
|
262
|
-
if (!this.db.rules[ruleId]) {
|
|
263
|
-
this.db.rules[ruleId] = {
|
|
264
|
-
total: 0,
|
|
265
|
-
truePositives: 0,
|
|
266
|
-
falsePositives: 0,
|
|
267
|
-
accuracy: 1.0,
|
|
268
|
-
confidenceMultiplier: 1.0
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
const stats = this.db.rules[ruleId];
|
|
273
|
-
stats.total++;
|
|
274
|
-
|
|
275
|
-
switch (feedbackType) {
|
|
276
|
-
case FEEDBACK_TYPE.FIX:
|
|
277
|
-
case FEEDBACK_TYPE.BLOCK_CORRECT:
|
|
278
|
-
stats.truePositives++;
|
|
279
|
-
break;
|
|
280
|
-
case FEEDBACK_TYPE.DISMISS:
|
|
281
|
-
case FEEDBACK_TYPE.OVERRIDE:
|
|
282
|
-
stats.falsePositives++;
|
|
283
|
-
break;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Calculate accuracy
|
|
287
|
-
if (stats.total >= this.minSamplesForAdjustment) {
|
|
288
|
-
stats.accuracy = stats.truePositives / stats.total;
|
|
289
|
-
stats.confidenceMultiplier = 0.5 + (stats.accuracy * 0.5); // Range: 0.5 to 1.0
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* Update path pattern statistics
|
|
295
|
-
*/
|
|
296
|
-
updatePathStats(feedback) {
|
|
297
|
-
const pathPattern = this.extractPathPattern(feedback.filePath);
|
|
298
|
-
|
|
299
|
-
if (!this.db.pathPatterns[pathPattern]) {
|
|
300
|
-
this.db.pathPatterns[pathPattern] = {
|
|
301
|
-
violations: 0,
|
|
302
|
-
dismissals: 0,
|
|
303
|
-
fixes: 0,
|
|
304
|
-
shouldSkip: false
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const stats = this.db.pathPatterns[pathPattern];
|
|
309
|
-
stats.violations++;
|
|
310
|
-
|
|
311
|
-
switch (feedback.feedbackType) {
|
|
312
|
-
case FEEDBACK_TYPE.DISMISS:
|
|
313
|
-
case FEEDBACK_TYPE.OVERRIDE:
|
|
314
|
-
stats.dismissals++;
|
|
315
|
-
break;
|
|
316
|
-
case FEEDBACK_TYPE.FIX:
|
|
317
|
-
stats.fixes++;
|
|
318
|
-
break;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
// If dismissal rate is very high, mark as should skip
|
|
322
|
-
if (stats.violations >= this.minSamplesForAdjustment) {
|
|
323
|
-
stats.shouldSkip = (stats.dismissals / stats.violations) > 0.8;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* Calculate confidence adjustment for a pattern
|
|
329
|
-
*/
|
|
330
|
-
calculateConfidenceAdjustment(stats) {
|
|
331
|
-
if (stats.occurrences < this.minSamplesForAdjustment) {
|
|
332
|
-
return 0; // Not enough data
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// Calculate dismissal rate
|
|
336
|
-
const dismissalRate = (stats.dismissed + stats.overridden) / stats.occurrences;
|
|
337
|
-
const fixRate = stats.fixed / stats.occurrences;
|
|
338
|
-
|
|
339
|
-
// Adjust confidence based on historical accuracy
|
|
340
|
-
// High dismissal rate = lower confidence
|
|
341
|
-
// High fix rate = higher confidence
|
|
342
|
-
let adjustment = (fixRate - dismissalRate) * 0.2;
|
|
343
|
-
|
|
344
|
-
// Clamp to max adjustment
|
|
345
|
-
adjustment = Math.max(-this.maxConfidenceAdjustment,
|
|
346
|
-
Math.min(this.maxConfidenceAdjustment, adjustment));
|
|
347
|
-
|
|
348
|
-
return adjustment;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
352
|
-
// CONFIDENCE ADJUSTMENT API
|
|
353
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* Get adjusted confidence for a violation
|
|
357
|
-
*/
|
|
358
|
-
getAdjustedConfidence(originalConfidence, {
|
|
359
|
-
ruleId,
|
|
360
|
-
codePattern,
|
|
361
|
-
filePath
|
|
362
|
-
}) {
|
|
363
|
-
let adjustment = 0;
|
|
364
|
-
|
|
365
|
-
// Apply pattern-based adjustment
|
|
366
|
-
const patternHash = this.hashPattern(codePattern);
|
|
367
|
-
if (this.db.patterns[patternHash]) {
|
|
368
|
-
adjustment += this.db.patterns[patternHash].confidenceAdjustment;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
// Apply rule-based multiplier
|
|
372
|
-
if (this.db.rules[ruleId]) {
|
|
373
|
-
const ruleStats = this.db.rules[ruleId];
|
|
374
|
-
if (ruleStats.total >= this.minSamplesForAdjustment) {
|
|
375
|
-
adjustment += (ruleStats.confidenceMultiplier - 1) * 0.2;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// Apply path-based adjustment
|
|
380
|
-
const pathPattern = this.extractPathPattern(filePath);
|
|
381
|
-
if (this.db.pathPatterns[pathPattern]?.shouldSkip) {
|
|
382
|
-
adjustment -= 0.3; // Significantly reduce for commonly dismissed paths
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// Calculate final confidence
|
|
386
|
-
let finalConfidence = originalConfidence + adjustment;
|
|
387
|
-
|
|
388
|
-
// Clamp to valid range
|
|
389
|
-
finalConfidence = Math.max(0, Math.min(1, finalConfidence));
|
|
390
|
-
|
|
391
|
-
return {
|
|
392
|
-
original: originalConfidence,
|
|
393
|
-
adjusted: finalConfidence,
|
|
394
|
-
adjustment,
|
|
395
|
-
factors: {
|
|
396
|
-
pattern: this.db.patterns[patternHash]?.confidenceAdjustment || 0,
|
|
397
|
-
rule: this.db.rules[ruleId]?.confidenceMultiplier || 1,
|
|
398
|
-
path: this.db.pathPatterns[pathPattern]?.shouldSkip ? -0.3 : 0
|
|
399
|
-
}
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
/**
|
|
404
|
-
* Check if a violation should be auto-dismissed
|
|
405
|
-
*/
|
|
406
|
-
shouldAutoDismiss({
|
|
407
|
-
ruleId,
|
|
408
|
-
codePattern,
|
|
409
|
-
filePath,
|
|
410
|
-
confidenceThreshold = 0.4
|
|
411
|
-
}) {
|
|
412
|
-
const patternHash = this.hashPattern(codePattern);
|
|
413
|
-
const patternStats = this.db.patterns[patternHash];
|
|
414
|
-
const ruleStats = this.db.rules[ruleId];
|
|
415
|
-
const pathStats = this.db.pathPatterns[this.extractPathPattern(filePath)];
|
|
416
|
-
|
|
417
|
-
// Auto-dismiss if pattern is almost always dismissed
|
|
418
|
-
if (patternStats && patternStats.occurrences >= 10) {
|
|
419
|
-
const dismissRate = (patternStats.dismissed + patternStats.overridden) / patternStats.occurrences;
|
|
420
|
-
if (dismissRate > 0.9) {
|
|
421
|
-
return {
|
|
422
|
-
dismiss: true,
|
|
423
|
-
reason: `Pattern dismissed ${Math.round(dismissRate * 100)}% of the time (${patternStats.occurrences} occurrences)`,
|
|
424
|
-
confidence: 1 - dismissRate
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// Auto-dismiss if rule has very high false positive rate
|
|
430
|
-
if (ruleStats && ruleStats.total >= 20 && ruleStats.accuracy < 0.3) {
|
|
431
|
-
return {
|
|
432
|
-
dismiss: true,
|
|
433
|
-
reason: `Rule ${ruleId} has only ${Math.round(ruleStats.accuracy * 100)}% accuracy`,
|
|
434
|
-
confidence: ruleStats.accuracy
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
// Auto-dismiss if path pattern is commonly dismissed
|
|
439
|
-
if (pathStats?.shouldSkip) {
|
|
440
|
-
return {
|
|
441
|
-
dismiss: true,
|
|
442
|
-
reason: `Files matching this pattern are commonly dismissed`,
|
|
443
|
-
confidence: 0.2
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
return { dismiss: false };
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
451
|
-
// CONVENTION LEARNING
|
|
452
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
453
|
-
|
|
454
|
-
/**
|
|
455
|
-
* Detect if feedback creates a new team convention
|
|
456
|
-
*/
|
|
457
|
-
detectConvention(feedback) {
|
|
458
|
-
// Check if this pattern is consistently overridden with a reason
|
|
459
|
-
if (feedback.feedbackType === FEEDBACK_TYPE.OVERRIDE && feedback.reason) {
|
|
460
|
-
const patternHash = feedback.patternHash;
|
|
461
|
-
const patternStats = this.db.patterns[patternHash];
|
|
462
|
-
|
|
463
|
-
if (patternStats && patternStats.overridden >= 3) {
|
|
464
|
-
// Multiple overrides with reasons = potential convention
|
|
465
|
-
const conventionId = `${feedback.ruleId}-${patternHash}`;
|
|
466
|
-
|
|
467
|
-
if (!this.db.conventions[conventionId]) {
|
|
468
|
-
this.db.conventions[conventionId] = {
|
|
469
|
-
pattern: feedback.codePattern,
|
|
470
|
-
ruleId: feedback.ruleId,
|
|
471
|
-
context: feedback.context,
|
|
472
|
-
reasons: [],
|
|
473
|
-
occurrences: 0,
|
|
474
|
-
approved: false,
|
|
475
|
-
createdAt: feedback.timestamp
|
|
476
|
-
};
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
const convention = this.db.conventions[conventionId];
|
|
480
|
-
convention.occurrences++;
|
|
481
|
-
convention.reasons.push(feedback.reason);
|
|
482
|
-
convention.lastUpdated = feedback.timestamp;
|
|
483
|
-
|
|
484
|
-
// Auto-approve if consistent pattern
|
|
485
|
-
if (convention.occurrences >= 5 && !convention.approved) {
|
|
486
|
-
convention.approved = true;
|
|
487
|
-
convention.approvedAt = feedback.timestamp;
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* Get approved conventions that match a violation
|
|
495
|
-
*/
|
|
496
|
-
getMatchingConventions(ruleId, codePattern) {
|
|
497
|
-
const patternHash = this.hashPattern(codePattern);
|
|
498
|
-
const conventionId = `${ruleId}-${patternHash}`;
|
|
499
|
-
|
|
500
|
-
const convention = this.db.conventions[conventionId];
|
|
501
|
-
if (convention?.approved) {
|
|
502
|
-
return [convention];
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
// Also check for similar patterns
|
|
506
|
-
const similar = [];
|
|
507
|
-
for (const [id, conv] of Object.entries(this.db.conventions)) {
|
|
508
|
-
if (conv.approved && conv.ruleId === ruleId) {
|
|
509
|
-
const similarity = this.calculatePatternSimilarity(codePattern, conv.pattern);
|
|
510
|
-
if (similarity > 0.8) {
|
|
511
|
-
similar.push({ ...conv, similarity });
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
return similar.sort((a, b) => b.similarity - a.similarity);
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
520
|
-
// PERFORMANCE TRACKING
|
|
521
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
522
|
-
|
|
523
|
-
/**
|
|
524
|
-
* Update global performance metrics
|
|
525
|
-
*/
|
|
526
|
-
updatePerformanceMetrics(feedback) {
|
|
527
|
-
const perf = this.db.performance;
|
|
528
|
-
perf.totalPredictions++;
|
|
529
|
-
|
|
530
|
-
switch (feedback.feedbackType) {
|
|
531
|
-
case FEEDBACK_TYPE.FIX:
|
|
532
|
-
case FEEDBACK_TYPE.BLOCK_CORRECT:
|
|
533
|
-
perf.correctPredictions++;
|
|
534
|
-
break;
|
|
535
|
-
case FEEDBACK_TYPE.DISMISS:
|
|
536
|
-
case FEEDBACK_TYPE.OVERRIDE:
|
|
537
|
-
perf.falsePositives++;
|
|
538
|
-
break;
|
|
539
|
-
case FEEDBACK_TYPE.ALLOW_INCORRECT:
|
|
540
|
-
perf.falseNegatives++;
|
|
541
|
-
break;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
// Calculate metrics
|
|
545
|
-
const tp = perf.correctPredictions;
|
|
546
|
-
const fp = perf.falsePositives;
|
|
547
|
-
const fn = perf.falseNegatives;
|
|
548
|
-
|
|
549
|
-
perf.accuracy = perf.totalPredictions > 0
|
|
550
|
-
? perf.correctPredictions / perf.totalPredictions
|
|
551
|
-
: 0;
|
|
552
|
-
|
|
553
|
-
perf.precision = (tp + fp) > 0 ? tp / (tp + fp) : 0;
|
|
554
|
-
perf.recall = (tp + fn) > 0 ? tp / (tp + fn) : 0;
|
|
555
|
-
perf.f1Score = (perf.precision + perf.recall) > 0
|
|
556
|
-
? 2 * (perf.precision * perf.recall) / (perf.precision + perf.recall)
|
|
557
|
-
: 0;
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
/**
|
|
561
|
-
* Get performance report
|
|
562
|
-
*/
|
|
563
|
-
getPerformanceReport() {
|
|
564
|
-
const perf = this.db.performance;
|
|
565
|
-
|
|
566
|
-
return {
|
|
567
|
-
totalPredictions: perf.totalPredictions,
|
|
568
|
-
accuracy: (perf.accuracy * 100).toFixed(1) + "%",
|
|
569
|
-
precision: (perf.precision * 100).toFixed(1) + "%",
|
|
570
|
-
recall: (perf.recall * 100).toFixed(1) + "%",
|
|
571
|
-
f1Score: (perf.f1Score * 100).toFixed(1) + "%",
|
|
572
|
-
falsePositiveRate: perf.totalPredictions > 0
|
|
573
|
-
? ((perf.falsePositives / perf.totalPredictions) * 100).toFixed(1) + "%"
|
|
574
|
-
: "N/A",
|
|
575
|
-
topFalsePositivePatterns: this.getTopFalsePositivePatterns(5),
|
|
576
|
-
topFalsePositiveRules: this.getTopFalsePositiveRules(5),
|
|
577
|
-
conventionsLearned: Object.values(this.db.conventions).filter(c => c.approved).length
|
|
578
|
-
};
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
/**
|
|
582
|
-
* Get patterns with highest false positive rates
|
|
583
|
-
*/
|
|
584
|
-
getTopFalsePositivePatterns(limit) {
|
|
585
|
-
const patterns = Object.entries(this.db.patterns)
|
|
586
|
-
.filter(([, stats]) => stats.occurrences >= this.minSamplesForAdjustment)
|
|
587
|
-
.map(([hash, stats]) => ({
|
|
588
|
-
hash,
|
|
589
|
-
pattern: stats.pattern,
|
|
590
|
-
fpRate: (stats.dismissed + stats.overridden) / stats.occurrences,
|
|
591
|
-
occurrences: stats.occurrences
|
|
592
|
-
}))
|
|
593
|
-
.sort((a, b) => b.fpRate - a.fpRate)
|
|
594
|
-
.slice(0, limit);
|
|
595
|
-
|
|
596
|
-
return patterns;
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
/**
|
|
600
|
-
* Get rules with highest false positive rates
|
|
601
|
-
*/
|
|
602
|
-
getTopFalsePositiveRules(limit) {
|
|
603
|
-
const rules = Object.entries(this.db.rules)
|
|
604
|
-
.filter(([, stats]) => stats.total >= this.minSamplesForAdjustment)
|
|
605
|
-
.map(([ruleId, stats]) => ({
|
|
606
|
-
ruleId,
|
|
607
|
-
fpRate: stats.falsePositives / stats.total,
|
|
608
|
-
total: stats.total,
|
|
609
|
-
accuracy: stats.accuracy
|
|
610
|
-
}))
|
|
611
|
-
.sort((a, b) => b.fpRate - a.fpRate)
|
|
612
|
-
.slice(0, limit);
|
|
613
|
-
|
|
614
|
-
return rules;
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
618
|
-
// UTILITY FUNCTIONS
|
|
619
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
620
|
-
|
|
621
|
-
/**
|
|
622
|
-
* Generate unique ID
|
|
623
|
-
*/
|
|
624
|
-
generateId() {
|
|
625
|
-
return crypto.randomBytes(8).toString("hex");
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
/**
|
|
629
|
-
* Hash a code pattern for deduplication
|
|
630
|
-
*/
|
|
631
|
-
hashPattern(pattern) {
|
|
632
|
-
if (!pattern) return "empty";
|
|
633
|
-
const normalized = this.normalizePattern(pattern);
|
|
634
|
-
return crypto.createHash("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
/**
|
|
638
|
-
* Normalize a code pattern (remove whitespace, variable names, etc.)
|
|
639
|
-
*/
|
|
640
|
-
normalizePattern(pattern) {
|
|
641
|
-
if (!pattern) return "";
|
|
642
|
-
|
|
643
|
-
return pattern
|
|
644
|
-
.replace(/\s+/g, " ") // Normalize whitespace
|
|
645
|
-
.replace(/["'`].*?["'`]/g, '""') // Normalize strings
|
|
646
|
-
.replace(/\b\d+\b/g, "0") // Normalize numbers
|
|
647
|
-
.replace(/\b[a-z_][a-z0-9_]*\b/gi, "ID") // Normalize identifiers
|
|
648
|
-
.trim();
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
/**
|
|
652
|
-
* Extract a generalized path pattern from a file path
|
|
653
|
-
*/
|
|
654
|
-
extractPathPattern(filePath) {
|
|
655
|
-
if (!filePath) return "unknown";
|
|
656
|
-
|
|
657
|
-
// Extract meaningful parts: directory structure + file type
|
|
658
|
-
const parts = filePath.replace(/\\/g, "/").split("/");
|
|
659
|
-
const fileName = parts.pop();
|
|
660
|
-
const ext = path.extname(fileName);
|
|
661
|
-
|
|
662
|
-
// Check for common patterns
|
|
663
|
-
if (/test|spec|__tests__|__mocks__/i.test(filePath)) {
|
|
664
|
-
return `**/*test*${ext}`;
|
|
665
|
-
}
|
|
666
|
-
if (/\.d\.ts$/.test(fileName)) {
|
|
667
|
-
return "**/*.d.ts";
|
|
668
|
-
}
|
|
669
|
-
if (/generated|__generated__|\.generated\./i.test(filePath)) {
|
|
670
|
-
return "**/generated/**";
|
|
671
|
-
}
|
|
672
|
-
if (/config|\.config\./i.test(fileName)) {
|
|
673
|
-
return `**/config*${ext}`;
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
// Return directory pattern + extension
|
|
677
|
-
const lastDir = parts.filter(p => p && p !== ".").pop() || "";
|
|
678
|
-
return `**/${lastDir}/*${ext}`;
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
/**
|
|
682
|
-
* Calculate similarity between two patterns
|
|
683
|
-
*/
|
|
684
|
-
calculatePatternSimilarity(pattern1, pattern2) {
|
|
685
|
-
const norm1 = this.normalizePattern(pattern1);
|
|
686
|
-
const norm2 = this.normalizePattern(pattern2);
|
|
687
|
-
|
|
688
|
-
if (norm1 === norm2) return 1;
|
|
689
|
-
|
|
690
|
-
// Levenshtein-based similarity
|
|
691
|
-
const maxLen = Math.max(norm1.length, norm2.length);
|
|
692
|
-
if (maxLen === 0) return 1;
|
|
693
|
-
|
|
694
|
-
const distance = this.levenshteinDistance(norm1, norm2);
|
|
695
|
-
return 1 - (distance / maxLen);
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
/**
|
|
699
|
-
* Calculate Levenshtein distance
|
|
700
|
-
*/
|
|
701
|
-
levenshteinDistance(str1, str2) {
|
|
702
|
-
const m = str1.length;
|
|
703
|
-
const n = str2.length;
|
|
704
|
-
const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
|
|
705
|
-
|
|
706
|
-
for (let i = 0; i <= m; i++) dp[i][0] = i;
|
|
707
|
-
for (let j = 0; j <= n; j++) dp[0][j] = j;
|
|
708
|
-
|
|
709
|
-
for (let i = 1; i <= m; i++) {
|
|
710
|
-
for (let j = 1; j <= n; j++) {
|
|
711
|
-
const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
|
|
712
|
-
dp[i][j] = Math.min(
|
|
713
|
-
dp[i - 1][j] + 1,
|
|
714
|
-
dp[i][j - 1] + 1,
|
|
715
|
-
dp[i - 1][j - 1] + cost
|
|
716
|
-
);
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
return dp[m][n];
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
/**
|
|
724
|
-
* Hash user ID for privacy
|
|
725
|
-
*/
|
|
726
|
-
hashUserId(userId) {
|
|
727
|
-
return crypto.createHash("sha256").update(userId).digest("hex").slice(0, 12);
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
/**
|
|
731
|
-
* Sync feedback to cloud (placeholder for ecosystem learning)
|
|
732
|
-
*/
|
|
733
|
-
async syncToCloud(feedback) {
|
|
734
|
-
// Would send anonymized feedback to cloud service
|
|
735
|
-
// for cross-project learning (opt-in only)
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
739
|
-
// EXPORT/IMPORT
|
|
740
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
741
|
-
|
|
742
|
-
/**
|
|
743
|
-
* Export learning data for sharing
|
|
744
|
-
*/
|
|
745
|
-
exportLearnings() {
|
|
746
|
-
return {
|
|
747
|
-
version: this.db.version,
|
|
748
|
-
exported: new Date().toISOString(),
|
|
749
|
-
patterns: Object.entries(this.db.patterns)
|
|
750
|
-
.filter(([, stats]) => stats.occurrences >= this.minSamplesForAdjustment)
|
|
751
|
-
.map(([hash, stats]) => ({
|
|
752
|
-
hash,
|
|
753
|
-
confidenceAdjustment: stats.confidenceAdjustment,
|
|
754
|
-
occurrences: stats.occurrences
|
|
755
|
-
})),
|
|
756
|
-
rules: Object.entries(this.db.rules)
|
|
757
|
-
.filter(([, stats]) => stats.total >= this.minSamplesForAdjustment)
|
|
758
|
-
.map(([ruleId, stats]) => ({
|
|
759
|
-
ruleId,
|
|
760
|
-
confidenceMultiplier: stats.confidenceMultiplier,
|
|
761
|
-
accuracy: stats.accuracy
|
|
762
|
-
})),
|
|
763
|
-
conventions: Object.values(this.db.conventions)
|
|
764
|
-
.filter(c => c.approved)
|
|
765
|
-
.map(c => ({
|
|
766
|
-
ruleId: c.ruleId,
|
|
767
|
-
pattern: c.pattern,
|
|
768
|
-
reasons: c.reasons.slice(0, 3) // Limit reasons
|
|
769
|
-
}))
|
|
770
|
-
};
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
/**
|
|
774
|
-
* Import learning data from another source
|
|
775
|
-
*/
|
|
776
|
-
importLearnings(data, options = {}) {
|
|
777
|
-
const { merge = true, weight = 0.5 } = options;
|
|
778
|
-
|
|
779
|
-
if (!merge) {
|
|
780
|
-
// Replace mode - use imported data directly
|
|
781
|
-
this.db = { ...DEFAULT_LEARNING_DB, ...data };
|
|
782
|
-
this.saveDatabase();
|
|
783
|
-
return;
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
// Merge mode - combine with existing data
|
|
787
|
-
for (const pattern of (data.patterns || [])) {
|
|
788
|
-
if (this.db.patterns[pattern.hash]) {
|
|
789
|
-
// Average the adjustments
|
|
790
|
-
this.db.patterns[pattern.hash].confidenceAdjustment =
|
|
791
|
-
(this.db.patterns[pattern.hash].confidenceAdjustment * (1 - weight)) +
|
|
792
|
-
(pattern.confidenceAdjustment * weight);
|
|
793
|
-
} else {
|
|
794
|
-
this.db.patterns[pattern.hash] = {
|
|
795
|
-
...DEFAULT_LEARNING_DB.patterns,
|
|
796
|
-
confidenceAdjustment: pattern.confidenceAdjustment * weight,
|
|
797
|
-
occurrences: Math.round(pattern.occurrences * weight)
|
|
798
|
-
};
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
for (const rule of (data.rules || [])) {
|
|
803
|
-
if (this.db.rules[rule.ruleId]) {
|
|
804
|
-
this.db.rules[rule.ruleId].confidenceMultiplier =
|
|
805
|
-
(this.db.rules[rule.ruleId].confidenceMultiplier * (1 - weight)) +
|
|
806
|
-
(rule.confidenceMultiplier * weight);
|
|
807
|
-
} else {
|
|
808
|
-
this.db.rules[rule.ruleId] = {
|
|
809
|
-
...DEFAULT_LEARNING_DB.rules,
|
|
810
|
-
confidenceMultiplier: rule.confidenceMultiplier
|
|
811
|
-
};
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
this.saveDatabase();
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
/**
|
|
819
|
-
* Reset all learning data
|
|
820
|
-
*/
|
|
821
|
-
reset() {
|
|
822
|
-
this.db = { ...DEFAULT_LEARNING_DB };
|
|
823
|
-
this.saveDatabase();
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
828
|
-
// SINGLETON INSTANCE
|
|
829
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
830
|
-
|
|
831
|
-
let instance = null;
|
|
832
|
-
|
|
833
|
-
function getLearningEngine(options = {}) {
|
|
834
|
-
if (!instance || options.projectRoot !== instance.projectRoot) {
|
|
835
|
-
instance = new LearningEngine(options);
|
|
836
|
-
}
|
|
837
|
-
return instance;
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
841
|
-
// EXPORTS
|
|
842
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
843
|
-
|
|
844
|
-
module.exports = {
|
|
845
|
-
LearningEngine,
|
|
846
|
-
getLearningEngine,
|
|
847
|
-
FEEDBACK_TYPE,
|
|
848
|
-
FEEDBACK_WEIGHT
|
|
849
|
-
};
|