tryassay 0.28.2 → 0.30.1

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.
Files changed (93) hide show
  1. package/dist/cli.js +10 -0
  2. package/dist/cli.js.map +1 -1
  3. package/dist/commands/assess.js +4 -2
  4. package/dist/commands/assess.js.map +1 -1
  5. package/dist/commands/generate.js +1 -0
  6. package/dist/commands/generate.js.map +1 -1
  7. package/dist/commands/harvest.d.ts +9 -0
  8. package/dist/commands/harvest.js +76 -0
  9. package/dist/commands/harvest.js.map +1 -0
  10. package/dist/lib/__tests__/learned-rules.test.d.ts +1 -0
  11. package/dist/lib/__tests__/learned-rules.test.js +260 -0
  12. package/dist/lib/__tests__/learned-rules.test.js.map +1 -0
  13. package/dist/lib/__tests__/pr-harvester-types.test.d.ts +1 -0
  14. package/dist/lib/__tests__/pr-harvester-types.test.js +43 -0
  15. package/dist/lib/__tests__/pr-harvester-types.test.js.map +1 -0
  16. package/dist/lib/__tests__/pr-harvester.test.d.ts +1 -0
  17. package/dist/lib/__tests__/pr-harvester.test.js +341 -0
  18. package/dist/lib/__tests__/pr-harvester.test.js.map +1 -0
  19. package/dist/lib/__tests__/rule-harvester.test.d.ts +1 -0
  20. package/dist/lib/__tests__/rule-harvester.test.js +526 -0
  21. package/dist/lib/__tests__/rule-harvester.test.js.map +1 -0
  22. package/dist/lib/claim-extractor.d.ts +1 -0
  23. package/dist/lib/claim-extractor.js +116 -15
  24. package/dist/lib/claim-extractor.js.map +1 -1
  25. package/dist/lib/code-verifier.d.ts +12 -1
  26. package/dist/lib/code-verifier.js +155 -12
  27. package/dist/lib/code-verifier.js.map +1 -1
  28. package/dist/lib/learned-rules/category-map.d.ts +28 -0
  29. package/dist/lib/learned-rules/category-map.js +110 -0
  30. package/dist/lib/learned-rules/category-map.js.map +1 -0
  31. package/dist/lib/learned-rules/index.d.ts +105 -0
  32. package/dist/lib/learned-rules/index.js +198 -0
  33. package/dist/lib/learned-rules/index.js.map +1 -0
  34. package/dist/lib/learned-rules/learned-catalog.d.ts +62 -0
  35. package/dist/lib/learned-rules/learned-catalog.js +161 -0
  36. package/dist/lib/learned-rules/learned-catalog.js.map +1 -0
  37. package/dist/lib/learned-rules/pattern-extractor.d.ts +25 -0
  38. package/dist/lib/learned-rules/pattern-extractor.js +351 -0
  39. package/dist/lib/learned-rules/pattern-extractor.js.map +1 -0
  40. package/dist/lib/learned-rules/rule-codifier.d.ts +41 -0
  41. package/dist/lib/learned-rules/rule-codifier.js +138 -0
  42. package/dist/lib/learned-rules/rule-codifier.js.map +1 -0
  43. package/dist/lib/learned-rules/starter-catalog.d.ts +16 -0
  44. package/dist/lib/learned-rules/starter-catalog.js +402 -0
  45. package/dist/lib/learned-rules/starter-catalog.js.map +1 -0
  46. package/dist/lib/learned-rules/types.d.ts +196 -0
  47. package/dist/lib/learned-rules/types.js +9 -0
  48. package/dist/lib/learned-rules/types.js.map +1 -0
  49. package/dist/lib/learned-rules/validation-harness.d.ts +26 -0
  50. package/dist/lib/learned-rules/validation-harness.js +260 -0
  51. package/dist/lib/learned-rules/validation-harness.js.map +1 -0
  52. package/dist/lib/llm-provider.d.ts +7 -0
  53. package/dist/lib/llm-provider.js +99 -6
  54. package/dist/lib/llm-provider.js.map +1 -1
  55. package/dist/lib/rule-harvester/diff-parser.d.ts +9 -0
  56. package/dist/lib/rule-harvester/diff-parser.js +77 -0
  57. package/dist/lib/rule-harvester/diff-parser.js.map +1 -0
  58. package/dist/lib/rule-harvester/file-selector.d.ts +10 -0
  59. package/dist/lib/rule-harvester/file-selector.js +59 -0
  60. package/dist/lib/rule-harvester/file-selector.js.map +1 -0
  61. package/dist/lib/rule-harvester/ground-truth.d.ts +19 -0
  62. package/dist/lib/rule-harvester/ground-truth.js +156 -0
  63. package/dist/lib/rule-harvester/ground-truth.js.map +1 -0
  64. package/dist/lib/rule-harvester/harvest.d.ts +26 -0
  65. package/dist/lib/rule-harvester/harvest.js +307 -0
  66. package/dist/lib/rule-harvester/harvest.js.map +1 -0
  67. package/dist/lib/rule-harvester/pr-discovery.d.ts +49 -0
  68. package/dist/lib/rule-harvester/pr-discovery.js +168 -0
  69. package/dist/lib/rule-harvester/pr-discovery.js.map +1 -0
  70. package/dist/lib/rule-harvester/pr-harvest.d.ts +53 -0
  71. package/dist/lib/rule-harvester/pr-harvest.js +326 -0
  72. package/dist/lib/rule-harvester/pr-harvest.js.map +1 -0
  73. package/dist/lib/rule-harvester/progress.d.ts +13 -0
  74. package/dist/lib/rule-harvester/progress.js +50 -0
  75. package/dist/lib/rule-harvester/progress.js.map +1 -0
  76. package/dist/lib/rule-harvester/reporter.d.ts +35 -0
  77. package/dist/lib/rule-harvester/reporter.js +46 -0
  78. package/dist/lib/rule-harvester/reporter.js.map +1 -0
  79. package/dist/lib/rule-harvester/rule-generalizer.d.ts +25 -0
  80. package/dist/lib/rule-harvester/rule-generalizer.js +135 -0
  81. package/dist/lib/rule-harvester/rule-generalizer.js.map +1 -0
  82. package/dist/lib/rule-harvester/scanner.d.ts +20 -0
  83. package/dist/lib/rule-harvester/scanner.js +37 -0
  84. package/dist/lib/rule-harvester/scanner.js.map +1 -0
  85. package/dist/runtime/types.d.ts +1 -1
  86. package/dist/sdk/forward-verify.d.ts +3 -1
  87. package/dist/sdk/forward-verify.js +68 -5
  88. package/dist/sdk/forward-verify.js.map +1 -1
  89. package/dist/sdk/index.d.ts +1 -1
  90. package/dist/sdk/index.js +7 -5
  91. package/dist/sdk/index.js.map +1 -1
  92. package/dist/sdk/types.d.ts +21 -0
  93. package/package.json +1 -1
@@ -0,0 +1,156 @@
1
+ import { execSync } from 'node:child_process';
2
+ export function runTscCheck(repoDir) {
3
+ try {
4
+ execSync('npx tsc --noEmit 2>&1', {
5
+ cwd: repoDir,
6
+ timeout: 120_000,
7
+ encoding: 'utf-8',
8
+ });
9
+ return { errors: '', exitCode: 0 };
10
+ }
11
+ catch (err) {
12
+ const e = err;
13
+ if (e.status === undefined) {
14
+ return null;
15
+ }
16
+ const combined = ((e.stdout ?? '') + (e.stderr ?? '')).trim();
17
+ return { errors: combined, exitCode: e.status };
18
+ }
19
+ }
20
+ export function tscReportsErrorInFile(tscOutput, filePath) {
21
+ return tscOutput.includes(filePath);
22
+ }
23
+ export function confirmByGrep(code, claim) {
24
+ const cat = claim.category.toLowerCase();
25
+ const desc = claim.description.toLowerCase();
26
+ // error-handling: await without surrounding try/catch or .catch()
27
+ if (cat.includes('error-handling') ||
28
+ desc.includes('error handling') ||
29
+ desc.includes('try/catch')) {
30
+ const hasAwait = /\bawait\b/.test(code);
31
+ const hasTryCatch = /\btry\s*\{/.test(code);
32
+ const hasCatchChain = /\.catch\s*\(/.test(code);
33
+ const confirmed = hasAwait && !hasTryCatch && !hasCatchChain;
34
+ return {
35
+ claimId: claim.claimId,
36
+ confirmed,
37
+ method: 'grep',
38
+ evidence: confirmed
39
+ ? 'Found await without try/catch or .catch()'
40
+ : 'await is wrapped in try/catch or .catch()',
41
+ };
42
+ }
43
+ // security + SQL injection: template literals with SQL keywords and ${
44
+ if ((cat.includes('security') &&
45
+ (desc.includes('sql') ||
46
+ desc.includes('injection') ||
47
+ desc.includes('query'))) ||
48
+ cat.includes('injection')) {
49
+ const sqlTemplatePattern = /`[^`]*(SELECT|INSERT|UPDATE|DELETE|FROM|WHERE)[^`]*\$\{/i;
50
+ const confirmed = sqlTemplatePattern.test(code);
51
+ return {
52
+ claimId: claim.claimId,
53
+ confirmed,
54
+ method: 'grep',
55
+ evidence: confirmed
56
+ ? 'Found SQL template literal with ${} interpolation'
57
+ : 'No SQL injection pattern found',
58
+ };
59
+ }
60
+ // hardcoded secrets: key|secret|token|password|api_key = "long_string"
61
+ if (cat.includes('hardcoded') ||
62
+ cat.includes('secret') ||
63
+ desc.includes('hardcoded') ||
64
+ desc.includes('secret') ||
65
+ desc.includes('api key') ||
66
+ desc.includes('api_key')) {
67
+ const secretPattern = /\b(?:key|secret|token|password|api_key)\s*=\s*["'][^"']{8,}["']/i;
68
+ const confirmed = secretPattern.test(code);
69
+ return {
70
+ claimId: claim.claimId,
71
+ confirmed,
72
+ method: 'grep',
73
+ evidence: confirmed
74
+ ? 'Found hardcoded secret assignment'
75
+ : 'No hardcoded secret pattern found',
76
+ };
77
+ }
78
+ // edge-case / null/undefined: nested property access without optional chaining or null checks
79
+ if (cat.includes('edge-case') ||
80
+ cat.includes('null') ||
81
+ cat.includes('undefined') ||
82
+ desc.includes('null') ||
83
+ desc.includes('undefined') ||
84
+ desc.includes('optional')) {
85
+ // Match a.b.c patterns where there's no ?. in the chain
86
+ const unsafeAccessPattern = /\b\w+\.\w+\.\w+/;
87
+ const safeAccessPattern = /\b\w+\??\.\w+\??\.\w+/;
88
+ const hasUnsafe = unsafeAccessPattern.test(code);
89
+ const hasSafe = /\?\.\w+/.test(code);
90
+ const confirmed = hasUnsafe && !hasSafe;
91
+ return {
92
+ claimId: claim.claimId,
93
+ confirmed,
94
+ method: 'grep',
95
+ evidence: confirmed
96
+ ? 'Found nested property access without optional chaining'
97
+ : 'Optional chaining or null checks present',
98
+ };
99
+ }
100
+ // input validation: req.body/params/query without validation
101
+ if (cat.includes('input validation') ||
102
+ cat.includes('validation') ||
103
+ desc.includes('input validation') ||
104
+ desc.includes('req.body') ||
105
+ desc.includes('req.params') ||
106
+ desc.includes('req.query')) {
107
+ const hasDirectAccess = /req\.(body|params|query)/.test(code);
108
+ const hasValidation = /validate|schema|zod|yup|joi|\.parse\(|\.safeParse\(/.test(code);
109
+ const confirmed = hasDirectAccess && !hasValidation;
110
+ return {
111
+ claimId: claim.claimId,
112
+ confirmed,
113
+ method: 'grep',
114
+ evidence: confirmed
115
+ ? 'Found req.body/params/query without validation'
116
+ : 'Validation present',
117
+ };
118
+ }
119
+ // unhandled promise: floating promise without await
120
+ if (cat.includes('unhandled promise') ||
121
+ cat.includes('promise') ||
122
+ desc.includes('unhandled promise') ||
123
+ desc.includes('floating promise')) {
124
+ // Look for function calls that return promises but are not awaited
125
+ const floatingPattern = /^\s+(?!.*await\s)\w+\s*\(.*\)\s*;/m;
126
+ const confirmed = floatingPattern.test(code);
127
+ return {
128
+ claimId: claim.claimId,
129
+ confirmed,
130
+ method: 'grep',
131
+ evidence: confirmed
132
+ ? 'Found potential floating promise (unawaited call)'
133
+ : 'No floating promise pattern found',
134
+ };
135
+ }
136
+ return {
137
+ claimId: claim.claimId,
138
+ confirmed: false,
139
+ method: 'grep',
140
+ evidence: `No grep strategy for category "${claim.category}"`,
141
+ };
142
+ }
143
+ export function parseConfirmation(code, claim, tscOutput, filePath) {
144
+ if (tscOutput !== null &&
145
+ claim.category.toLowerCase().includes('type-safety') &&
146
+ tscReportsErrorInFile(tscOutput, filePath)) {
147
+ return {
148
+ claimId: claim.claimId,
149
+ confirmed: true,
150
+ method: 'compiler',
151
+ evidence: `tsc reported error in ${filePath}`,
152
+ };
153
+ }
154
+ return confirmByGrep(code, claim);
155
+ }
156
+ //# sourceMappingURL=ground-truth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ground-truth.js","sourceRoot":"","sources":["../../../src/lib/rule-harvester/ground-truth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ export interface HarvestConfig {
2
+ repoList: Array<{
3
+ owner: string;
4
+ name: string;
5
+ category: string;
6
+ }>;
7
+ /** Assay project root — where rules go */
8
+ catalogPath: string;
9
+ /** Where progress.json lives */
10
+ progressDir: string;
11
+ /** Where run reports go */
12
+ runsDir: string;
13
+ /** Temp dir for clones (e.g. /private/tmp/assay-harvester) */
14
+ workDir: string;
15
+ /** Model name for reporting */
16
+ model: string;
17
+ /** Max repos to scan */
18
+ limit?: number;
19
+ /** Only scan these repos (owner/name format) */
20
+ repoFilter?: string[];
21
+ /** Skip completed repos */
22
+ resume?: boolean;
23
+ /** Logging callback */
24
+ onLog?: (msg: string) => void;
25
+ }
26
+ export declare function harvestRepos(config: HarvestConfig): Promise<void>;
@@ -0,0 +1,307 @@
1
+ import { execSync } from 'node:child_process';
2
+ import { readFile, rm } from 'node:fs/promises';
3
+ import { readdirSync, readFileSync, statSync } from 'node:fs';
4
+ import { join, relative } from 'node:path';
5
+ import { selectFiles } from './file-selector.js';
6
+ import { scanFile } from './scanner.js';
7
+ import { parseConfirmation, runTscCheck } from './ground-truth.js';
8
+ import { loadProgress, saveProgress, getFileState, setFileState, initRepo, } from './progress.js';
9
+ import { createRunReport, saveRunReport } from './reporter.js';
10
+ import { learnFromFinding, getLearnedRulesSummary } from '../learned-rules/index.js';
11
+ // ── Helpers ───────────────────────────────────────────────────
12
+ function repoKey(repo) {
13
+ return `${repo.owner}/${repo.name}`;
14
+ }
15
+ function log(config, msg) {
16
+ if (config.onLog) {
17
+ config.onLog(msg);
18
+ }
19
+ else {
20
+ console.log(msg);
21
+ }
22
+ }
23
+ function cloneRepo(repo, targetDir) {
24
+ const url = `https://github.com/${repo.owner}/${repo.name}.git`;
25
+ try {
26
+ execSync(`git clone --depth 1 "${url}" "${targetDir}"`, {
27
+ timeout: 120_000,
28
+ stdio: 'pipe',
29
+ });
30
+ return true;
31
+ }
32
+ catch {
33
+ return false;
34
+ }
35
+ }
36
+ function installDeps(repoDir) {
37
+ try {
38
+ execSync('npm install --ignore-scripts', {
39
+ cwd: repoDir,
40
+ timeout: 180_000,
41
+ stdio: 'pipe',
42
+ });
43
+ return true;
44
+ }
45
+ catch {
46
+ return false;
47
+ }
48
+ }
49
+ function listTsFiles(dir, base) {
50
+ const root = base ?? dir;
51
+ const results = [];
52
+ const TS_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx']);
53
+ const SKIP_DIRS = new Set(['node_modules', '.git', 'dist']);
54
+ let entries;
55
+ try {
56
+ entries = readdirSync(dir);
57
+ }
58
+ catch {
59
+ return results;
60
+ }
61
+ for (const entry of entries) {
62
+ if (SKIP_DIRS.has(entry))
63
+ continue;
64
+ const fullPath = join(dir, entry);
65
+ let stat;
66
+ try {
67
+ stat = statSync(fullPath);
68
+ }
69
+ catch {
70
+ continue;
71
+ }
72
+ if (stat.isDirectory()) {
73
+ results.push(...listTsFiles(fullPath, root));
74
+ }
75
+ else if (stat.isFile()) {
76
+ const ext = entry.slice(entry.lastIndexOf('.'));
77
+ if (!TS_EXTENSIONS.has(ext))
78
+ continue;
79
+ let content = '';
80
+ try {
81
+ content = readFileSync(fullPath, 'utf-8');
82
+ }
83
+ catch {
84
+ // If unreadable, estimate 0 lines
85
+ }
86
+ const lineCount = content ? content.split('\n').length : 0;
87
+ const relPath = relative(root, fullPath);
88
+ results.push({ path: relPath, lineCount });
89
+ }
90
+ }
91
+ return results;
92
+ }
93
+ // ── Main Pipeline ─────────────────────────────────────────────
94
+ export async function harvestRepos(config) {
95
+ const startTime = Date.now();
96
+ // 1. Load progress if resume=true
97
+ let progress = {};
98
+ if (config.resume) {
99
+ progress = await loadProgress(config.progressDir);
100
+ log(config, `Resumed progress: ${Object.keys(progress).length} repos tracked`);
101
+ }
102
+ // 2. Filter repos by repoFilter and limit
103
+ let repos = config.repoList;
104
+ if (config.repoFilter && config.repoFilter.length > 0) {
105
+ const filterSet = new Set(config.repoFilter);
106
+ repos = repos.filter((r) => filterSet.has(repoKey(r)));
107
+ }
108
+ if (config.limit !== undefined && config.limit > 0) {
109
+ repos = repos.slice(0, config.limit);
110
+ }
111
+ log(config, `Processing ${repos.length} repos`);
112
+ const allFileResults = [];
113
+ for (const repo of repos) {
114
+ const key = repoKey(repo);
115
+ // 3. Skip completed repos on resume
116
+ if (config.resume) {
117
+ const repoProgress = progress[key];
118
+ if (repoProgress?.status === 'complete') {
119
+ log(config, `[SKIP] ${key} — already complete`);
120
+ continue;
121
+ }
122
+ }
123
+ log(config, `[START] ${key}`);
124
+ const repoDir = join(config.workDir, repo.name);
125
+ // 4a. Clone
126
+ const cloneOk = cloneRepo(repo, repoDir);
127
+ if (!cloneOk) {
128
+ log(config, `[FAIL] ${key} — clone failed`);
129
+ continue;
130
+ }
131
+ log(config, `[CLONED] ${key}`);
132
+ // 4b. Install deps (best-effort)
133
+ const installOk = installDeps(repoDir);
134
+ if (!installOk) {
135
+ log(config, `[WARN] ${key} — npm install failed (continuing)`);
136
+ }
137
+ // 4c. Run tsc baseline
138
+ const tscResult = runTscCheck(repoDir);
139
+ const tscOutput = tscResult?.errors ?? null;
140
+ log(config, `[TSC] ${key} — exit ${tscResult?.exitCode ?? 'null'}, ${tscOutput?.length ?? 0} chars`);
141
+ // 4d. List all .ts/.tsx/.js/.jsx files
142
+ const allFiles = listTsFiles(repoDir);
143
+ log(config, `[FILES] ${key} — ${allFiles.length} files found`);
144
+ // 4e. Select files
145
+ const selectedFiles = selectFiles(allFiles);
146
+ log(config, `[SELECTED] ${key} — ${selectedFiles.length} files to scan`);
147
+ // Initialize repo in progress
148
+ progress = initRepo(progress, key);
149
+ // 4f. Scan each selected file
150
+ for (const selectedFile of selectedFiles) {
151
+ const absFilePath = join(repoDir, selectedFile.path);
152
+ const fileState = getFileState(progress, key, selectedFile.path);
153
+ // Skip already-learned/confirmed files on resume
154
+ if (config.resume && (fileState === 'learned' || fileState === 'confirmed')) {
155
+ log(config, ` [SKIP] ${selectedFile.path} — ${fileState}`);
156
+ continue;
157
+ }
158
+ const fileStart = Date.now();
159
+ const result = {
160
+ repo: key,
161
+ filePath: selectedFile.path,
162
+ claimsExtracted: 0,
163
+ confirmed: 0,
164
+ rulesLearned: 0,
165
+ rulesRejected: 0,
166
+ rulesDuplicate: 0,
167
+ unmatchedCategories: [],
168
+ durationMs: 0,
169
+ };
170
+ // Read file content
171
+ let code;
172
+ try {
173
+ code = await readFile(absFilePath, 'utf-8');
174
+ }
175
+ catch (err) {
176
+ log(config, ` [WARN] ${selectedFile.path} — unreadable: ${String(err)}`);
177
+ continue;
178
+ }
179
+ // Scan with timeout (clear timer on success to avoid accumulating pending timers)
180
+ let scanOutput = null;
181
+ let timer;
182
+ try {
183
+ const scanPromise = scanFile(code, 'typescript');
184
+ const timeoutPromise = new Promise((_, reject) => {
185
+ timer = setTimeout(() => reject(new Error('scanFile timeout')), 1_200_000);
186
+ });
187
+ scanOutput = await Promise.race([scanPromise, timeoutPromise]);
188
+ }
189
+ catch (err) {
190
+ log(config, ` [SCAN_ERR] ${selectedFile.path} — ${String(err)}`);
191
+ progress = setFileState(progress, key, selectedFile.path, 'scanned');
192
+ await saveProgress(config.progressDir, progress);
193
+ result.durationMs = Date.now() - fileStart;
194
+ allFileResults.push(result);
195
+ continue;
196
+ }
197
+ finally {
198
+ if (timer)
199
+ clearTimeout(timer);
200
+ }
201
+ result.claimsExtracted = scanOutput.result.totalClaims;
202
+ // Update progress to scanned
203
+ progress = setFileState(progress, key, selectedFile.path, 'scanned');
204
+ // For each FAIL/PARTIAL finding, confirm and learn
205
+ for (const failure of scanOutput.result.failures) {
206
+ const claimForConfirmation = {
207
+ claimId: failure.claimId,
208
+ category: failure.category,
209
+ description: failure.description,
210
+ verdict: 'FAIL',
211
+ };
212
+ const groundTruth = parseConfirmation(code, claimForConfirmation, tscOutput, selectedFile.path);
213
+ if (!groundTruth.confirmed)
214
+ continue;
215
+ result.confirmed += 1;
216
+ progress = setFileState(progress, key, selectedFile.path, 'confirmed');
217
+ // Coerce severity: low → medium (ExtractedPattern.severity doesn't include 'low')
218
+ const rawSeverity = failure.severity;
219
+ const coercedSeverity = rawSeverity === 'critical' || rawSeverity === 'high' || rawSeverity === 'medium'
220
+ ? rawSeverity
221
+ : 'medium';
222
+ const extractionInput = {
223
+ claim: {
224
+ id: failure.claimId,
225
+ category: failure.category,
226
+ severity: coercedSeverity,
227
+ description: failure.description,
228
+ assertion: failure.assertion,
229
+ },
230
+ verification: {
231
+ verdict: 'FAIL',
232
+ reasoning: failure.reasoning,
233
+ },
234
+ code,
235
+ language: 'typescript',
236
+ filePath: selectedFile.path,
237
+ };
238
+ let learnResult;
239
+ try {
240
+ learnResult = await learnFromFinding(config.catalogPath, extractionInput);
241
+ }
242
+ catch (err) {
243
+ log(config, ` [LEARN_ERR] ${selectedFile.path}:${failure.claimId} — ${String(err)}`);
244
+ result.rulesRejected += 1;
245
+ continue;
246
+ }
247
+ if (learnResult.learned) {
248
+ result.rulesLearned += 1;
249
+ progress = setFileState(progress, key, selectedFile.path, 'learned');
250
+ }
251
+ else {
252
+ const reason = learnResult.reason ?? '';
253
+ if (reason.includes('No extraction strategy')) {
254
+ result.unmatchedCategories.push(failure.category);
255
+ }
256
+ else if (reason.includes('duplicate')) {
257
+ result.rulesDuplicate += 1;
258
+ }
259
+ else {
260
+ result.rulesRejected += 1;
261
+ }
262
+ }
263
+ }
264
+ result.durationMs = Date.now() - fileStart;
265
+ allFileResults.push(result);
266
+ // Save after each file (Ctrl+C safe)
267
+ await saveProgress(config.progressDir, progress);
268
+ log(config, ` [FILE] ${selectedFile.path} — ${result.claimsExtracted} claims, ${result.confirmed} confirmed, ${result.rulesLearned} learned (${result.durationMs}ms)`);
269
+ }
270
+ // 4g. Delete clone
271
+ try {
272
+ await rm(repoDir, { recursive: true, force: true });
273
+ log(config, `[CLEANED] ${key}`);
274
+ }
275
+ catch (err) {
276
+ log(config, `[WARN] ${key} — rm failed: ${String(err)}`);
277
+ }
278
+ log(config, `[DONE] ${key}`);
279
+ }
280
+ // 5. Generate and save run report
281
+ const catalogSummary = await getLearnedRulesSummary(config.catalogPath);
282
+ const catalogSize = {
283
+ total: catalogSummary.total,
284
+ promoted: catalogSummary.promoted,
285
+ rejected: catalogSummary.rejected,
286
+ };
287
+ const report = createRunReport(allFileResults, config.model, catalogSize);
288
+ const reportPath = await saveRunReport(config.runsDir, report);
289
+ // 6. Print summary
290
+ const totalDurationMin = ((Date.now() - startTime) / 60_000).toFixed(1);
291
+ log(config, '');
292
+ log(config, '══════════════════════════════════════');
293
+ log(config, ' HARVEST COMPLETE');
294
+ log(config, '══════════════════════════════════════');
295
+ log(config, ` Repos scanned: ${report.reposScanned}`);
296
+ log(config, ` Files scanned: ${report.filesScanned}`);
297
+ log(config, ` Claims extracted: ${report.claimsExtracted}`);
298
+ log(config, ` Findings confirmed: ${report.findingsConfirmed}`);
299
+ log(config, ` Rules learned: ${report.rulesLearned}`);
300
+ log(config, ` Rules rejected: ${report.rulesRejected}`);
301
+ log(config, ` Rules duplicate: ${report.rulesDuplicate}`);
302
+ log(config, ` Catalog total: ${catalogSize.total} (${catalogSize.promoted} promoted)`);
303
+ log(config, ` Duration: ${totalDurationMin}m`);
304
+ log(config, ` Report: ${reportPath}`);
305
+ log(config, '══════════════════════════════════════');
306
+ }
307
+ //# sourceMappingURL=harvest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harvest.js","sourceRoot":"","sources":["../../../src/lib/rule-harvester/harvest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import type { DiscoveredPR, ParsedPRDiff } from '../learned-rules/types.js';
2
+ /** Default labels that indicate a bug-fix PR. Matched case-insensitively as substrings. */
3
+ export declare const DEFAULT_BUG_LABELS: readonly string[];
4
+ /**
5
+ * Returns true if any label in the array contains a bug-related keyword.
6
+ * Matches case-insensitively. Accepts optional custom bug label list.
7
+ */
8
+ export declare function matchesBugLabels(labels: string[], bugLabels?: readonly string[]): boolean;
9
+ /**
10
+ * Returns true if the PR title matches common bug-fix prefixes.
11
+ * Looks for fix/bug/resolve/patch at the start or after a conventional commit prefix.
12
+ */
13
+ export declare function matchesBugTitle(title: string): boolean;
14
+ /**
15
+ * Returns true if the file path is a TypeScript/JavaScript source file
16
+ * that is NOT a test file or declaration file.
17
+ */
18
+ export declare function isTargetFile(path: string): boolean;
19
+ interface DiscoveryConfig {
20
+ /** Max PRs to fetch per page (GitHub max: 100). */
21
+ readonly perPage?: number;
22
+ /** Max pages to paginate through. */
23
+ readonly maxPages?: number;
24
+ /** Custom bug labels to match. Defaults to DEFAULT_BUG_LABELS. */
25
+ readonly bugLabels?: readonly string[];
26
+ /** Whether to fall back to title matching if no labels match. */
27
+ readonly titleFallback?: boolean;
28
+ /** Whether to fetch review comments for each PR. */
29
+ readonly fetchComments?: boolean;
30
+ }
31
+ /**
32
+ * Discover merged bug-fix PRs from a GitHub repo.
33
+ *
34
+ * Flow:
35
+ * 1. Fetch closed PRs (paginated)
36
+ * 2. Filter to merged-only (merged_at !== null)
37
+ * 3. Filter by bug labels (with optional title fallback)
38
+ * 4. Optionally enrich with review comments
39
+ * 5. Collect changed file paths
40
+ */
41
+ export declare function discoverBugFixPRs(owner: string, name: string, config?: DiscoveryConfig): DiscoveredPR[];
42
+ /**
43
+ * Fetch and parse diffs for a discovered PR.
44
+ *
45
+ * Calls the files endpoint, filters to target files,
46
+ * and parses each file's patch into typed DiffHunks.
47
+ */
48
+ export declare function fetchPRDiffs(owner: string, name: string, pr: DiscoveredPR): ParsedPRDiff;
49
+ export {};