tryassay 0.29.0 → 0.31.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/cli.js +21 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/catalog-push.d.ts +7 -0
- package/dist/commands/catalog-push.js +47 -0
- package/dist/commands/catalog-push.js.map +1 -0
- package/dist/commands/generate.js +1 -0
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/harvest.d.ts +9 -0
- package/dist/commands/harvest.js +76 -0
- package/dist/commands/harvest.js.map +1 -0
- package/dist/lib/__tests__/learned-rules.test.d.ts +1 -0
- package/dist/lib/__tests__/learned-rules.test.js +260 -0
- package/dist/lib/__tests__/learned-rules.test.js.map +1 -0
- package/dist/lib/__tests__/pr-harvester-types.test.d.ts +1 -0
- package/dist/lib/__tests__/pr-harvester-types.test.js +43 -0
- package/dist/lib/__tests__/pr-harvester-types.test.js.map +1 -0
- package/dist/lib/__tests__/pr-harvester.test.d.ts +1 -0
- package/dist/lib/__tests__/pr-harvester.test.js +341 -0
- package/dist/lib/__tests__/pr-harvester.test.js.map +1 -0
- package/dist/lib/__tests__/rule-harvester.test.d.ts +1 -0
- package/dist/lib/__tests__/rule-harvester.test.js +526 -0
- package/dist/lib/__tests__/rule-harvester.test.js.map +1 -0
- package/dist/lib/learned-rules/category-map.d.ts +28 -0
- package/dist/lib/learned-rules/category-map.js +110 -0
- package/dist/lib/learned-rules/category-map.js.map +1 -0
- package/dist/lib/learned-rules/index.d.ts +107 -0
- package/dist/lib/learned-rules/index.js +198 -0
- package/dist/lib/learned-rules/index.js.map +1 -0
- package/dist/lib/learned-rules/learned-catalog.d.ts +62 -0
- package/dist/lib/learned-rules/learned-catalog.js +161 -0
- package/dist/lib/learned-rules/learned-catalog.js.map +1 -0
- package/dist/lib/learned-rules/pattern-extractor.d.ts +25 -0
- package/dist/lib/learned-rules/pattern-extractor.js +351 -0
- package/dist/lib/learned-rules/pattern-extractor.js.map +1 -0
- package/dist/lib/learned-rules/rule-codifier.d.ts +41 -0
- package/dist/lib/learned-rules/rule-codifier.js +138 -0
- package/dist/lib/learned-rules/rule-codifier.js.map +1 -0
- package/dist/lib/learned-rules/starter-catalog.d.ts +16 -0
- package/dist/lib/learned-rules/starter-catalog.js +402 -0
- package/dist/lib/learned-rules/starter-catalog.js.map +1 -0
- package/dist/lib/learned-rules/types.d.ts +196 -0
- package/dist/lib/learned-rules/types.js +9 -0
- package/dist/lib/learned-rules/types.js.map +1 -0
- package/dist/lib/learned-rules/validation-harness.d.ts +26 -0
- package/dist/lib/learned-rules/validation-harness.js +260 -0
- package/dist/lib/learned-rules/validation-harness.js.map +1 -0
- package/dist/lib/rule-harvester/diff-parser.d.ts +9 -0
- package/dist/lib/rule-harvester/diff-parser.js +77 -0
- package/dist/lib/rule-harvester/diff-parser.js.map +1 -0
- package/dist/lib/rule-harvester/file-selector.d.ts +10 -0
- package/dist/lib/rule-harvester/file-selector.js +59 -0
- package/dist/lib/rule-harvester/file-selector.js.map +1 -0
- package/dist/lib/rule-harvester/ground-truth.d.ts +19 -0
- package/dist/lib/rule-harvester/ground-truth.js +156 -0
- package/dist/lib/rule-harvester/ground-truth.js.map +1 -0
- package/dist/lib/rule-harvester/harvest.d.ts +26 -0
- package/dist/lib/rule-harvester/harvest.js +307 -0
- package/dist/lib/rule-harvester/harvest.js.map +1 -0
- package/dist/lib/rule-harvester/pr-discovery.d.ts +49 -0
- package/dist/lib/rule-harvester/pr-discovery.js +168 -0
- package/dist/lib/rule-harvester/pr-discovery.js.map +1 -0
- package/dist/lib/rule-harvester/pr-harvest.d.ts +53 -0
- package/dist/lib/rule-harvester/pr-harvest.js +326 -0
- package/dist/lib/rule-harvester/pr-harvest.js.map +1 -0
- package/dist/lib/rule-harvester/progress.d.ts +13 -0
- package/dist/lib/rule-harvester/progress.js +50 -0
- package/dist/lib/rule-harvester/progress.js.map +1 -0
- package/dist/lib/rule-harvester/reporter.d.ts +35 -0
- package/dist/lib/rule-harvester/reporter.js +46 -0
- package/dist/lib/rule-harvester/reporter.js.map +1 -0
- package/dist/lib/rule-harvester/rule-generalizer.d.ts +25 -0
- package/dist/lib/rule-harvester/rule-generalizer.js +135 -0
- package/dist/lib/rule-harvester/rule-generalizer.js.map +1 -0
- package/dist/lib/rule-harvester/scanner.d.ts +20 -0
- package/dist/lib/rule-harvester/scanner.js +37 -0
- package/dist/lib/rule-harvester/scanner.js.map +1 -0
- package/dist/sdk/api-client.d.ts +65 -0
- package/dist/sdk/api-client.js +41 -0
- package/dist/sdk/api-client.js.map +1 -0
- package/dist/sdk/forward-verify.d.ts +3 -1
- package/dist/sdk/forward-verify.js +138 -5
- package/dist/sdk/forward-verify.js.map +1 -1
- package/dist/sdk/index.d.ts +1 -1
- package/dist/sdk/types.d.ts +21 -0
- package/package.json +1 -1
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PR Harvest Orchestrator — ties discovery -> extraction -> generalization -> catalog
|
|
3
|
+
* into a complete pipeline for mining bug-fix PRs from public GitHub repos.
|
|
4
|
+
*
|
|
5
|
+
* PR-sourced rules are validated through the validation harness before promotion.
|
|
6
|
+
* Fuzzy deduplication via Dice coefficient prevents near-duplicate rules.
|
|
7
|
+
*/
|
|
8
|
+
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { discoverBugFixPRs, fetchPRDiffs } from './pr-discovery.js';
|
|
11
|
+
import { filterHunks } from './diff-parser.js';
|
|
12
|
+
import { generalizeHunk, mapToExtractedPattern } from './rule-generalizer.js';
|
|
13
|
+
import { addRule, updateRule, loadRules, loadStats } from '../learned-rules/learned-catalog.js';
|
|
14
|
+
import { validateRule } from '../learned-rules/validation-harness.js';
|
|
15
|
+
import { saveRunReport } from './reporter.js';
|
|
16
|
+
export function createPRProgress() {
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
export function markPRProcessed(progress, repo, prNumber) {
|
|
20
|
+
const existing = progress[repo] ?? [];
|
|
21
|
+
if (existing.includes(prNumber))
|
|
22
|
+
return progress;
|
|
23
|
+
return { ...progress, [repo]: [...existing, prNumber] };
|
|
24
|
+
}
|
|
25
|
+
export function isPRProcessed(progress, repo, prNumber) {
|
|
26
|
+
return (progress[repo] ?? []).includes(prNumber);
|
|
27
|
+
}
|
|
28
|
+
export function getPRProgressStats(progress, repo) {
|
|
29
|
+
return { processedPRs: (progress[repo] ?? []).length };
|
|
30
|
+
}
|
|
31
|
+
export async function loadPRProgress(dir) {
|
|
32
|
+
const filePath = join(dir, 'pr-progress.json');
|
|
33
|
+
try {
|
|
34
|
+
const content = await readFile(filePath, 'utf-8');
|
|
35
|
+
return JSON.parse(content);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export async function savePRProgress(dir, progress) {
|
|
42
|
+
await mkdir(dir, { recursive: true });
|
|
43
|
+
const filePath = join(dir, 'pr-progress.json');
|
|
44
|
+
await writeFile(filePath, JSON.stringify(progress, null, 2), 'utf-8');
|
|
45
|
+
}
|
|
46
|
+
// ── Fuzzy Deduplication ─────────────────────────────────────
|
|
47
|
+
/**
|
|
48
|
+
* Dice coefficient (bigram overlap) for fuzzy string similarity.
|
|
49
|
+
* Returns a value between 0 (no overlap) and 1 (identical).
|
|
50
|
+
*/
|
|
51
|
+
export function diceCoefficient(a, b) {
|
|
52
|
+
if (a === b)
|
|
53
|
+
return 1;
|
|
54
|
+
if (a.length < 2 || b.length < 2)
|
|
55
|
+
return 0;
|
|
56
|
+
const bigramsA = new Map();
|
|
57
|
+
for (let i = 0; i < a.length - 1; i++) {
|
|
58
|
+
const bigram = a.substring(i, i + 2);
|
|
59
|
+
bigramsA.set(bigram, (bigramsA.get(bigram) ?? 0) + 1);
|
|
60
|
+
}
|
|
61
|
+
const bigramsB = new Map();
|
|
62
|
+
for (let i = 0; i < b.length - 1; i++) {
|
|
63
|
+
const bigram = b.substring(i, i + 2);
|
|
64
|
+
bigramsB.set(bigram, (bigramsB.get(bigram) ?? 0) + 1);
|
|
65
|
+
}
|
|
66
|
+
let intersectionSize = 0;
|
|
67
|
+
for (const [bigram, countA] of bigramsA) {
|
|
68
|
+
const countB = bigramsB.get(bigram) ?? 0;
|
|
69
|
+
intersectionSize += Math.min(countA, countB);
|
|
70
|
+
}
|
|
71
|
+
return (2 * intersectionSize) / (a.length - 1 + (b.length - 1));
|
|
72
|
+
}
|
|
73
|
+
/** Similarity threshold above which two descriptions are considered duplicates. */
|
|
74
|
+
const DEDUP_THRESHOLD = 0.7;
|
|
75
|
+
/**
|
|
76
|
+
* Find the most similar existing rule by description, if above threshold.
|
|
77
|
+
*/
|
|
78
|
+
export function findFuzzyDuplicate(existingRules, description, category) {
|
|
79
|
+
let bestMatch = null;
|
|
80
|
+
let bestScore = 0;
|
|
81
|
+
for (const rule of existingRules) {
|
|
82
|
+
// Only compare within the same category
|
|
83
|
+
if (rule.pattern.claimCategory !== category)
|
|
84
|
+
continue;
|
|
85
|
+
if (rule.status === 'rejected' || rule.status === 'deprecated')
|
|
86
|
+
continue;
|
|
87
|
+
const score = diceCoefficient(rule.pattern.description.toLowerCase(), description.toLowerCase());
|
|
88
|
+
if (score > bestScore) {
|
|
89
|
+
bestScore = score;
|
|
90
|
+
bestMatch = rule;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return bestScore >= DEDUP_THRESHOLD ? bestMatch : null;
|
|
94
|
+
}
|
|
95
|
+
// ── Rule Construction ───────────────────────────────────────
|
|
96
|
+
function buildLearnedRule(generalizedRule, patternId, repo, prNumber, hunkFile, buggyCode) {
|
|
97
|
+
const now = new Date().toISOString();
|
|
98
|
+
const pattern = mapToExtractedPattern(generalizedRule, patternId);
|
|
99
|
+
// Use actual buggy code from the diff hunk so validation has real test material
|
|
100
|
+
const codeSnippet = buggyCode && buggyCode.trim().length > 10
|
|
101
|
+
? buggyCode
|
|
102
|
+
: `PR #${prNumber}: ${generalizedRule.fix.description}`;
|
|
103
|
+
const sourceFinding = {
|
|
104
|
+
claimId: `pr_${repo.replace('/', '_')}_${prNumber}`,
|
|
105
|
+
claimDescription: generalizedRule.description,
|
|
106
|
+
codeSnippet,
|
|
107
|
+
filePath: hunkFile,
|
|
108
|
+
language: generalizedRule.detection.language,
|
|
109
|
+
timestamp: now,
|
|
110
|
+
};
|
|
111
|
+
return {
|
|
112
|
+
id: patternId,
|
|
113
|
+
pattern,
|
|
114
|
+
status: 'promoted', // Default; overridden by validation harness result
|
|
115
|
+
createdAt: now,
|
|
116
|
+
updatedAt: now,
|
|
117
|
+
fireCount: 0,
|
|
118
|
+
truePositiveCount: 0,
|
|
119
|
+
falsePositiveCount: 0,
|
|
120
|
+
sourceFindings: [sourceFinding],
|
|
121
|
+
source: 'pr',
|
|
122
|
+
fixDescription: generalizedRule.fix.description,
|
|
123
|
+
fixPattern: generalizedRule.fix.pattern,
|
|
124
|
+
confirmationCount: 1,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// ── Main Orchestrator ───────────────────────────────────────
|
|
128
|
+
function log(config, msg) {
|
|
129
|
+
if (config.onLog) {
|
|
130
|
+
config.onLog(msg);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
console.log(msg);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
export async function harvestPRs(config) {
|
|
137
|
+
const startTime = Date.now();
|
|
138
|
+
// 1. Load progress if resuming
|
|
139
|
+
let progress = config.resume
|
|
140
|
+
? await loadPRProgress(config.progressDir)
|
|
141
|
+
: createPRProgress();
|
|
142
|
+
if (config.resume) {
|
|
143
|
+
const totalTracked = Object.values(progress).reduce((sum, prs) => sum + prs.length, 0);
|
|
144
|
+
log(config, `Resumed PR progress: ${totalTracked} PRs tracked`);
|
|
145
|
+
}
|
|
146
|
+
// 2. Filter repos
|
|
147
|
+
let repos = config.repoList;
|
|
148
|
+
if (config.repoFilter && config.repoFilter.length > 0) {
|
|
149
|
+
const filterSet = new Set(config.repoFilter);
|
|
150
|
+
repos = repos.filter((r) => filterSet.has(`${r.owner}/${r.name}`));
|
|
151
|
+
}
|
|
152
|
+
if (config.limit !== undefined && config.limit > 0) {
|
|
153
|
+
repos = repos.slice(0, config.limit);
|
|
154
|
+
}
|
|
155
|
+
log(config, `Processing ${repos.length} repos in PR mode`);
|
|
156
|
+
// Tracking for the run report
|
|
157
|
+
let totalPRsDiscovered = 0;
|
|
158
|
+
let totalHunksProcessed = 0;
|
|
159
|
+
let rulesLearned = 0;
|
|
160
|
+
let rulesRejected = 0;
|
|
161
|
+
let rulesDuplicate = 0;
|
|
162
|
+
const unmatchedCategories = {};
|
|
163
|
+
for (const repo of repos) {
|
|
164
|
+
const key = `${repo.owner}/${repo.name}`;
|
|
165
|
+
log(config, `[PR-DISCOVER] ${key}`);
|
|
166
|
+
// 3. Discover bug-fix PRs
|
|
167
|
+
let discoveredPRs;
|
|
168
|
+
try {
|
|
169
|
+
const maxPRs = config.maxPRsPerRepo ?? 100;
|
|
170
|
+
const allDiscoveredPRs = discoverBugFixPRs(repo.owner, repo.name, {
|
|
171
|
+
perPage: 30,
|
|
172
|
+
maxPages: Math.ceil(maxPRs / 30),
|
|
173
|
+
titleFallback: true,
|
|
174
|
+
fetchComments: true,
|
|
175
|
+
});
|
|
176
|
+
discoveredPRs = allDiscoveredPRs.slice(0, maxPRs);
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
log(config, `[FAIL] ${key} — discovery failed: ${String(err)}`);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
log(config, `[FOUND] ${key} — ${discoveredPRs.length} bug-fix PRs`);
|
|
183
|
+
totalPRsDiscovered += discoveredPRs.length;
|
|
184
|
+
for (const pr of discoveredPRs) {
|
|
185
|
+
// Skip already-processed PRs on resume
|
|
186
|
+
if (config.resume && isPRProcessed(progress, key, pr.prNumber)) {
|
|
187
|
+
log(config, ` [SKIP] PR #${pr.prNumber} — already processed`);
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
log(config, ` [PR] #${pr.prNumber}: ${pr.title}`);
|
|
191
|
+
// 4. Fetch and parse diffs
|
|
192
|
+
let parsedDiff;
|
|
193
|
+
try {
|
|
194
|
+
parsedDiff = fetchPRDiffs(repo.owner, repo.name, pr);
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
log(config, ` [DIFF_ERR] PR #${pr.prNumber} — ${String(err)}`);
|
|
198
|
+
progress = markPRProcessed(progress, key, pr.prNumber);
|
|
199
|
+
await savePRProgress(config.progressDir, progress);
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
// Filter to meaningful hunks
|
|
203
|
+
const meaningfulHunks = filterHunks(parsedDiff.hunks);
|
|
204
|
+
if (meaningfulHunks.length === 0) {
|
|
205
|
+
log(config, ` [SKIP] PR #${pr.prNumber} — no meaningful hunks`);
|
|
206
|
+
progress = markPRProcessed(progress, key, pr.prNumber);
|
|
207
|
+
await savePRProgress(config.progressDir, progress);
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
log(config, ` [HUNKS] ${meaningfulHunks.length} meaningful (of ${parsedDiff.hunks.length} total)`);
|
|
211
|
+
totalHunksProcessed += meaningfulHunks.length;
|
|
212
|
+
// Load catalog once before processing hunks (refreshed after writes)
|
|
213
|
+
let existingRules = await loadRules(config.catalogPath);
|
|
214
|
+
// 5. Generalize each hunk
|
|
215
|
+
for (const hunk of meaningfulHunks) {
|
|
216
|
+
// Find matching review comment for this hunk's file
|
|
217
|
+
const matchingComment = pr.reviewComments.find((c) => c.path === hunk.file);
|
|
218
|
+
let generalizedRule;
|
|
219
|
+
try {
|
|
220
|
+
generalizedRule = await generalizeHunk(hunk, key, pr.prNumber, pr.title, matchingComment?.body);
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
log(config, ` [GEN_ERR] ${hunk.file}:${hunk.startLine} — ${String(err)}`);
|
|
224
|
+
rulesRejected++;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (!generalizedRule) {
|
|
228
|
+
log(config, ` [SKIP] ${hunk.file}:${hunk.startLine} — too context-specific`);
|
|
229
|
+
rulesRejected++;
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
// 6. Check for fuzzy duplicates in catalog
|
|
233
|
+
const duplicate = findFuzzyDuplicate(existingRules, generalizedRule.description, generalizedRule.category);
|
|
234
|
+
if (duplicate) {
|
|
235
|
+
// Merge provenance into existing rule
|
|
236
|
+
log(config, ` [DEDUP] "${generalizedRule.description}" → merging into ${duplicate.id}`);
|
|
237
|
+
await updateRule(config.catalogPath, duplicate.id, (existing) => ({
|
|
238
|
+
...existing,
|
|
239
|
+
confirmationCount: (existing.confirmationCount ?? 1) + 1,
|
|
240
|
+
updatedAt: new Date().toISOString(),
|
|
241
|
+
sourceFindings: [
|
|
242
|
+
...existing.sourceFindings,
|
|
243
|
+
{
|
|
244
|
+
claimId: `pr_${key.replace('/', '_')}_${pr.prNumber}`,
|
|
245
|
+
claimDescription: generalizedRule.description,
|
|
246
|
+
codeSnippet: `PR #${pr.prNumber}: ${generalizedRule.fix.description}`,
|
|
247
|
+
filePath: hunk.file,
|
|
248
|
+
language: generalizedRule.detection.language,
|
|
249
|
+
timestamp: new Date().toISOString(),
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
}));
|
|
253
|
+
rulesDuplicate++;
|
|
254
|
+
existingRules = await loadRules(config.catalogPath);
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
// 7. Build candidate rule and validate before promotion
|
|
258
|
+
const patternId = `lp_pr_${Date.now()}`;
|
|
259
|
+
// Pass the buggy code (removed lines + context) so validation has real test material
|
|
260
|
+
const buggyCode = [...hunk.context, ...hunk.removedLines].join('\n');
|
|
261
|
+
const candidateRule = buildLearnedRule(generalizedRule, patternId, key, pr.prNumber, hunk.file, buggyCode);
|
|
262
|
+
// 7b. Run through validation harness
|
|
263
|
+
const validationResult = validateRule(candidateRule);
|
|
264
|
+
const finalRule = {
|
|
265
|
+
...candidateRule,
|
|
266
|
+
status: validationResult.passed ? 'promoted' : 'rejected',
|
|
267
|
+
validationResults: validationResult,
|
|
268
|
+
};
|
|
269
|
+
await addRule(config.catalogPath, finalRule);
|
|
270
|
+
existingRules = await loadRules(config.catalogPath);
|
|
271
|
+
if (validationResult.passed) {
|
|
272
|
+
rulesLearned++;
|
|
273
|
+
log(config, ` [LEARNED] ${patternId}: ${generalizedRule.description} (precision=${validationResult.precision.toFixed(2)} recall=${validationResult.recall.toFixed(2)})`);
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
rulesRejected++;
|
|
277
|
+
log(config, ` [REJECTED] ${patternId}: ${generalizedRule.description} — validation failed (precision=${validationResult.precision.toFixed(2)} recall=${validationResult.recall.toFixed(2)})`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
// Mark PR as processed and persist after each PR
|
|
281
|
+
progress = markPRProcessed(progress, key, pr.prNumber);
|
|
282
|
+
await savePRProgress(config.progressDir, progress);
|
|
283
|
+
}
|
|
284
|
+
log(config, `[DONE] ${key}`);
|
|
285
|
+
}
|
|
286
|
+
// 8. Generate and save run report
|
|
287
|
+
const stats = await loadStats(config.catalogPath);
|
|
288
|
+
const allRules = await loadRules(config.catalogPath);
|
|
289
|
+
const catalogSize = {
|
|
290
|
+
total: allRules.length,
|
|
291
|
+
promoted: allRules.filter((r) => r.status === 'promoted').length,
|
|
292
|
+
rejected: allRules.filter((r) => r.status === 'rejected').length,
|
|
293
|
+
};
|
|
294
|
+
const durationMinutes = (Date.now() - startTime) / 60_000;
|
|
295
|
+
const report = {
|
|
296
|
+
timestamp: new Date().toISOString(),
|
|
297
|
+
model: config.model,
|
|
298
|
+
reposScanned: repos.length,
|
|
299
|
+
filesScanned: totalHunksProcessed, // hunks are the PR-mode equivalent of files
|
|
300
|
+
claimsExtracted: totalPRsDiscovered,
|
|
301
|
+
findingsConfirmed: totalHunksProcessed,
|
|
302
|
+
rulesLearned,
|
|
303
|
+
rulesRejected,
|
|
304
|
+
rulesDuplicate,
|
|
305
|
+
unmatchedCategories,
|
|
306
|
+
durationMinutes,
|
|
307
|
+
catalogSize,
|
|
308
|
+
};
|
|
309
|
+
const reportPath = await saveRunReport(config.runsDir, report);
|
|
310
|
+
// 9. Print summary
|
|
311
|
+
log(config, '');
|
|
312
|
+
log(config, '══════════════════════════════════════');
|
|
313
|
+
log(config, ' PR HARVEST COMPLETE');
|
|
314
|
+
log(config, '══════════════════════════════════════');
|
|
315
|
+
log(config, ` Repos processed: ${repos.length}`);
|
|
316
|
+
log(config, ` PRs discovered: ${totalPRsDiscovered}`);
|
|
317
|
+
log(config, ` Hunks processed: ${totalHunksProcessed}`);
|
|
318
|
+
log(config, ` Rules learned: ${rulesLearned}`);
|
|
319
|
+
log(config, ` Rules duplicate: ${rulesDuplicate}`);
|
|
320
|
+
log(config, ` Rules rejected: ${rulesRejected}`);
|
|
321
|
+
log(config, ` Catalog total: ${catalogSize.total} (${catalogSize.promoted} promoted)`);
|
|
322
|
+
log(config, ` Duration: ${durationMinutes.toFixed(1)}m`);
|
|
323
|
+
log(config, ` Report: ${reportPath}`);
|
|
324
|
+
log(config, '══════════════════════════════════════');
|
|
325
|
+
}
|
|
326
|
+
//# sourceMappingURL=pr-harvest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pr-harvest.js","sourceRoot":"","sources":["../../../src/lib/rule-harvester/pr-harvest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type FileState = 'pending' | 'scanned' | 'confirmed' | 'learned';
|
|
2
|
+
export interface RepoProgress {
|
|
3
|
+
status: 'pending' | 'in_progress' | 'complete';
|
|
4
|
+
startedAt: string;
|
|
5
|
+
files: Record<string, FileState>;
|
|
6
|
+
}
|
|
7
|
+
export type HarvestProgress = Record<string, RepoProgress>;
|
|
8
|
+
export declare function loadProgress(dir: string): Promise<HarvestProgress>;
|
|
9
|
+
export declare function saveProgress(dir: string, progress: HarvestProgress): Promise<void>;
|
|
10
|
+
export declare function getFileState(progress: HarvestProgress, repoKey: string, filePath: string): FileState;
|
|
11
|
+
export declare function getRepoStatus(files: Record<string, FileState>): 'pending' | 'in_progress' | 'complete';
|
|
12
|
+
export declare function initRepo(progress: HarvestProgress, repoKey: string): HarvestProgress;
|
|
13
|
+
export declare function setFileState(progress: HarvestProgress, repoKey: string, filePath: string, state: FileState): HarvestProgress;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
export async function loadProgress(dir) {
|
|
4
|
+
const filePath = join(dir, 'progress.json');
|
|
5
|
+
try {
|
|
6
|
+
const content = await readFile(filePath, 'utf-8');
|
|
7
|
+
return JSON.parse(content);
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return {};
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export async function saveProgress(dir, progress) {
|
|
14
|
+
await mkdir(dir, { recursive: true });
|
|
15
|
+
const filePath = join(dir, 'progress.json');
|
|
16
|
+
await writeFile(filePath, JSON.stringify(progress, null, 2), 'utf-8');
|
|
17
|
+
}
|
|
18
|
+
export function getFileState(progress, repoKey, filePath) {
|
|
19
|
+
return progress[repoKey]?.files[filePath] ?? 'pending';
|
|
20
|
+
}
|
|
21
|
+
export function getRepoStatus(files) {
|
|
22
|
+
const states = Object.values(files);
|
|
23
|
+
if (states.length === 0)
|
|
24
|
+
return 'pending';
|
|
25
|
+
if (states.every((s) => s === 'learned'))
|
|
26
|
+
return 'complete';
|
|
27
|
+
if (states.every((s) => s === 'pending'))
|
|
28
|
+
return 'pending';
|
|
29
|
+
return 'in_progress';
|
|
30
|
+
}
|
|
31
|
+
export function initRepo(progress, repoKey) {
|
|
32
|
+
if (progress[repoKey])
|
|
33
|
+
return progress;
|
|
34
|
+
return {
|
|
35
|
+
...progress,
|
|
36
|
+
[repoKey]: { status: 'pending', startedAt: new Date().toISOString(), files: {} },
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function setFileState(progress, repoKey, filePath, state) {
|
|
40
|
+
const existingRepo = progress[repoKey];
|
|
41
|
+
const existingFiles = existingRepo?.files ?? {};
|
|
42
|
+
const updatedFiles = { ...existingFiles, [filePath]: state };
|
|
43
|
+
const updatedRepo = {
|
|
44
|
+
status: getRepoStatus(updatedFiles),
|
|
45
|
+
startedAt: existingRepo?.startedAt ?? new Date().toISOString(),
|
|
46
|
+
files: updatedFiles,
|
|
47
|
+
};
|
|
48
|
+
return { ...progress, [repoKey]: updatedRepo };
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=progress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../../../src/lib/rule-harvester/progress.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAYjC,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAW;IAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAC5C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAoB,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAW,EAAE,QAAyB;IACvE,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAC5C,MAAM,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,QAAyB,EACzB,OAAe,EACf,QAAgB;IAEhB,OAAO,QAAQ,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,KAAgC;IAEhC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACpC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC1C,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC;QAAE,OAAO,UAAU,CAAC;IAC5D,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IAC3D,OAAO,aAAa,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,QAAQ,CACtB,QAAyB,EACzB,OAAe;IAEf,IAAI,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,QAAQ,CAAC;IACvC,OAAO;QACL,GAAG,QAAQ;QACX,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;KACjF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,QAAyB,EACzB,OAAe,EACf,QAAgB,EAChB,KAAgB;IAEhB,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,aAAa,GAAG,YAAY,EAAE,KAAK,IAAI,EAAE,CAAC;IAChD,MAAM,YAAY,GAA8B,EAAE,GAAG,aAAa,EAAE,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAC;IAExF,MAAM,WAAW,GAAiB;QAChC,MAAM,EAAE,aAAa,CAAC,YAAY,CAAC;QACnC,SAAS,EAAE,YAAY,EAAE,SAAS,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAC9D,KAAK,EAAE,YAAY;KACpB,CAAC;IAEF,OAAO,EAAE,GAAG,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface ScanFileResult {
|
|
2
|
+
repo: string;
|
|
3
|
+
filePath: string;
|
|
4
|
+
claimsExtracted: number;
|
|
5
|
+
confirmed: number;
|
|
6
|
+
rulesLearned: number;
|
|
7
|
+
rulesRejected: number;
|
|
8
|
+
rulesDuplicate: number;
|
|
9
|
+
unmatchedCategories: string[];
|
|
10
|
+
durationMs: number;
|
|
11
|
+
}
|
|
12
|
+
export interface RunReport {
|
|
13
|
+
timestamp: string;
|
|
14
|
+
model: string;
|
|
15
|
+
reposScanned: number;
|
|
16
|
+
filesScanned: number;
|
|
17
|
+
claimsExtracted: number;
|
|
18
|
+
findingsConfirmed: number;
|
|
19
|
+
rulesLearned: number;
|
|
20
|
+
rulesRejected: number;
|
|
21
|
+
rulesDuplicate: number;
|
|
22
|
+
unmatchedCategories: Record<string, number>;
|
|
23
|
+
durationMinutes: number;
|
|
24
|
+
catalogSize: {
|
|
25
|
+
total: number;
|
|
26
|
+
promoted: number;
|
|
27
|
+
rejected: number;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export declare function createRunReport(fileResults: ScanFileResult[], model: string, catalogSize: {
|
|
31
|
+
total: number;
|
|
32
|
+
promoted: number;
|
|
33
|
+
rejected: number;
|
|
34
|
+
}): RunReport;
|
|
35
|
+
export declare function saveRunReport(runsDir: string, report: RunReport): Promise<string>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
export function createRunReport(fileResults, model, catalogSize) {
|
|
4
|
+
const uniqueRepos = new Set(fileResults.map((r) => r.repo));
|
|
5
|
+
let claimsExtracted = 0;
|
|
6
|
+
let findingsConfirmed = 0;
|
|
7
|
+
let rulesLearned = 0;
|
|
8
|
+
let rulesRejected = 0;
|
|
9
|
+
let rulesDuplicate = 0;
|
|
10
|
+
let totalDurationMs = 0;
|
|
11
|
+
const unmatchedCategories = {};
|
|
12
|
+
for (const result of fileResults) {
|
|
13
|
+
claimsExtracted += result.claimsExtracted;
|
|
14
|
+
findingsConfirmed += result.confirmed;
|
|
15
|
+
rulesLearned += result.rulesLearned;
|
|
16
|
+
rulesRejected += result.rulesRejected;
|
|
17
|
+
rulesDuplicate += result.rulesDuplicate;
|
|
18
|
+
totalDurationMs += result.durationMs;
|
|
19
|
+
for (const category of result.unmatchedCategories) {
|
|
20
|
+
unmatchedCategories[category] = (unmatchedCategories[category] ?? 0) + 1;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
timestamp: new Date().toISOString(),
|
|
25
|
+
model,
|
|
26
|
+
reposScanned: uniqueRepos.size,
|
|
27
|
+
filesScanned: fileResults.length,
|
|
28
|
+
claimsExtracted,
|
|
29
|
+
findingsConfirmed,
|
|
30
|
+
rulesLearned,
|
|
31
|
+
rulesRejected,
|
|
32
|
+
rulesDuplicate,
|
|
33
|
+
unmatchedCategories,
|
|
34
|
+
durationMinutes: totalDurationMs / 60_000,
|
|
35
|
+
catalogSize,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export async function saveRunReport(runsDir, report) {
|
|
39
|
+
await mkdir(runsDir, { recursive: true });
|
|
40
|
+
// Replace : and . with - to make a safe filename
|
|
41
|
+
const safeName = report.timestamp.replace(/[:.]/g, '-');
|
|
42
|
+
const filePath = join(runsDir, `${safeName}.json`);
|
|
43
|
+
await writeFile(filePath, JSON.stringify(report, null, 2), 'utf-8');
|
|
44
|
+
return filePath;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../../../src/lib/rule-harvester/reporter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AA6BjC,MAAM,UAAU,eAAe,CAC7B,WAA6B,EAC7B,KAAa,EACb,WAAkE;IAElE,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAE5D,IAAI,eAAe,GAAG,CAAC,CAAC;IACxB,IAAI,iBAAiB,GAAG,CAAC,CAAC;IAC1B,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,IAAI,eAAe,GAAG,CAAC,CAAC;IACxB,MAAM,mBAAmB,GAA2B,EAAE,CAAC;IAEvD,KAAK,MAAM,MAAM,IAAI,WAAW,EAAE,CAAC;QACjC,eAAe,IAAI,MAAM,CAAC,eAAe,CAAC;QAC1C,iBAAiB,IAAI,MAAM,CAAC,SAAS,CAAC;QACtC,YAAY,IAAI,MAAM,CAAC,YAAY,CAAC;QACpC,aAAa,IAAI,MAAM,CAAC,aAAa,CAAC;QACtC,cAAc,IAAI,MAAM,CAAC,cAAc,CAAC;QACxC,eAAe,IAAI,MAAM,CAAC,UAAU,CAAC;QAErC,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,mBAAmB,EAAE,CAAC;YAClD,mBAAmB,CAAC,QAAQ,CAAC,GAAG,CAAC,mBAAmB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IAED,OAAO;QACL,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,KAAK;QACL,YAAY,EAAE,WAAW,CAAC,IAAI;QAC9B,YAAY,EAAE,WAAW,CAAC,MAAM;QAChC,eAAe;QACf,iBAAiB;QACjB,YAAY;QACZ,aAAa;QACb,cAAc;QACd,mBAAmB;QACnB,eAAe,EAAE,eAAe,GAAG,MAAM;QACzC,WAAW;KACZ,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAe,EAAE,MAAiB;IACpE,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1C,iDAAiD;IACjD,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,QAAQ,OAAO,CAAC,CAAC;IAEnD,MAAM,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAEpE,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule Generalizer — Uses LLM to generalize PR diff hunks into reusable
|
|
3
|
+
* code quality rules for the Assay catalog.
|
|
4
|
+
*
|
|
5
|
+
* Given a before/after diff hunk (and optional reviewer comment), the LLM
|
|
6
|
+
* produces a GeneralizedRule with detection regex, fix description, severity,
|
|
7
|
+
* and provenance. The rule is then mapped to an ExtractedPattern for catalog
|
|
8
|
+
* ingestion.
|
|
9
|
+
*/
|
|
10
|
+
import type { DiffHunk, GeneralizedRule } from '../learned-rules/types.js';
|
|
11
|
+
import type { ExtractedPattern } from '../learned-rules/types.js';
|
|
12
|
+
/**
|
|
13
|
+
* Build the user prompt for the LLM generalization call.
|
|
14
|
+
*/
|
|
15
|
+
export declare function buildGeneralizationPrompt(hunk: DiffHunk, repo: string, prNumber: number, prTitle: string, reviewComment?: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Call the LLM to generalize a diff hunk into a reusable rule.
|
|
18
|
+
* Returns null if the hunk is too context-specific, parse fails,
|
|
19
|
+
* or all retries are exhausted.
|
|
20
|
+
*/
|
|
21
|
+
export declare function generalizeHunk(hunk: DiffHunk, repo: string, prNumber: number, prTitle: string, reviewComment?: string): Promise<GeneralizedRule | null>;
|
|
22
|
+
/**
|
|
23
|
+
* Map a GeneralizedRule to an ExtractedPattern for catalog ingestion.
|
|
24
|
+
*/
|
|
25
|
+
export declare function mapToExtractedPattern(rule: GeneralizedRule, patternId: string): ExtractedPattern;
|