@rigour-labs/core 3.0.5 → 4.0.0
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/dist/deep/fact-extractor.d.ts +80 -0
- package/dist/deep/fact-extractor.js +626 -0
- package/dist/deep/index.d.ts +14 -0
- package/dist/deep/index.js +12 -0
- package/dist/deep/prompts.d.ts +22 -0
- package/dist/deep/prompts.js +374 -0
- package/dist/deep/verifier.d.ts +16 -0
- package/dist/deep/verifier.js +388 -0
- package/dist/gates/deep-analysis.d.ts +28 -0
- package/dist/gates/deep-analysis.js +302 -0
- package/dist/gates/deprecated-apis-rules-lang.d.ts +21 -0
- package/dist/gates/deprecated-apis-rules-lang.js +311 -0
- package/dist/gates/deprecated-apis-rules-node.d.ts +19 -0
- package/dist/gates/deprecated-apis-rules-node.js +199 -0
- package/dist/gates/deprecated-apis-rules.d.ts +6 -0
- package/dist/gates/deprecated-apis-rules.js +6 -0
- package/dist/gates/deprecated-apis.js +1 -502
- package/dist/gates/hallucinated-imports-lang.d.ts +16 -0
- package/dist/gates/hallucinated-imports-lang.js +374 -0
- package/dist/gates/hallucinated-imports-stdlib.d.ts +12 -0
- package/dist/gates/hallucinated-imports-stdlib.js +228 -0
- package/dist/gates/hallucinated-imports.d.ts +0 -98
- package/dist/gates/hallucinated-imports.js +10 -678
- package/dist/gates/phantom-apis-data.d.ts +33 -0
- package/dist/gates/phantom-apis-data.js +398 -0
- package/dist/gates/phantom-apis.js +1 -393
- package/dist/gates/phantom-apis.test.js +52 -0
- package/dist/gates/promise-safety-helpers.d.ts +19 -0
- package/dist/gates/promise-safety-helpers.js +101 -0
- package/dist/gates/promise-safety-rules.d.ts +7 -0
- package/dist/gates/promise-safety-rules.js +19 -0
- package/dist/gates/promise-safety.d.ts +1 -21
- package/dist/gates/promise-safety.js +51 -257
- package/dist/gates/runner.d.ts +4 -2
- package/dist/gates/runner.js +46 -1
- package/dist/gates/test-quality-lang.d.ts +30 -0
- package/dist/gates/test-quality-lang.js +188 -0
- package/dist/gates/test-quality.d.ts +0 -14
- package/dist/gates/test-quality.js +13 -186
- package/dist/index.d.ts +10 -0
- package/dist/index.js +12 -2
- package/dist/inference/cloud-provider.d.ts +34 -0
- package/dist/inference/cloud-provider.js +126 -0
- package/dist/inference/index.d.ts +17 -0
- package/dist/inference/index.js +23 -0
- package/dist/inference/model-manager.d.ts +26 -0
- package/dist/inference/model-manager.js +106 -0
- package/dist/inference/sidecar-provider.d.ts +15 -0
- package/dist/inference/sidecar-provider.js +153 -0
- package/dist/inference/types.d.ts +77 -0
- package/dist/inference/types.js +19 -0
- package/dist/pattern-index/indexer-helpers.d.ts +38 -0
- package/dist/pattern-index/indexer-helpers.js +111 -0
- package/dist/pattern-index/indexer-lang.d.ts +13 -0
- package/dist/pattern-index/indexer-lang.js +244 -0
- package/dist/pattern-index/indexer-ts.d.ts +22 -0
- package/dist/pattern-index/indexer-ts.js +258 -0
- package/dist/pattern-index/indexer.d.ts +4 -106
- package/dist/pattern-index/indexer.js +58 -707
- package/dist/pattern-index/staleness-data.d.ts +6 -0
- package/dist/pattern-index/staleness-data.js +262 -0
- package/dist/pattern-index/staleness.js +1 -258
- package/dist/settings.d.ts +104 -0
- package/dist/settings.js +186 -0
- package/dist/storage/db.d.ts +16 -0
- package/dist/storage/db.js +132 -0
- package/dist/storage/findings.d.ts +14 -0
- package/dist/storage/findings.js +38 -0
- package/dist/storage/index.d.ts +9 -0
- package/dist/storage/index.js +8 -0
- package/dist/storage/patterns.d.ts +35 -0
- package/dist/storage/patterns.js +62 -0
- package/dist/storage/scans.d.ts +42 -0
- package/dist/storage/scans.js +55 -0
- package/dist/templates/index.d.ts +12 -16
- package/dist/templates/index.js +11 -527
- package/dist/templates/paradigms.d.ts +2 -0
- package/dist/templates/paradigms.js +46 -0
- package/dist/templates/presets.d.ts +14 -0
- package/dist/templates/presets.js +227 -0
- package/dist/templates/universal-config.d.ts +2 -0
- package/dist/templates/universal-config.js +190 -0
- package/dist/types/index.d.ts +438 -15
- package/dist/types/index.js +41 -1
- package/package.json +6 -2
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import { Logger } from '../utils/logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* Verify LLM findings against AST-extracted facts.
|
|
4
|
+
* Returns only findings that pass verification.
|
|
5
|
+
*/
|
|
6
|
+
export function verifyFindings(findings, facts) {
|
|
7
|
+
const factsByPath = new Map();
|
|
8
|
+
for (const f of facts) {
|
|
9
|
+
factsByPath.set(f.path, f);
|
|
10
|
+
}
|
|
11
|
+
const verified = [];
|
|
12
|
+
for (const finding of findings) {
|
|
13
|
+
const result = verifyFinding(finding, factsByPath);
|
|
14
|
+
if (result.verified) {
|
|
15
|
+
verified.push(result);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
Logger.debug(`Dropped unverified finding: ${finding.category} in ${finding.file} — ${result.verificationNotes}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return verified;
|
|
22
|
+
}
|
|
23
|
+
function verifyFinding(finding, factsByPath) {
|
|
24
|
+
// Check 1: Does the referenced file exist in facts?
|
|
25
|
+
const fileFacts = findFile(finding.file, factsByPath);
|
|
26
|
+
if (!fileFacts) {
|
|
27
|
+
return {
|
|
28
|
+
...finding,
|
|
29
|
+
verified: false,
|
|
30
|
+
verificationNotes: `File not found in analyzed files: ${finding.file}`,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
// Check 2: Category-specific verification
|
|
34
|
+
switch (finding.category) {
|
|
35
|
+
// ── Class/Struct-based categories ──
|
|
36
|
+
case 'god_class':
|
|
37
|
+
case 'srp_violation':
|
|
38
|
+
case 'ocp_violation':
|
|
39
|
+
case 'lsp_violation':
|
|
40
|
+
case 'isp_violation':
|
|
41
|
+
case 'dip_violation':
|
|
42
|
+
return verifyClassOrStructFinding(finding, fileFacts);
|
|
43
|
+
// ── Function-based categories ──
|
|
44
|
+
case 'god_function':
|
|
45
|
+
case 'long_params':
|
|
46
|
+
case 'complex_conditional':
|
|
47
|
+
return verifyFunctionFinding(finding, fileFacts);
|
|
48
|
+
// ── Error handling categories ──
|
|
49
|
+
case 'empty_catch':
|
|
50
|
+
case 'error_inconsistency':
|
|
51
|
+
case 'error_swallowing':
|
|
52
|
+
case 'missing_error_check':
|
|
53
|
+
case 'panic_in_library':
|
|
54
|
+
return verifyErrorHandlingFinding(finding, fileFacts);
|
|
55
|
+
// ── Interface categories (Go-specific) ──
|
|
56
|
+
case 'isp_violation_interface':
|
|
57
|
+
return verifyInterfaceFinding(finding, fileFacts);
|
|
58
|
+
// ── Concurrency categories ──
|
|
59
|
+
case 'race_condition':
|
|
60
|
+
case 'goroutine_leak':
|
|
61
|
+
case 'missing_context':
|
|
62
|
+
case 'channel_misuse':
|
|
63
|
+
case 'mutex_scope':
|
|
64
|
+
return verifyConcurrencyFinding(finding, fileFacts);
|
|
65
|
+
// ── Test categories ──
|
|
66
|
+
case 'test_quality':
|
|
67
|
+
case 'test_coupling':
|
|
68
|
+
case 'test_duplication':
|
|
69
|
+
case 'missing_test':
|
|
70
|
+
return verifyTestFinding(finding, fileFacts);
|
|
71
|
+
// ── File-level categories (verified by file existence + basic checks) ──
|
|
72
|
+
case 'long_file':
|
|
73
|
+
return {
|
|
74
|
+
...finding,
|
|
75
|
+
verified: fileFacts.lineCount > 300,
|
|
76
|
+
verificationNotes: fileFacts.lineCount > 300
|
|
77
|
+
? `File is ${fileFacts.lineCount} lines`
|
|
78
|
+
: `File is only ${fileFacts.lineCount} lines`,
|
|
79
|
+
};
|
|
80
|
+
case 'magic_number':
|
|
81
|
+
return {
|
|
82
|
+
...finding,
|
|
83
|
+
verified: (fileFacts.magicNumbers || 0) > 3,
|
|
84
|
+
verificationNotes: `${fileFacts.magicNumbers || 0} magic numbers detected`,
|
|
85
|
+
};
|
|
86
|
+
case 'resource_leak':
|
|
87
|
+
// For Go: check defers vs resource operations
|
|
88
|
+
if (fileFacts.language === 'go') {
|
|
89
|
+
const hasResources = fileFacts.imports.some(i => i.includes('os') || i.includes('net') || i.includes('http') || i.includes('io') || i.includes('sql'));
|
|
90
|
+
return {
|
|
91
|
+
...finding,
|
|
92
|
+
verified: hasResources && finding.confidence >= 0.4,
|
|
93
|
+
verificationNotes: hasResources ? 'File imports resource packages' : 'No resource imports found',
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return { ...finding, verified: finding.confidence >= 0.4, verificationNotes: 'Accepted on confidence' };
|
|
97
|
+
// ── Categories verified by file existence + reasonable confidence ──
|
|
98
|
+
case 'dry_violation':
|
|
99
|
+
case 'copy_paste_code':
|
|
100
|
+
case 'data_clump':
|
|
101
|
+
case 'feature_envy':
|
|
102
|
+
case 'shotgun_surgery':
|
|
103
|
+
case 'inappropriate_intimacy':
|
|
104
|
+
case 'primitive_obsession':
|
|
105
|
+
case 'lazy_class':
|
|
106
|
+
case 'speculative_generality':
|
|
107
|
+
case 'refused_bequest':
|
|
108
|
+
case 'architecture':
|
|
109
|
+
case 'circular_dependency':
|
|
110
|
+
case 'package_cohesion':
|
|
111
|
+
case 'api_design':
|
|
112
|
+
case 'missing_abstraction':
|
|
113
|
+
case 'language_idiom':
|
|
114
|
+
case 'naming_convention':
|
|
115
|
+
case 'dead_code':
|
|
116
|
+
case 'code_smell':
|
|
117
|
+
case 'performance':
|
|
118
|
+
case 'hardcoded_config':
|
|
119
|
+
return {
|
|
120
|
+
...finding,
|
|
121
|
+
verified: finding.confidence >= 0.3,
|
|
122
|
+
verificationNotes: finding.confidence < 0.3 ? 'Low confidence' : 'File exists, accepted',
|
|
123
|
+
};
|
|
124
|
+
default:
|
|
125
|
+
// Unknown category — accept if file exists and confidence is reasonable
|
|
126
|
+
return {
|
|
127
|
+
...finding,
|
|
128
|
+
verified: finding.confidence >= 0.3,
|
|
129
|
+
verificationNotes: 'Unknown category, accepted on confidence',
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Verify class OR struct-based findings.
|
|
135
|
+
* For Go: uses structs instead of classes.
|
|
136
|
+
*/
|
|
137
|
+
function verifyClassOrStructFinding(finding, facts) {
|
|
138
|
+
// Combine classes and structs for verification
|
|
139
|
+
const entities = [
|
|
140
|
+
...facts.classes.map(c => ({ name: c.name, methodCount: c.methodCount, lineCount: c.lineCount, methods: c.methods })),
|
|
141
|
+
...(facts.structs || []).map(s => ({ name: s.name, methodCount: s.methodCount, lineCount: s.lineCount, methods: s.methods })),
|
|
142
|
+
];
|
|
143
|
+
if (entities.length === 0) {
|
|
144
|
+
// No classes or structs but file exists — for Go, check if file has many functions
|
|
145
|
+
// which effectively makes it a "god module"
|
|
146
|
+
if (facts.language === 'go' && facts.functions.length >= 8) {
|
|
147
|
+
return {
|
|
148
|
+
...finding,
|
|
149
|
+
verified: true,
|
|
150
|
+
verificationNotes: `Go file with ${facts.functions.length} functions — module-level issue accepted`,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
...finding,
|
|
155
|
+
verified: false,
|
|
156
|
+
verificationNotes: 'No classes or structs found in file',
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
// Try to find the referenced entity name
|
|
160
|
+
const entityName = extractEntityName(finding.description, entities.map(e => e.name));
|
|
161
|
+
if (!entityName) {
|
|
162
|
+
// Entity not named but file has classes/structs — accept if reasonable
|
|
163
|
+
return {
|
|
164
|
+
...finding,
|
|
165
|
+
verified: entities.length > 0 && finding.confidence >= 0.4,
|
|
166
|
+
verificationNotes: entities.length > 0 ? 'File has entities, accepted' : 'No entities found',
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
const entity = entities.find(e => e.name === entityName);
|
|
170
|
+
if (!entity) {
|
|
171
|
+
return {
|
|
172
|
+
...finding,
|
|
173
|
+
verified: false,
|
|
174
|
+
verificationNotes: `Entity "${entityName}" not found in ${facts.path}`,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
// Category-specific thresholds
|
|
178
|
+
if (finding.category === 'god_class' || finding.category === 'srp_violation') {
|
|
179
|
+
if (entity.methodCount < 5 && entity.lineCount < 200) {
|
|
180
|
+
return {
|
|
181
|
+
...finding,
|
|
182
|
+
verified: false,
|
|
183
|
+
verificationNotes: `"${entityName}" has ${entity.methodCount} methods, ${entity.lineCount} lines — below god class threshold`,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
...finding,
|
|
189
|
+
verified: true,
|
|
190
|
+
verificationNotes: `"${entityName}" verified (${entity.methodCount} methods, ${entity.lineCount} lines)`,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function verifyFunctionFinding(finding, facts) {
|
|
194
|
+
const funcName = extractEntityName(finding.description, facts.functions.map(f => f.name));
|
|
195
|
+
if (!funcName) {
|
|
196
|
+
return {
|
|
197
|
+
...finding,
|
|
198
|
+
verified: facts.functions.length > 0 && finding.confidence >= 0.4,
|
|
199
|
+
verificationNotes: facts.functions.length > 0 ? 'File has functions, accepted' : 'No functions found',
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
const func = facts.functions.find(f => f.name === funcName);
|
|
203
|
+
if (!func) {
|
|
204
|
+
return {
|
|
205
|
+
...finding,
|
|
206
|
+
verified: false,
|
|
207
|
+
verificationNotes: `Function "${funcName}" not found in ${facts.path}`,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
if (finding.category === 'god_function') {
|
|
211
|
+
if (func.lineCount < 30) {
|
|
212
|
+
return {
|
|
213
|
+
...finding,
|
|
214
|
+
verified: false,
|
|
215
|
+
verificationNotes: `Function "${funcName}" is only ${func.lineCount} lines — not a god function`,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (finding.category === 'long_params') {
|
|
220
|
+
if (func.paramCount < 4) {
|
|
221
|
+
return {
|
|
222
|
+
...finding,
|
|
223
|
+
verified: false,
|
|
224
|
+
verificationNotes: `Function "${funcName}" only has ${func.paramCount} params`,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (finding.category === 'complex_conditional') {
|
|
229
|
+
if (func.maxNesting < 3) {
|
|
230
|
+
return {
|
|
231
|
+
...finding,
|
|
232
|
+
verified: false,
|
|
233
|
+
verificationNotes: `Function "${funcName}" max nesting is only ${func.maxNesting}`,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return {
|
|
238
|
+
...finding,
|
|
239
|
+
verified: true,
|
|
240
|
+
verificationNotes: `Function "${funcName}" verified (${func.lineCount} lines, ${func.paramCount} params, nesting:${func.maxNesting})`,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
function verifyErrorHandlingFinding(finding, facts) {
|
|
244
|
+
if (finding.category === 'empty_catch') {
|
|
245
|
+
if (facts.errorHandling.length === 0) {
|
|
246
|
+
return { ...finding, verified: false, verificationNotes: 'No error handling found' };
|
|
247
|
+
}
|
|
248
|
+
const hasEmpty = facts.errorHandling.some(e => e.isEmpty);
|
|
249
|
+
return {
|
|
250
|
+
...finding,
|
|
251
|
+
verified: hasEmpty,
|
|
252
|
+
verificationNotes: hasEmpty ? 'Empty catch blocks confirmed' : 'No empty catches found',
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
if (finding.category === 'error_inconsistency') {
|
|
256
|
+
const strategies = new Set(facts.errorHandling.map(e => e.strategy));
|
|
257
|
+
return {
|
|
258
|
+
...finding,
|
|
259
|
+
verified: strategies.size >= 2,
|
|
260
|
+
verificationNotes: `${strategies.size} error strategies: ${[...strategies].join(', ')}`,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
// missing_error_check, error_swallowing, panic_in_library
|
|
264
|
+
// These are harder to verify mechanically — accept on confidence + file existence
|
|
265
|
+
return {
|
|
266
|
+
...finding,
|
|
267
|
+
verified: finding.confidence >= 0.4,
|
|
268
|
+
verificationNotes: 'Accepted on confidence',
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
function verifyInterfaceFinding(finding, facts) {
|
|
272
|
+
const interfaces = facts.interfaces || [];
|
|
273
|
+
if (interfaces.length === 0) {
|
|
274
|
+
return { ...finding, verified: false, verificationNotes: 'No interfaces found' };
|
|
275
|
+
}
|
|
276
|
+
const ifaceName = extractEntityName(finding.description, interfaces.map(i => i.name));
|
|
277
|
+
if (ifaceName) {
|
|
278
|
+
const iface = interfaces.find(i => i.name === ifaceName);
|
|
279
|
+
if (iface && iface.methodCount > 5) {
|
|
280
|
+
return {
|
|
281
|
+
...finding,
|
|
282
|
+
verified: true,
|
|
283
|
+
verificationNotes: `Interface "${ifaceName}" has ${iface.methodCount} methods — ISP violation confirmed`,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
...finding,
|
|
289
|
+
verified: finding.confidence >= 0.5,
|
|
290
|
+
verificationNotes: 'Accepted on confidence',
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
function verifyConcurrencyFinding(finding, facts) {
|
|
294
|
+
const hasConcurrency = (facts.goroutines || 0) > 0
|
|
295
|
+
|| (facts.channels || 0) > 0
|
|
296
|
+
|| (facts.mutexes || 0) > 0
|
|
297
|
+
|| facts.functions.some(f => f.isAsync);
|
|
298
|
+
if (!hasConcurrency) {
|
|
299
|
+
return {
|
|
300
|
+
...finding,
|
|
301
|
+
verified: false,
|
|
302
|
+
verificationNotes: 'No concurrency constructs found in file',
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
// goroutine_leak: must have goroutines
|
|
306
|
+
if (finding.category === 'goroutine_leak' && (facts.goroutines || 0) === 0) {
|
|
307
|
+
return { ...finding, verified: false, verificationNotes: 'No goroutines found' };
|
|
308
|
+
}
|
|
309
|
+
// channel_misuse: must have channels
|
|
310
|
+
if (finding.category === 'channel_misuse' && (facts.channels || 0) === 0) {
|
|
311
|
+
return { ...finding, verified: false, verificationNotes: 'No channels found' };
|
|
312
|
+
}
|
|
313
|
+
// mutex_scope: must have mutexes
|
|
314
|
+
if (finding.category === 'mutex_scope' && (facts.mutexes || 0) === 0) {
|
|
315
|
+
return { ...finding, verified: false, verificationNotes: 'No mutex usage found' };
|
|
316
|
+
}
|
|
317
|
+
return {
|
|
318
|
+
...finding,
|
|
319
|
+
verified: finding.confidence >= 0.4,
|
|
320
|
+
verificationNotes: `Concurrency constructs present: goroutines:${facts.goroutines || 0}, channels:${facts.channels || 0}, mutexes:${facts.mutexes || 0}`,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
function verifyTestFinding(finding, facts) {
|
|
324
|
+
if (finding.category === 'missing_test') {
|
|
325
|
+
// The finding says a file needs tests — verify the file is substantial enough
|
|
326
|
+
return {
|
|
327
|
+
...finding,
|
|
328
|
+
verified: !facts.hasTests && facts.lineCount > 50 && facts.functions.length > 1,
|
|
329
|
+
verificationNotes: facts.hasTests
|
|
330
|
+
? 'File already has tests'
|
|
331
|
+
: `File has ${facts.lineCount} lines, ${facts.functions.length} functions — needs tests`,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
if (finding.category === 'test_quality' && facts.hasTests) {
|
|
335
|
+
return {
|
|
336
|
+
...finding,
|
|
337
|
+
verified: finding.confidence >= 0.3,
|
|
338
|
+
verificationNotes: `Test file with ${facts.testAssertions} assertions`,
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
return {
|
|
342
|
+
...finding,
|
|
343
|
+
verified: finding.confidence >= 0.3,
|
|
344
|
+
verificationNotes: 'Accepted on confidence',
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Try to find a known entity name referenced in a description string.
|
|
349
|
+
*/
|
|
350
|
+
function extractEntityName(description, knownNames) {
|
|
351
|
+
const sorted = [...knownNames].sort((a, b) => b.length - a.length);
|
|
352
|
+
for (const name of sorted) {
|
|
353
|
+
if (description.includes(name)) {
|
|
354
|
+
return name;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Find a file in the facts map, handling path normalization.
|
|
361
|
+
*/
|
|
362
|
+
function findFile(filePath, factsByPath) {
|
|
363
|
+
if (factsByPath.has(filePath))
|
|
364
|
+
return factsByPath.get(filePath);
|
|
365
|
+
const normalized = filePath.replace(/^\.\//, '');
|
|
366
|
+
if (factsByPath.has(normalized))
|
|
367
|
+
return factsByPath.get(normalized);
|
|
368
|
+
const parts = normalized.split('/');
|
|
369
|
+
if (parts.length >= 2) {
|
|
370
|
+
for (const [key, value] of factsByPath) {
|
|
371
|
+
if (key.endsWith('/' + normalized) || key === normalized) {
|
|
372
|
+
return value;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
const fileName = parts[0];
|
|
378
|
+
const matches = [];
|
|
379
|
+
for (const [key, value] of factsByPath) {
|
|
380
|
+
if (key.endsWith('/' + fileName) || key === fileName) {
|
|
381
|
+
matches.push(value);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (matches.length === 1)
|
|
385
|
+
return matches[0];
|
|
386
|
+
}
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep Analysis Gate — LLM-powered code quality analysis.
|
|
3
|
+
*
|
|
4
|
+
* Three-step pipeline:
|
|
5
|
+
* 1. AST extracts facts → "UserService has 8 public methods touching 4 domains"
|
|
6
|
+
* 2. LLM interprets facts → "UserService violates Single Responsibility"
|
|
7
|
+
* 3. AST verifies LLM → Does UserService actually have those methods? ✓
|
|
8
|
+
*
|
|
9
|
+
* AST grounds LLM. LLM interprets AST. Neither works alone.
|
|
10
|
+
*/
|
|
11
|
+
import { Gate, GateContext } from './base.js';
|
|
12
|
+
import { Failure, Provenance, DeepOptions } from '../types/index.js';
|
|
13
|
+
export interface DeepGateConfig {
|
|
14
|
+
options: DeepOptions;
|
|
15
|
+
checks?: Record<string, boolean>;
|
|
16
|
+
threads?: number;
|
|
17
|
+
maxTokens?: number;
|
|
18
|
+
temperature?: number;
|
|
19
|
+
timeoutMs?: number;
|
|
20
|
+
onProgress?: (message: string) => void;
|
|
21
|
+
}
|
|
22
|
+
export declare class DeepAnalysisGate extends Gate {
|
|
23
|
+
private config;
|
|
24
|
+
private provider;
|
|
25
|
+
constructor(config: DeepGateConfig);
|
|
26
|
+
protected get provenance(): Provenance;
|
|
27
|
+
run(context: GateContext): Promise<Failure[]>;
|
|
28
|
+
}
|