@oss-scout/core 1.1.0 → 1.2.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.
|
@@ -28,6 +28,20 @@ const MODULE = "issue-discovery";
|
|
|
28
28
|
const LOW_BUDGET_THRESHOLD = 20;
|
|
29
29
|
/** If remaining search quota is below this, only run Phase 0. */
|
|
30
30
|
const CRITICAL_BUDGET_THRESHOLD = 10;
|
|
31
|
+
/**
|
|
32
|
+
* Page size for Phase 0 (repos the user has contributed to). Larger than the
|
|
33
|
+
* default 5 so the backlog of open issues in known repos is reachable, not
|
|
34
|
+
* just the 5 newest-created. One `listForRepo` call regardless of page size,
|
|
35
|
+
* so this widens the candidate pool at no extra REST cost.
|
|
36
|
+
*/
|
|
37
|
+
const PHASE0_PER_PAGE = 30;
|
|
38
|
+
/**
|
|
39
|
+
* Max issue age (by last activity) for Phase 0 contributed repos. Relaxed well
|
|
40
|
+
* past the default `maxIssueAgeDays` (90) because in a repo the user already
|
|
41
|
+
* knows, an older-but-still-open issue is still worth evaluating — the vetter
|
|
42
|
+
* screens staleness, existing PRs, and claims downstream.
|
|
43
|
+
*/
|
|
44
|
+
const CONTRIBUTED_REPO_MAX_AGE_DAYS = 365;
|
|
31
45
|
/** Build a reusable filter function from config. */
|
|
32
46
|
function buildIssueFilter(config) {
|
|
33
47
|
return (items) => {
|
|
@@ -63,8 +77,8 @@ function buildIssueFilter(config) {
|
|
|
63
77
|
}
|
|
64
78
|
/** Phase 0: Search repos where user has merged PRs (highest merge probability). */
|
|
65
79
|
async function runPhase0(octokit, vetter, repos, maxResults, filterIssues) {
|
|
66
|
-
info(MODULE, `Phase 0: Searching issues in ${repos.length} merged-PR repos (no label filter)...`);
|
|
67
|
-
const { candidates, allReposFailed, rateLimitHit } = await fetchIssuesFromKnownRepos(octokit, vetter, repos, [], maxResults, "merged_pr", filterIssues);
|
|
80
|
+
info(MODULE, `Phase 0: Searching issues in ${repos.length} merged-PR repos (no label filter, ${PHASE0_PER_PAGE}/repo)...`);
|
|
81
|
+
const { candidates, allReposFailed, rateLimitHit } = await fetchIssuesFromKnownRepos(octokit, vetter, repos, [], maxResults, "merged_pr", filterIssues, PHASE0_PER_PAGE);
|
|
68
82
|
info(MODULE, `Found ${candidates.length} candidates from merged-PR repos`);
|
|
69
83
|
return {
|
|
70
84
|
candidates,
|
|
@@ -374,15 +388,24 @@ export class IssueDiscovery {
|
|
|
374
388
|
if (aiBlocklisted.size > 0) {
|
|
375
389
|
debug(MODULE, `[AI_POLICY_FILTER] Filtering issues from ${aiBlocklisted.size} blocklisted repo(s): ${[...aiBlocklisted].join(", ")}`);
|
|
376
390
|
}
|
|
377
|
-
const
|
|
391
|
+
const baseFilterConfig = {
|
|
378
392
|
excludedRepos: new Set(config.excludeRepos.map((r) => r.toLowerCase())),
|
|
379
393
|
excludeOrgs: new Set((config.excludeOrgs ?? []).map((o) => o.toLowerCase())),
|
|
380
394
|
aiBlocklisted,
|
|
381
395
|
lowScoringRepos,
|
|
382
396
|
skippedUrls: options.skippedUrls ?? new Set(),
|
|
383
|
-
maxAgeDays: config.maxIssueAgeDays || 90,
|
|
384
397
|
now: new Date(),
|
|
385
398
|
includeDocIssues: config.includeDocIssues ?? true,
|
|
399
|
+
};
|
|
400
|
+
const filterIssues = buildIssueFilter({
|
|
401
|
+
...baseFilterConfig,
|
|
402
|
+
maxAgeDays: config.maxIssueAgeDays || 90,
|
|
403
|
+
});
|
|
404
|
+
// Phase 0 (contributed repos) gets a relaxed age window so the existing
|
|
405
|
+
// backlog surfaces, not just issues active in the last 90 days.
|
|
406
|
+
const filterIssuesPhase0 = buildIssueFilter({
|
|
407
|
+
...baseFilterConfig,
|
|
408
|
+
maxAgeDays: CONTRIBUTED_REPO_MAX_AGE_DAYS,
|
|
386
409
|
});
|
|
387
410
|
// Phase 0: Repos the user has engaged with — merged PRs first (strongest
|
|
388
411
|
// signal), then open PRs (active engagement even without a merge yet).
|
|
@@ -401,7 +424,7 @@ export class IssueDiscovery {
|
|
|
401
424
|
if (phase0Repos.length > 0 && enabledStrategies.has("merged")) {
|
|
402
425
|
const remaining = maxResults - allCandidates.length;
|
|
403
426
|
if (remaining > 0) {
|
|
404
|
-
const result = await runPhase0(this.octokit, this.vetter, phase0Repos, remaining,
|
|
427
|
+
const result = await runPhase0(this.octokit, this.vetter, phase0Repos, remaining, filterIssuesPhase0);
|
|
405
428
|
recordPhaseResult("0", result);
|
|
406
429
|
}
|
|
407
430
|
strategiesUsed.push("merged");
|
|
@@ -43,7 +43,7 @@ export declare function fetchIssuesFromMaintainedRepos(octokit: Octokit, repos:
|
|
|
43
43
|
* calls `GET /repos/{owner}/{repo}/issues` which counts against the much
|
|
44
44
|
* larger Core API rate limit and avoids consuming the scarce Search quota.
|
|
45
45
|
*/
|
|
46
|
-
export declare function fetchIssuesFromKnownRepos(octokit: Octokit, vetter: IssueVetter, repos: string[], labels: string[], maxResults: number, priority: SearchPriority, filterFn: (items: GitHubSearchItem[]) => GitHubSearchItem[]): Promise<{
|
|
46
|
+
export declare function fetchIssuesFromKnownRepos(octokit: Octokit, vetter: IssueVetter, repos: string[], labels: string[], maxResults: number, priority: SearchPriority, filterFn: (items: GitHubSearchItem[]) => GitHubSearchItem[], perPage?: number): Promise<{
|
|
47
47
|
candidates: IssueCandidate[];
|
|
48
48
|
allReposFailed: boolean;
|
|
49
49
|
rateLimitHit: boolean;
|
|
@@ -186,7 +186,7 @@ export async function fetchIssuesFromMaintainedRepos(octokit, repos, minStars, m
|
|
|
186
186
|
* calls `GET /repos/{owner}/{repo}/issues` which counts against the much
|
|
187
187
|
* larger Core API rate limit and avoids consuming the scarce Search quota.
|
|
188
188
|
*/
|
|
189
|
-
export async function fetchIssuesFromKnownRepos(octokit, vetter, repos, labels, maxResults, priority, filterFn) {
|
|
189
|
+
export async function fetchIssuesFromKnownRepos(octokit, vetter, repos, labels, maxResults, priority, filterFn, perPage = 5) {
|
|
190
190
|
const candidates = [];
|
|
191
191
|
let failedRepos = 0;
|
|
192
192
|
let rateLimitFailures = 0;
|
|
@@ -213,7 +213,7 @@ export async function fetchIssuesFromKnownRepos(octokit, vetter, repos, labels,
|
|
|
213
213
|
state: "open",
|
|
214
214
|
sort: "created",
|
|
215
215
|
direction: "desc",
|
|
216
|
-
per_page:
|
|
216
|
+
per_page: perPage,
|
|
217
217
|
...(label !== undefined ? { labels: label } : {}),
|
|
218
218
|
});
|
|
219
219
|
for (const issue of response.data) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oss-scout/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Personalized GitHub issue finder with multi-strategy search, deep vetting, and viability scoring — CLI, library, MCP server, and Claude Code plugin",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|