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.
- package/dist/cli.js +10 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/assess.js +4 -2
- package/dist/commands/assess.js.map +1 -1
- 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/claim-extractor.d.ts +1 -0
- package/dist/lib/claim-extractor.js +116 -15
- package/dist/lib/claim-extractor.js.map +1 -1
- package/dist/lib/code-verifier.d.ts +12 -1
- package/dist/lib/code-verifier.js +155 -12
- package/dist/lib/code-verifier.js.map +1 -1
- 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 +105 -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/llm-provider.d.ts +7 -0
- package/dist/lib/llm-provider.js +99 -6
- package/dist/lib/llm-provider.js.map +1 -1
- 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/runtime/types.d.ts +1 -1
- package/dist/sdk/forward-verify.d.ts +3 -1
- package/dist/sdk/forward-verify.js +68 -5
- package/dist/sdk/forward-verify.js.map +1 -1
- package/dist/sdk/index.d.ts +1 -1
- package/dist/sdk/index.js +7 -5
- package/dist/sdk/index.js.map +1 -1
- package/dist/sdk/types.d.ts +21 -0
- 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 {};
|