@oss-scout/mcp 0.3.0 → 0.4.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/mcp-server.bundle.cjs +201 -79
- package/dist/tools.js +3 -0
- package/package.json +2 -2
|
@@ -35817,8 +35817,11 @@ var ScoutPreferencesSchema = external_exports3.object({
|
|
|
35817
35817
|
maxIssueAgeDays: external_exports3.number().default(90),
|
|
35818
35818
|
includeDocIssues: external_exports3.boolean().default(true),
|
|
35819
35819
|
minRepoScoreThreshold: external_exports3.number().default(4),
|
|
35820
|
+
interPhaseDelayMs: external_exports3.number().min(0).max(12e4).default(3e4),
|
|
35820
35821
|
persistence: PersistenceModeSchema.default("local"),
|
|
35821
|
-
defaultStrategy: external_exports3.array(SearchStrategySchema).optional()
|
|
35822
|
+
defaultStrategy: external_exports3.array(SearchStrategySchema).optional(),
|
|
35823
|
+
broadPhaseDelayMs: external_exports3.number().min(0).max(3e5).default(9e4),
|
|
35824
|
+
skipBroadWhenSufficientResults: external_exports3.number().int().min(0).max(100).default(15)
|
|
35822
35825
|
});
|
|
35823
35826
|
var ScoutStateSchema = external_exports3.object({
|
|
35824
35827
|
version: external_exports3.literal(1),
|
|
@@ -36833,7 +36836,6 @@ var IssueVetter = class {
|
|
|
36833
36836
|
var MODULE8 = "search-phases";
|
|
36834
36837
|
var GITHUB_MAX_BOOLEAN_OPS = 5;
|
|
36835
36838
|
var INTER_QUERY_DELAY_MS = 2e3;
|
|
36836
|
-
var BATCH_SIZE = 3;
|
|
36837
36839
|
function chunkLabels(labels, reservedOps = 0) {
|
|
36838
36840
|
const maxPerChunk = GITHUB_MAX_BOOLEAN_OPS - reservedOps + 1;
|
|
36839
36841
|
if (maxPerChunk < 1) {
|
|
@@ -36879,26 +36881,138 @@ function interleaveArrays(arrays) {
|
|
|
36879
36881
|
}
|
|
36880
36882
|
return result;
|
|
36881
36883
|
}
|
|
36882
|
-
function batchRepos(repos, batchSize) {
|
|
36883
|
-
const batches = [];
|
|
36884
|
-
for (let i = 0; i < repos.length; i += batchSize) {
|
|
36885
|
-
batches.push(repos.slice(i, i + batchSize));
|
|
36886
|
-
}
|
|
36887
|
-
return batches;
|
|
36888
|
-
}
|
|
36889
36884
|
var SEARCH_CACHE_TTL_MS = 15 * 60 * 1e3;
|
|
36890
36885
|
async function cachedSearchIssues(octokit, params) {
|
|
36891
36886
|
const cacheKey = `search:${params.q}:${params.sort}:${params.order}:${params.per_page}`;
|
|
36892
|
-
|
|
36893
|
-
|
|
36894
|
-
|
|
36887
|
+
const cache = getHttpCache();
|
|
36888
|
+
const cached2 = cache.getIfFresh(cacheKey, SEARCH_CACHE_TTL_MS);
|
|
36889
|
+
if (cached2) {
|
|
36890
|
+
debug(MODULE8, `Search cache hit for query`);
|
|
36891
|
+
return cached2;
|
|
36892
|
+
}
|
|
36893
|
+
const tracker = getSearchBudgetTracker();
|
|
36894
|
+
await tracker.waitForBudget();
|
|
36895
|
+
let data;
|
|
36896
|
+
try {
|
|
36897
|
+
const response = await octokit.search.issuesAndPullRequests(params);
|
|
36898
|
+
data = response.data;
|
|
36899
|
+
} finally {
|
|
36900
|
+
tracker.recordCall();
|
|
36901
|
+
}
|
|
36902
|
+
if (data.items.length > 0) {
|
|
36903
|
+
cache.set(cacheKey, "", data);
|
|
36904
|
+
} else {
|
|
36905
|
+
debug(MODULE8, `Skipping cache for empty search result (possible rate limit artifact)`);
|
|
36906
|
+
}
|
|
36907
|
+
return data;
|
|
36908
|
+
}
|
|
36909
|
+
async function fetchIssuesFromMaintainedRepos(octokit, repos, minStars, maxResults) {
|
|
36910
|
+
const items = [];
|
|
36911
|
+
for (const repoFullName of repos) {
|
|
36912
|
+
if (items.length >= maxResults * 3)
|
|
36913
|
+
break;
|
|
36914
|
+
const [owner, repo] = repoFullName.split("/");
|
|
36915
|
+
if (!owner || !repo)
|
|
36916
|
+
continue;
|
|
36895
36917
|
try {
|
|
36896
|
-
const { data } = await octokit.
|
|
36897
|
-
|
|
36898
|
-
|
|
36899
|
-
|
|
36918
|
+
const { data: repoData } = await octokit.repos.get({ owner, repo });
|
|
36919
|
+
if (!repoData.pushed_at)
|
|
36920
|
+
continue;
|
|
36921
|
+
const pushedAt = new Date(repoData.pushed_at);
|
|
36922
|
+
const thirtyDaysAgo = /* @__PURE__ */ new Date();
|
|
36923
|
+
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
|
|
36924
|
+
if (pushedAt < thirtyDaysAgo)
|
|
36925
|
+
continue;
|
|
36926
|
+
if ((repoData.stargazers_count ?? 0) < minStars)
|
|
36927
|
+
continue;
|
|
36928
|
+
if (repoData.archived)
|
|
36929
|
+
continue;
|
|
36930
|
+
const { data: issues } = await octokit.issues.listForRepo({
|
|
36931
|
+
owner,
|
|
36932
|
+
repo,
|
|
36933
|
+
state: "open",
|
|
36934
|
+
sort: "created",
|
|
36935
|
+
direction: "desc",
|
|
36936
|
+
per_page: 5
|
|
36937
|
+
});
|
|
36938
|
+
const realIssues = issues.filter((i) => !i.pull_request && !i.assignee);
|
|
36939
|
+
for (const issue2 of realIssues) {
|
|
36940
|
+
items.push({
|
|
36941
|
+
html_url: issue2.html_url,
|
|
36942
|
+
repository_url: `https://api.github.com/repos/${repoFullName}`,
|
|
36943
|
+
updated_at: issue2.updated_at ?? "",
|
|
36944
|
+
title: issue2.title,
|
|
36945
|
+
labels: issue2.labels
|
|
36946
|
+
});
|
|
36947
|
+
}
|
|
36948
|
+
await sleep(INTER_QUERY_DELAY_MS);
|
|
36949
|
+
} catch (error48) {
|
|
36950
|
+
if (getHttpStatusCode(error48) === 401)
|
|
36951
|
+
throw error48;
|
|
36952
|
+
if (isRateLimitError(error48)) {
|
|
36953
|
+
warn(MODULE8, `Rate limit hit fetching issues from ${repoFullName}:`, errorMessage(error48));
|
|
36954
|
+
break;
|
|
36955
|
+
}
|
|
36956
|
+
warn(MODULE8, `Error fetching issues from ${repoFullName}:`, errorMessage(error48));
|
|
36900
36957
|
}
|
|
36901
|
-
}
|
|
36958
|
+
}
|
|
36959
|
+
return items;
|
|
36960
|
+
}
|
|
36961
|
+
async function fetchIssuesFromKnownRepos(octokit, vetter, repos, labels, maxResults, priority, filterFn) {
|
|
36962
|
+
const candidates = [];
|
|
36963
|
+
let failedRepos = 0;
|
|
36964
|
+
let rateLimitFailures = 0;
|
|
36965
|
+
for (let i = 0; i < repos.length; i++) {
|
|
36966
|
+
if (candidates.length >= maxResults)
|
|
36967
|
+
break;
|
|
36968
|
+
if (i > 0)
|
|
36969
|
+
await sleep(INTER_QUERY_DELAY_MS);
|
|
36970
|
+
const repoFullName = repos[i];
|
|
36971
|
+
const [owner, repo] = repoFullName.split("/");
|
|
36972
|
+
try {
|
|
36973
|
+
const response = await octokit.issues.listForRepo({
|
|
36974
|
+
owner,
|
|
36975
|
+
repo,
|
|
36976
|
+
state: "open",
|
|
36977
|
+
sort: "created",
|
|
36978
|
+
direction: "desc",
|
|
36979
|
+
per_page: 5,
|
|
36980
|
+
...labels.length > 0 ? { labels: labels.join(",") } : {}
|
|
36981
|
+
});
|
|
36982
|
+
const issuesOnly = response.data.filter((item) => !("pull_request" in item) && !item.assignee);
|
|
36983
|
+
const mapped = issuesOnly.map((issue2) => ({
|
|
36984
|
+
html_url: issue2.html_url,
|
|
36985
|
+
repository_url: `https://api.github.com/repos/${repoFullName}`,
|
|
36986
|
+
updated_at: issue2.updated_at ?? "",
|
|
36987
|
+
title: issue2.title,
|
|
36988
|
+
labels: issue2.labels
|
|
36989
|
+
}));
|
|
36990
|
+
if (mapped.length > 0) {
|
|
36991
|
+
const filtered = filterFn(mapped);
|
|
36992
|
+
if (filtered.length > 0) {
|
|
36993
|
+
const remainingNeeded = maxResults - candidates.length;
|
|
36994
|
+
const { candidates: vetted, rateLimitHit: vetRateLimitHit } = await vetter.vetIssuesParallel(filtered.slice(0, remainingNeeded * 2).map((item) => item.html_url), remainingNeeded, priority);
|
|
36995
|
+
candidates.push(...vetted);
|
|
36996
|
+
if (vetRateLimitHit)
|
|
36997
|
+
rateLimitFailures++;
|
|
36998
|
+
}
|
|
36999
|
+
}
|
|
37000
|
+
} catch (error48) {
|
|
37001
|
+
if (getHttpStatusCode(error48) === 401)
|
|
37002
|
+
throw error48;
|
|
37003
|
+
failedRepos++;
|
|
37004
|
+
if (isRateLimitError(error48)) {
|
|
37005
|
+
rateLimitFailures++;
|
|
37006
|
+
}
|
|
37007
|
+
warn(MODULE8, `Error fetching issues from ${repoFullName}:`, errorMessage(error48));
|
|
37008
|
+
}
|
|
37009
|
+
}
|
|
37010
|
+
const allReposFailed = failedRepos === repos.length && repos.length > 0;
|
|
37011
|
+
const rateLimitHit = rateLimitFailures > 0;
|
|
37012
|
+
if (allReposFailed) {
|
|
37013
|
+
warn(MODULE8, `All ${repos.length} repo(s) failed for ${priority} phase. This may indicate a systemic issue (rate limit, auth, network).`);
|
|
37014
|
+
}
|
|
37015
|
+
return { candidates, allReposFailed, rateLimitHit };
|
|
36902
37016
|
}
|
|
36903
37017
|
async function searchWithChunkedLabels(octokit, labels, reservedOps, buildQuery, perPage) {
|
|
36904
37018
|
const labelChunks = chunkLabels(labels, reservedOps);
|
|
@@ -36954,50 +37068,9 @@ async function filterVetAndScore(vetter, items, filterIssues, excludedRepoSets,
|
|
|
36954
37068
|
}
|
|
36955
37069
|
return { candidates: starFiltered, allVetFailed, rateLimitHit };
|
|
36956
37070
|
}
|
|
36957
|
-
async function searchInRepos(octokit, vetter, repos, baseQualifiers, labels, maxResults, priority, filterFn) {
|
|
36958
|
-
const candidates = [];
|
|
36959
|
-
const batches = batchRepos(repos, BATCH_SIZE);
|
|
36960
|
-
let failedBatches = 0;
|
|
36961
|
-
let rateLimitFailures = 0;
|
|
36962
|
-
for (let batchIdx = 0; batchIdx < batches.length; batchIdx++) {
|
|
36963
|
-
const batch = batches[batchIdx];
|
|
36964
|
-
if (candidates.length >= maxResults)
|
|
36965
|
-
break;
|
|
36966
|
-
if (batchIdx > 0)
|
|
36967
|
-
await sleep(INTER_QUERY_DELAY_MS);
|
|
36968
|
-
try {
|
|
36969
|
-
const repoFilter = batch.map((r) => `repo:${r}`).join(" OR ");
|
|
36970
|
-
const repoOps = batch.length - 1;
|
|
36971
|
-
const perPage = Math.min(30, (maxResults - candidates.length) * 3);
|
|
36972
|
-
const allItems = await searchWithChunkedLabels(octokit, labels, repoOps, (labelQ) => `${baseQualifiers} ${labelQ} (${repoFilter})`.replace(/ +/g, " ").trim(), perPage);
|
|
36973
|
-
if (allItems.length > 0) {
|
|
36974
|
-
const filtered = filterFn(allItems);
|
|
36975
|
-
const remainingNeeded = maxResults - candidates.length;
|
|
36976
|
-
const { candidates: vetted, rateLimitHit: vetRateLimitHit } = await vetter.vetIssuesParallel(filtered.slice(0, remainingNeeded * 2).map((i) => i.html_url), remainingNeeded, priority);
|
|
36977
|
-
candidates.push(...vetted);
|
|
36978
|
-
if (vetRateLimitHit)
|
|
36979
|
-
rateLimitFailures++;
|
|
36980
|
-
}
|
|
36981
|
-
} catch (error48) {
|
|
36982
|
-
failedBatches++;
|
|
36983
|
-
if (isRateLimitError(error48)) {
|
|
36984
|
-
rateLimitFailures++;
|
|
36985
|
-
}
|
|
36986
|
-
const batchReposStr = batch.join(", ");
|
|
36987
|
-
warn(MODULE8, `Error searching issues in batch [${batchReposStr}]:`, errorMessage(error48));
|
|
36988
|
-
}
|
|
36989
|
-
}
|
|
36990
|
-
const allBatchesFailed = failedBatches === batches.length && batches.length > 0;
|
|
36991
|
-
const rateLimitHit = rateLimitFailures > 0;
|
|
36992
|
-
if (allBatchesFailed) {
|
|
36993
|
-
warn(MODULE8, `All ${batches.length} batch(es) failed for ${priority} phase. This may indicate a systemic issue (rate limit, auth, network).`);
|
|
36994
|
-
}
|
|
36995
|
-
return { candidates, allBatchesFailed, rateLimitHit };
|
|
36996
|
-
}
|
|
36997
37071
|
|
|
36998
37072
|
// ../core/dist/core/issue-discovery.js
|
|
36999
37073
|
var MODULE9 = "issue-discovery";
|
|
37000
|
-
var INTER_PHASE_DELAY_MS = 2e3;
|
|
37001
37074
|
var LOW_BUDGET_THRESHOLD = 20;
|
|
37002
37075
|
var CRITICAL_BUDGET_THRESHOLD = 10;
|
|
37003
37076
|
function buildIssueFilter(config2) {
|
|
@@ -37029,24 +37102,25 @@ function buildIssueFilter(config2) {
|
|
|
37029
37102
|
});
|
|
37030
37103
|
};
|
|
37031
37104
|
}
|
|
37032
|
-
async function runPhase0(octokit, vetter, repos,
|
|
37105
|
+
async function runPhase0(octokit, vetter, repos, maxResults, filterIssues) {
|
|
37033
37106
|
info(MODULE9, `Phase 0: Searching issues in ${repos.length} merged-PR repos (no label filter)...`);
|
|
37034
|
-
const { candidates,
|
|
37107
|
+
const { candidates, allReposFailed, rateLimitHit } = await fetchIssuesFromKnownRepos(octokit, vetter, repos, [], maxResults, "merged_pr", filterIssues);
|
|
37035
37108
|
info(MODULE9, `Found ${candidates.length} candidates from merged-PR repos`);
|
|
37036
37109
|
return {
|
|
37037
37110
|
candidates,
|
|
37038
|
-
error:
|
|
37111
|
+
error: allReposFailed ? "All merged-PR repo fetches failed" : null,
|
|
37039
37112
|
rateLimitHit
|
|
37040
37113
|
};
|
|
37041
37114
|
}
|
|
37042
|
-
async function runPhase1(octokit, vetter, repos,
|
|
37115
|
+
async function runPhase1(octokit, vetter, repos, labels, maxResults, filterIssues) {
|
|
37043
37116
|
info(MODULE9, `Phase 1: Searching issues in ${repos.length} starred repos...`);
|
|
37044
37117
|
const phase1Labels = labels.slice(0, 3);
|
|
37045
|
-
const
|
|
37118
|
+
const reposToSearch = repos.slice(0, 10);
|
|
37119
|
+
const { candidates, allReposFailed, rateLimitHit } = await fetchIssuesFromKnownRepos(octokit, vetter, reposToSearch, phase1Labels, maxResults, "starred", filterIssues);
|
|
37046
37120
|
info(MODULE9, `Found ${candidates.length} candidates from starred repos`);
|
|
37047
37121
|
return {
|
|
37048
37122
|
candidates,
|
|
37049
|
-
error:
|
|
37123
|
+
error: allReposFailed ? "All starred repo fetches failed" : null,
|
|
37050
37124
|
rateLimitHit
|
|
37051
37125
|
};
|
|
37052
37126
|
}
|
|
@@ -37112,8 +37186,32 @@ async function runPhase2(octokit, vetter, scopes, labels, configLabels, baseQual
|
|
|
37112
37186
|
rateLimitHit
|
|
37113
37187
|
};
|
|
37114
37188
|
}
|
|
37115
|
-
async function runPhase3(octokit, vetter, langQuery, minStars, projectCategories, maxResults, phase0RepoSet, starredRepoSet, existingCandidates, filterIssues) {
|
|
37189
|
+
async function runPhase3(octokit, vetter, langQuery, minStars, projectCategories, maxResults, phase0RepoSet, starredRepoSet, starredRepos, existingCandidates, filterIssues) {
|
|
37116
37190
|
info(MODULE9, "Phase 3: Searching actively maintained repos...");
|
|
37191
|
+
const seenRepos = new Set(existingCandidates.map((c) => c.issue.repo));
|
|
37192
|
+
const eligibleStarred = starredRepos.filter((r) => !phase0RepoSet.has(r) && !seenRepos.has(r));
|
|
37193
|
+
if (eligibleStarred.length > 0) {
|
|
37194
|
+
info(MODULE9, `Phase 3: Checking ${eligibleStarred.length} starred repos via REST API...`);
|
|
37195
|
+
const restItems = await fetchIssuesFromMaintainedRepos(octokit, eligibleStarred.slice(0, 15), minStars, maxResults);
|
|
37196
|
+
if (restItems.length > 0) {
|
|
37197
|
+
try {
|
|
37198
|
+
const { candidates, allVetFailed, rateLimitHit: vetRateLimitHit } = await filterVetAndScore(vetter, restItems, filterIssues, [phase0RepoSet, seenRepos], maxResults, minStars, "Phase 3 (REST)");
|
|
37199
|
+
if (candidates.length > 0) {
|
|
37200
|
+
info(MODULE9, `Found ${candidates.length} candidates from maintained-repo REST search`);
|
|
37201
|
+
return {
|
|
37202
|
+
candidates,
|
|
37203
|
+
error: allVetFailed ? "all vetting failed" : null,
|
|
37204
|
+
rateLimitHit: vetRateLimitHit
|
|
37205
|
+
};
|
|
37206
|
+
}
|
|
37207
|
+
} catch (error48) {
|
|
37208
|
+
if (getHttpStatusCode(error48) === 401)
|
|
37209
|
+
throw error48;
|
|
37210
|
+
warn(MODULE9, `Phase 3 REST vetting failed, falling back to Search API:`, errorMessage(error48));
|
|
37211
|
+
}
|
|
37212
|
+
}
|
|
37213
|
+
}
|
|
37214
|
+
info(MODULE9, "Phase 3: Falling back to Search API...");
|
|
37117
37215
|
const thirtyDaysAgo = /* @__PURE__ */ new Date();
|
|
37118
37216
|
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
|
|
37119
37217
|
const pushedSince = thirtyDaysAgo.toISOString().split("T")[0];
|
|
@@ -37128,7 +37226,6 @@ async function runPhase3(octokit, vetter, langQuery, minStars, projectCategories
|
|
|
37128
37226
|
per_page: maxResults * 3
|
|
37129
37227
|
});
|
|
37130
37228
|
info(MODULE9, `Found ${data.total_count} issues in maintained-repo search, processing top ${data.items.length}...`);
|
|
37131
|
-
const seenRepos = new Set(existingCandidates.map((c) => c.issue.repo));
|
|
37132
37229
|
const { candidates, allVetFailed, rateLimitHit: vetRateLimitHit } = await filterVetAndScore(vetter, data.items, filterIssues, [phase0RepoSet, starredRepoSet, seenRepos], maxResults, minStars, "Phase 3");
|
|
37133
37230
|
info(MODULE9, `Found ${candidates.length} candidates from maintained-repo search`);
|
|
37134
37231
|
return {
|
|
@@ -37204,6 +37301,7 @@ var IssueDiscovery = class {
|
|
|
37204
37301
|
const labels = options.labels || (scopes ? buildEffectiveLabels(scopes, config2.labels) : config2.labels);
|
|
37205
37302
|
const maxResults = options.maxResults || 10;
|
|
37206
37303
|
const minStars = config2.minStars ?? 50;
|
|
37304
|
+
const interPhaseDelay = config2.interPhaseDelayMs ?? 3e4;
|
|
37207
37305
|
const ALL_STRATEGIES = CONCRETE_STRATEGIES;
|
|
37208
37306
|
const rawStrategies = options.strategies ?? config2.defaultStrategy ?? ["all"];
|
|
37209
37307
|
const enabledStrategies = new Set(rawStrategies.includes("all") ? ALL_STRATEGIES : rawStrategies);
|
|
@@ -37264,7 +37362,7 @@ var IssueDiscovery = class {
|
|
|
37264
37362
|
if (phase0Repos.length > 0 && enabledStrategies.has("merged")) {
|
|
37265
37363
|
const remaining = maxResults - allCandidates.length;
|
|
37266
37364
|
if (remaining > 0) {
|
|
37267
|
-
const result = await runPhase0(this.octokit, this.vetter, phase0Repos,
|
|
37365
|
+
const result = await runPhase0(this.octokit, this.vetter, phase0Repos, remaining, filterIssues);
|
|
37268
37366
|
allCandidates.push(...result.candidates);
|
|
37269
37367
|
phaseErrors["0"] = result.error;
|
|
37270
37368
|
if (result.rateLimitHit)
|
|
@@ -37273,12 +37371,15 @@ var IssueDiscovery = class {
|
|
|
37273
37371
|
strategiesUsed.push("merged");
|
|
37274
37372
|
}
|
|
37275
37373
|
if (allCandidates.length < maxResults && starredRepos.length > 0 && searchBudget >= CRITICAL_BUDGET_THRESHOLD && enabledStrategies.has("starred")) {
|
|
37276
|
-
|
|
37374
|
+
if (interPhaseDelay > 0) {
|
|
37375
|
+
info(MODULE9, `Waiting ${(interPhaseDelay / 1e3).toFixed(0)}s between phases for rate limit management...`);
|
|
37376
|
+
await sleep(interPhaseDelay);
|
|
37377
|
+
}
|
|
37277
37378
|
const reposToSearch = starredRepos.filter((r) => !phase0RepoSet.has(r));
|
|
37278
37379
|
if (reposToSearch.length > 0) {
|
|
37279
37380
|
const remaining = maxResults - allCandidates.length;
|
|
37280
37381
|
if (remaining > 0) {
|
|
37281
|
-
const result = await runPhase1(this.octokit, this.vetter, reposToSearch,
|
|
37382
|
+
const result = await runPhase1(this.octokit, this.vetter, reposToSearch, labels, remaining, filterIssues);
|
|
37282
37383
|
allCandidates.push(...result.candidates);
|
|
37283
37384
|
phaseErrors["1"] = result.error;
|
|
37284
37385
|
if (result.rateLimitHit)
|
|
@@ -37287,20 +37388,38 @@ var IssueDiscovery = class {
|
|
|
37287
37388
|
}
|
|
37288
37389
|
strategiesUsed.push("starred");
|
|
37289
37390
|
}
|
|
37391
|
+
const broadDelay = config2.broadPhaseDelayMs ?? 9e4;
|
|
37392
|
+
const skipThreshold = config2.skipBroadWhenSufficientResults ?? 15;
|
|
37290
37393
|
if (allCandidates.length < maxResults && searchBudget >= LOW_BUDGET_THRESHOLD && enabledStrategies.has("broad")) {
|
|
37291
|
-
|
|
37292
|
-
|
|
37293
|
-
|
|
37294
|
-
|
|
37295
|
-
|
|
37296
|
-
|
|
37297
|
-
|
|
37394
|
+
if (skipThreshold > 0 && allCandidates.length >= skipThreshold) {
|
|
37395
|
+
info(MODULE9, `Skipping broad search: already found ${allCandidates.length} candidates (threshold: ${skipThreshold})`);
|
|
37396
|
+
} else {
|
|
37397
|
+
if (interPhaseDelay > 0) {
|
|
37398
|
+
info(MODULE9, `Waiting ${(interPhaseDelay / 1e3).toFixed(0)}s between phases for rate limit management...`);
|
|
37399
|
+
await sleep(interPhaseDelay);
|
|
37400
|
+
}
|
|
37401
|
+
if (allCandidates.length > 0 && broadDelay > 0) {
|
|
37402
|
+
info(MODULE9, `Waiting ${(broadDelay / 1e3).toFixed(0)}s for rate limit cooldown before broad search...`);
|
|
37403
|
+
await sleep(broadDelay);
|
|
37404
|
+
} else if (allCandidates.length === 0) {
|
|
37405
|
+
info(MODULE9, `Skipping broad phase delay: no results from previous phases, proceeding immediately`);
|
|
37406
|
+
}
|
|
37407
|
+
const remaining = maxResults - allCandidates.length;
|
|
37408
|
+
const result = await runPhase2(this.octokit, this.vetter, scopes, labels, config2.labels, baseQualifiers, remaining, minStars, phase0RepoSet, starredRepoSet, allCandidates, filterIssues);
|
|
37409
|
+
allCandidates.push(...result.candidates);
|
|
37410
|
+
phaseErrors["2"] = result.error;
|
|
37411
|
+
if (result.rateLimitHit)
|
|
37412
|
+
rateLimitHitDuringSearch = true;
|
|
37413
|
+
}
|
|
37298
37414
|
strategiesUsed.push("broad");
|
|
37299
37415
|
}
|
|
37300
37416
|
if (allCandidates.length < maxResults && searchBudget >= LOW_BUDGET_THRESHOLD && enabledStrategies.has("maintained")) {
|
|
37301
|
-
|
|
37417
|
+
if (interPhaseDelay > 0) {
|
|
37418
|
+
info(MODULE9, `Waiting ${(interPhaseDelay / 1e3).toFixed(0)}s between phases for rate limit management...`);
|
|
37419
|
+
await sleep(interPhaseDelay);
|
|
37420
|
+
}
|
|
37302
37421
|
const remaining = maxResults - allCandidates.length;
|
|
37303
|
-
const result = await runPhase3(this.octokit, this.vetter, langQuery, minStars, config2.projectCategories ?? [], remaining, phase0RepoSet, starredRepoSet, allCandidates, filterIssues);
|
|
37422
|
+
const result = await runPhase3(this.octokit, this.vetter, langQuery, minStars, config2.projectCategories ?? [], remaining, phase0RepoSet, starredRepoSet, starredRepos, allCandidates, filterIssues);
|
|
37304
37423
|
allCandidates.push(...result.candidates);
|
|
37305
37424
|
phaseErrors["3"] = result.error;
|
|
37306
37425
|
if (result.rateLimitHit)
|
|
@@ -38313,7 +38432,10 @@ function registerTools(server, scout) {
|
|
|
38313
38432
|
const NUMBER_KEYS = /* @__PURE__ */ new Set([
|
|
38314
38433
|
"minStars",
|
|
38315
38434
|
"maxIssueAgeDays",
|
|
38316
|
-
"minRepoScoreThreshold"
|
|
38435
|
+
"minRepoScoreThreshold",
|
|
38436
|
+
"interPhaseDelayMs",
|
|
38437
|
+
"broadPhaseDelayMs",
|
|
38438
|
+
"skipBroadWhenSufficientResults"
|
|
38317
38439
|
]);
|
|
38318
38440
|
const BOOLEAN_KEYS = /* @__PURE__ */ new Set(["includeDocIssues"]);
|
|
38319
38441
|
server.tool(
|
package/dist/tools.js
CHANGED
|
@@ -181,6 +181,9 @@ export function registerTools(server, scout) {
|
|
|
181
181
|
"minStars",
|
|
182
182
|
"maxIssueAgeDays",
|
|
183
183
|
"minRepoScoreThreshold",
|
|
184
|
+
"interPhaseDelayMs",
|
|
185
|
+
"broadPhaseDelayMs",
|
|
186
|
+
"skipBroadWhenSufficientResults",
|
|
184
187
|
]);
|
|
185
188
|
const BOOLEAN_KEYS = new Set(["includeDocIssues"]);
|
|
186
189
|
server.tool("config-set", "Update an oss-scout configuration preference", {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oss-scout/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "MCP server for oss-scout — exposes issue discovery and vetting as MCP tools",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
20
20
|
"zod": "^4.3.6",
|
|
21
|
-
"@oss-scout/core": "^0.
|
|
21
|
+
"@oss-scout/core": "^0.4.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/node": "^25.5.0",
|