@oss-scout/mcp 0.10.1 → 0.11.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/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mcp-server.bundle.cjs +125 -17
- package/dist/tools.js +6 -3
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export { registerResources } from "./resources.js";
|
|
|
12
12
|
/** Server identity, kept in one place for the bin and any embedding host. */
|
|
13
13
|
export declare const SERVER_INFO: {
|
|
14
14
|
readonly name: "oss-scout-mcp";
|
|
15
|
-
readonly version: "0.
|
|
15
|
+
readonly version: "0.11.0";
|
|
16
16
|
};
|
|
17
17
|
/**
|
|
18
18
|
* Build a fully-wired MCP server (tools + resources) around a scout.
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ export { registerResources } from "./resources.js";
|
|
|
15
15
|
/** Server identity, kept in one place for the bin and any embedding host. */
|
|
16
16
|
export const SERVER_INFO = {
|
|
17
17
|
name: "oss-scout-mcp",
|
|
18
|
-
version: "0.
|
|
18
|
+
version: "0.11.0", // x-release-please-version
|
|
19
19
|
};
|
|
20
20
|
/**
|
|
21
21
|
* Build a fully-wired MCP server (tools + resources) around a scout.
|
|
@@ -36075,6 +36075,7 @@ var IssueScopeSchema = external_exports3.enum([
|
|
|
36075
36075
|
]);
|
|
36076
36076
|
var SearchStrategySchema = external_exports3.enum([
|
|
36077
36077
|
"merged",
|
|
36078
|
+
"orgs",
|
|
36078
36079
|
"starred",
|
|
36079
36080
|
"broad",
|
|
36080
36081
|
"maintained",
|
|
@@ -36082,6 +36083,7 @@ var SearchStrategySchema = external_exports3.enum([
|
|
|
36082
36083
|
]);
|
|
36083
36084
|
var CONCRETE_STRATEGIES = [
|
|
36084
36085
|
"merged",
|
|
36086
|
+
"orgs",
|
|
36085
36087
|
"starred",
|
|
36086
36088
|
"broad",
|
|
36087
36089
|
"maintained"
|
|
@@ -36215,6 +36217,9 @@ var ScoutPreferencesSchema = external_exports3.looseObject({
|
|
|
36215
36217
|
scope: external_exports3.array(IssueScopeSchema).optional(),
|
|
36216
36218
|
excludeRepos: external_exports3.array(external_exports3.string()).default([]),
|
|
36217
36219
|
excludeOrgs: external_exports3.array(external_exports3.string()).default([]),
|
|
36220
|
+
// Orgs whose repos the "orgs" search phase targets with org: qualifiers.
|
|
36221
|
+
// Empty (the default) disables the phase even when the strategy is enabled.
|
|
36222
|
+
preferredOrgs: external_exports3.array(external_exports3.string()).default([]),
|
|
36218
36223
|
aiPolicyBlocklist: external_exports3.array(external_exports3.string()).default(["matplotlib/matplotlib"]),
|
|
36219
36224
|
projectCategories: external_exports3.array(ProjectCategorySchema).default([]),
|
|
36220
36225
|
minStars: external_exports3.number().default(50),
|
|
@@ -37684,7 +37689,11 @@ async function triageWithSLM(input, options) {
|
|
|
37684
37689
|
format: SLM_TRIAGE_SCHEMA,
|
|
37685
37690
|
options: { temperature: 0.1 }
|
|
37686
37691
|
}),
|
|
37687
|
-
signal: AbortSignal.timeout(timeoutMs)
|
|
37692
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
37693
|
+
// No redirect following: an allowed private host could otherwise
|
|
37694
|
+
// 307/308-redirect the issue-text POST body to a public origin (#310).
|
|
37695
|
+
// Ollama never redirects, so this costs nothing.
|
|
37696
|
+
redirect: "error"
|
|
37688
37697
|
});
|
|
37689
37698
|
} catch {
|
|
37690
37699
|
return null;
|
|
@@ -38079,6 +38088,9 @@ var IssueVetter = class {
|
|
|
38079
38088
|
let rateLimitFailures = 0;
|
|
38080
38089
|
let attemptedCount = 0;
|
|
38081
38090
|
let firstAuthError = null;
|
|
38091
|
+
let firstForbiddenError = null;
|
|
38092
|
+
let forbiddenCount = 0;
|
|
38093
|
+
const forbiddenRepos = /* @__PURE__ */ new Set();
|
|
38082
38094
|
const uniqueUrls = [...new Set(urls)];
|
|
38083
38095
|
const issueRefs = uniqueUrls.flatMap((url2) => {
|
|
38084
38096
|
const p = parseGitHubUrl(url2);
|
|
@@ -38097,6 +38109,12 @@ var IssueVetter = class {
|
|
|
38097
38109
|
break;
|
|
38098
38110
|
if (firstAuthError)
|
|
38099
38111
|
break;
|
|
38112
|
+
const parsedUrl = parseGitHubUrl(url2);
|
|
38113
|
+
const repoKey = parsedUrl ? `${parsedUrl.owner}/${parsedUrl.repo}` : null;
|
|
38114
|
+
if (repoKey && forbiddenRepos.has(repoKey)) {
|
|
38115
|
+
debug(MODULE10, `Skipping ${url2}: repo already returned 403`);
|
|
38116
|
+
continue;
|
|
38117
|
+
}
|
|
38100
38118
|
attemptedCount++;
|
|
38101
38119
|
const core = prefetchFor(url2);
|
|
38102
38120
|
const task = this.vetIssue(url2, core ? { prefetched: core } : void 0).then((candidate) => {
|
|
@@ -38107,10 +38125,19 @@ var IssueVetter = class {
|
|
|
38107
38125
|
candidates.push(candidate);
|
|
38108
38126
|
}
|
|
38109
38127
|
}).catch((error48) => {
|
|
38110
|
-
if (
|
|
38128
|
+
if (getHttpStatusCode(error48) === 401) {
|
|
38111
38129
|
firstAuthError ??= error48;
|
|
38112
38130
|
return;
|
|
38113
38131
|
}
|
|
38132
|
+
if (isAuthError(error48)) {
|
|
38133
|
+
firstForbiddenError ??= error48;
|
|
38134
|
+
forbiddenCount++;
|
|
38135
|
+
failedVettingCount++;
|
|
38136
|
+
if (repoKey)
|
|
38137
|
+
forbiddenRepos.add(repoKey);
|
|
38138
|
+
warn(MODULE10, `Access forbidden for ${url2} (SAML/token scope?) \u2014 skipping this repo:`, errorMessage(error48));
|
|
38139
|
+
return;
|
|
38140
|
+
}
|
|
38114
38141
|
failedVettingCount++;
|
|
38115
38142
|
if (isRateLimitError(error48)) {
|
|
38116
38143
|
rateLimitFailures++;
|
|
@@ -38129,6 +38156,9 @@ var IssueVetter = class {
|
|
|
38129
38156
|
}
|
|
38130
38157
|
throw firstAuthError;
|
|
38131
38158
|
}
|
|
38159
|
+
if (firstForbiddenError && attemptedCount > 0 && forbiddenCount === attemptedCount) {
|
|
38160
|
+
throw firstForbiddenError;
|
|
38161
|
+
}
|
|
38132
38162
|
const allFailed = failedVettingCount === attemptedCount && attemptedCount > 0;
|
|
38133
38163
|
if (allFailed) {
|
|
38134
38164
|
warn(MODULE10, `All ${attemptedCount} issue(s) failed vetting. This may indicate a systemic issue (rate limit, auth, network).`);
|
|
@@ -38395,7 +38425,7 @@ function buildLanguageVariants(languages, isAnyLanguage, hasLabels) {
|
|
|
38395
38425
|
return [languages.map((l) => `language:${l}`).join(" ")];
|
|
38396
38426
|
return languages.map((l) => `language:${l}`);
|
|
38397
38427
|
}
|
|
38398
|
-
async function searchAcrossLanguagesAndLabels(octokit, languages, isAnyLanguage, labels, buildBaseQuery, perPage, tracker = getSearchBudgetTracker(), startOffset = 0) {
|
|
38428
|
+
async function searchAcrossLanguagesAndLabels(octokit, languages, isAnyLanguage, labels, buildBaseQuery, perPage, tracker = getSearchBudgetTracker(), startOffset = 0, reservedOps = 0) {
|
|
38399
38429
|
const variants = buildLanguageVariants(languages, isAnyLanguage, labels.length > 0);
|
|
38400
38430
|
const k = variants.length > 0 ? startOffset % variants.length : 0;
|
|
38401
38431
|
const langVariants = [...variants.slice(k), ...variants.slice(0, k)];
|
|
@@ -38404,7 +38434,7 @@ async function searchAcrossLanguagesAndLabels(octokit, languages, isAnyLanguage,
|
|
|
38404
38434
|
for (let i = 0; i < langVariants.length; i++) {
|
|
38405
38435
|
if (i > 0)
|
|
38406
38436
|
await sleep(GRAPHQL_INTER_QUERY_DELAY_MS);
|
|
38407
|
-
const items = await searchWithChunkedLabels(octokit, labels,
|
|
38437
|
+
const items = await searchWithChunkedLabels(octokit, labels, reservedOps, (labelQ) => `${buildBaseQuery(langVariants[i])} ${labelQ}`.replace(/ +/g, " ").trim(), perPage, tracker);
|
|
38408
38438
|
for (const item of items) {
|
|
38409
38439
|
if (!seenUrls.has(item.html_url)) {
|
|
38410
38440
|
seenUrls.add(item.html_url);
|
|
@@ -38582,6 +38612,35 @@ async function runPhase0(octokit, vetter, repos, maxResults, filterIssues) {
|
|
|
38582
38612
|
rateLimitHit
|
|
38583
38613
|
};
|
|
38584
38614
|
}
|
|
38615
|
+
var MAX_QUERY_ORGS = 8;
|
|
38616
|
+
async function runPhaseOrgs(octokit, vetter, orgs, languages, isAnyLanguage, labels, maxResults, minStars, phase0RepoSet, starredRepoSet, filterIssues, tracker) {
|
|
38617
|
+
info(MODULE12, `Orgs phase: searching ${orgs.length} preferred org(s)...`);
|
|
38618
|
+
const queryOrgs = orgs.slice(0, MAX_QUERY_ORGS);
|
|
38619
|
+
if (orgs.length > MAX_QUERY_ORGS) {
|
|
38620
|
+
warn(MODULE12, `Orgs phase: capping query at ${MAX_QUERY_ORGS} org(s); dropping ${orgs.slice(MAX_QUERY_ORGS).join(", ")}`);
|
|
38621
|
+
}
|
|
38622
|
+
const orgQuery = queryOrgs.map((o) => `org:${o}`).join(" ");
|
|
38623
|
+
try {
|
|
38624
|
+
const allItems = await searchAcrossLanguagesAndLabels(octokit, languages, isAnyLanguage, labels, (langQ) => `is:issue is:open ${langQ} no:assignee ${orgQuery}`.replace(/ +/g, " ").trim(), maxResults * 3, tracker, 0, queryOrgs.length);
|
|
38625
|
+
const { candidates, allVetFailed, rateLimitHit } = await filterVetAndScore(vetter, allItems, filterIssues, [phase0RepoSet, starredRepoSet], maxResults, minStars, "Orgs phase");
|
|
38626
|
+
info(MODULE12, `Found ${candidates.length} candidates from preferred orgs`);
|
|
38627
|
+
return {
|
|
38628
|
+
candidates,
|
|
38629
|
+
error: allVetFailed ? "all vetting failed" : null,
|
|
38630
|
+
rateLimitHit
|
|
38631
|
+
};
|
|
38632
|
+
} catch (error48) {
|
|
38633
|
+
if (getHttpStatusCode(error48) === 401)
|
|
38634
|
+
throw error48;
|
|
38635
|
+
const errMsg = errorMessage(error48);
|
|
38636
|
+
warn(MODULE12, `Error in preferred-orgs search: ${errMsg}`);
|
|
38637
|
+
return {
|
|
38638
|
+
candidates: [],
|
|
38639
|
+
error: errMsg,
|
|
38640
|
+
rateLimitHit: isRateLimitError(error48)
|
|
38641
|
+
};
|
|
38642
|
+
}
|
|
38643
|
+
}
|
|
38585
38644
|
async function runPhase1(octokit, vetter, repos, labels, maxResults, filterIssues) {
|
|
38586
38645
|
info(MODULE12, `Phase 1: Searching issues in ${repos.length} starred repos...`);
|
|
38587
38646
|
const phase1Labels = labels.slice(0, 3);
|
|
@@ -38806,6 +38865,7 @@ var IssueDiscovery = class {
|
|
|
38806
38865
|
resetGraphQLSearchQueryCount();
|
|
38807
38866
|
const tracker = this.budgetTracker;
|
|
38808
38867
|
let searchBudget = CRITICAL_BUDGET_THRESHOLD - 1;
|
|
38868
|
+
let preflightFailed = false;
|
|
38809
38869
|
try {
|
|
38810
38870
|
const rateLimit = await checkRateLimit(this.githubToken);
|
|
38811
38871
|
searchBudget = rateLimit.remaining;
|
|
@@ -38823,6 +38883,7 @@ var IssueDiscovery = class {
|
|
|
38823
38883
|
} catch (error48) {
|
|
38824
38884
|
if (getHttpStatusCode(error48) === 401)
|
|
38825
38885
|
throw error48;
|
|
38886
|
+
preflightFailed = true;
|
|
38826
38887
|
tracker.init(CRITICAL_BUDGET_THRESHOLD, new Date(Date.now() + 6e4).toISOString());
|
|
38827
38888
|
warn(MODULE12, "Could not check rate limit \u2014 using conservative budget, skipping the starred phase:", errorMessage(error48));
|
|
38828
38889
|
}
|
|
@@ -38869,7 +38930,9 @@ var IssueDiscovery = class {
|
|
|
38869
38930
|
break;
|
|
38870
38931
|
}
|
|
38871
38932
|
const phase0RepoSet = new Set(phase0Repos);
|
|
38872
|
-
const
|
|
38933
|
+
const preferredOrgs = config2.preferredOrgs ?? [];
|
|
38934
|
+
const orgsPhaseEnabled = preferredOrgs.length > 0 && enabledStrategies.has("orgs");
|
|
38935
|
+
const otherStrategiesCanRun = orgsPhaseEnabled || starredRepos.length > 0 && enabledStrategies.has("starred") || enabledStrategies.has("broad") || enabledStrategies.has("maintained");
|
|
38873
38936
|
if (phase0Repos.length > 0 && enabledStrategies.has("merged")) {
|
|
38874
38937
|
const phase0Cap = otherStrategiesCanRun ? Math.max(1, Math.ceil(maxResults * PHASE0_MAX_SHARE)) : maxResults;
|
|
38875
38938
|
const remaining = Math.min(maxResults - allCandidates.length, phase0Cap);
|
|
@@ -38879,6 +38942,13 @@ var IssueDiscovery = class {
|
|
|
38879
38942
|
}
|
|
38880
38943
|
strategiesUsed.push("merged");
|
|
38881
38944
|
}
|
|
38945
|
+
if (allCandidates.length < maxResults && orgsPhaseEnabled) {
|
|
38946
|
+
await applyInterPhaseDelay();
|
|
38947
|
+
const remaining = maxResults - allCandidates.length;
|
|
38948
|
+
const result = await runPhaseOrgs(this.octokit, this.vetter, preferredOrgs, languages, isAnyLanguage, labels, remaining, minStars, phase0RepoSet, starredRepoSet, filterIssues, tracker);
|
|
38949
|
+
recordPhaseResult("orgs", result);
|
|
38950
|
+
strategiesUsed.push("orgs");
|
|
38951
|
+
}
|
|
38882
38952
|
if (allCandidates.length < maxResults && starredRepos.length > 0 && searchBudget >= CRITICAL_BUDGET_THRESHOLD && enabledStrategies.has("starred")) {
|
|
38883
38953
|
await applyInterPhaseDelay();
|
|
38884
38954
|
const reposToSearch = starredRepos.filter((r) => !phase0RepoSet.has(r));
|
|
@@ -38898,7 +38968,8 @@ var IssueDiscovery = class {
|
|
|
38898
38968
|
if (viableBeforeBroad >= maxResults && enabledStrategies.has("broad")) {
|
|
38899
38969
|
info(MODULE12, `Skipping broad search: ${viableBeforeBroad} viable candidate(s) already meet maxResults (${maxResults})`);
|
|
38900
38970
|
} else if (enabledStrategies.has("broad")) {
|
|
38901
|
-
const
|
|
38971
|
+
const preferredOrgSet = new Set(preferredOrgs.map((o) => o.toLowerCase()));
|
|
38972
|
+
const newRepoCandidateCount = allCandidates.filter((c) => c.recommendation !== "skip" && !phase0RepoSet.has(c.issue.repo) && !starredRepoSet.has(c.issue.repo) && !preferredOrgSet.has(c.issue.repo.split("/")[0]?.toLowerCase() ?? "")).length;
|
|
38902
38973
|
if (skipThreshold > 0 && newRepoCandidateCount >= skipThreshold) {
|
|
38903
38974
|
info(MODULE12, `Skipping broad search: already found ${newRepoCandidateCount} viable candidate(s) from new repos (threshold: ${skipThreshold})`);
|
|
38904
38975
|
} else {
|
|
@@ -38926,10 +38997,11 @@ var IssueDiscovery = class {
|
|
|
38926
38997
|
strategiesUsed.push("maintained");
|
|
38927
38998
|
}
|
|
38928
38999
|
const phasesSkippedForBudget = searchBudget < CRITICAL_BUDGET_THRESHOLD;
|
|
38929
|
-
const budgetNote = phasesSkippedForBudget ? ` The starred-repo phase was skipped due to critically low API quota (${searchBudget} remaining); broad and maintained phases still ran on GraphQL.` : "";
|
|
39000
|
+
const budgetNote = phasesSkippedForBudget ? preflightFailed ? " The starred-repo phase was skipped as a precaution because the rate-limit check failed (quota unknown); broad and maintained phases still ran on GraphQL." : ` The starred-repo phase was skipped due to critically low API quota (${searchBudget} remaining); broad and maintained phases still ran on GraphQL.` : "";
|
|
38930
39001
|
if (allCandidates.length === 0) {
|
|
38931
39002
|
const errorDetails = [
|
|
38932
39003
|
phaseErrors["0"] ? `Phase 0 (merged-PR repos): ${phaseErrors["0"]}` : null,
|
|
39004
|
+
phaseErrors["orgs"] ? `Orgs phase (preferred orgs): ${phaseErrors["orgs"]}` : null,
|
|
38933
39005
|
phaseErrors["1"] ? `Phase 1 (starred repos): ${phaseErrors["1"]}` : null,
|
|
38934
39006
|
phaseErrors["2"] ? `Phase 2 (general): ${phaseErrors["2"]}` : null,
|
|
38935
39007
|
phaseErrors["3"] ? `Phase 3 (maintained repos): ${phaseErrors["3"]}` : null
|
|
@@ -39034,10 +39106,17 @@ function parseRoadmapIssueRefs(content, owner, repo) {
|
|
|
39034
39106
|
refs.add(n);
|
|
39035
39107
|
}
|
|
39036
39108
|
const fullRefStripPattern = /\b[\w.-]+\/[\w.-]+#\d+\b/gi;
|
|
39109
|
+
let inFence = false;
|
|
39037
39110
|
for (const line of content.split("\n")) {
|
|
39111
|
+
if (/^\s*(```|~~~)/.test(line)) {
|
|
39112
|
+
inFence = !inFence;
|
|
39113
|
+
continue;
|
|
39114
|
+
}
|
|
39115
|
+
if (inFence)
|
|
39116
|
+
continue;
|
|
39038
39117
|
if (/^\s*#+\s/.test(line))
|
|
39039
39118
|
continue;
|
|
39040
|
-
const stripped = line.replace(fullRefStripPattern, "");
|
|
39119
|
+
const stripped = line.replace(fullRefStripPattern, "").replace(/`[^`]*`/g, "");
|
|
39041
39120
|
for (const m of stripped.matchAll(/(?:^|[^&\w])#(\d+)\b/g)) {
|
|
39042
39121
|
const n = Number.parseInt(m[1], 10);
|
|
39043
39122
|
if (n > 0)
|
|
@@ -39114,7 +39193,8 @@ function classifyHorizon(input) {
|
|
|
39114
39193
|
function splitByHorizon(candidates, count, ratio = DEFAULT_SPLIT_RATIO) {
|
|
39115
39194
|
const allQuick = candidates.filter((c) => c.horizon === "quick-win").sort((a, b) => b.viabilityScore - a.viabilityScore);
|
|
39116
39195
|
const allBigger = candidates.filter((c) => c.horizon === "bigger-bet").sort((a, b) => b.viabilityScore - a.viabilityScore);
|
|
39117
|
-
const
|
|
39196
|
+
const clampedRatio = Math.max(0, Math.min(1, ratio));
|
|
39197
|
+
const targetQuick = Math.round(count * clampedRatio);
|
|
39118
39198
|
const targetBigger = count - targetQuick;
|
|
39119
39199
|
const quickTaken = Math.min(allQuick.length, targetQuick);
|
|
39120
39200
|
const biggerTaken = Math.min(allBigger.length, targetBigger);
|
|
@@ -39168,6 +39248,17 @@ function detectWontfixNoContributor(input) {
|
|
|
39168
39248
|
}
|
|
39169
39249
|
var NO_ANCHORS_MESSAGE = "No anchor repos yet (need 3+ merged PRs in a repo). Try `scout search` to build relationships first.";
|
|
39170
39250
|
var NO_RESULTS_MESSAGE = "No open feature opportunities in your anchor repos right now. Check back next week, or try `scout search` for fix-mode work.";
|
|
39251
|
+
function buildRepoExclusionFilter(opts) {
|
|
39252
|
+
const excluded = new Set([...opts.excludeRepos ?? [], ...opts.aiPolicyBlocklist ?? []].map((r) => r.toLowerCase()));
|
|
39253
|
+
const excludedOrgs = new Set((opts.excludeOrgs ?? []).map((o) => o.toLowerCase()));
|
|
39254
|
+
return (repoFullName) => {
|
|
39255
|
+
const lower = repoFullName.toLowerCase();
|
|
39256
|
+
if (excluded.has(lower))
|
|
39257
|
+
return false;
|
|
39258
|
+
const org = lower.split("/")[0];
|
|
39259
|
+
return !(org && excludedOrgs.has(org));
|
|
39260
|
+
};
|
|
39261
|
+
}
|
|
39171
39262
|
function extractLabels(item) {
|
|
39172
39263
|
if (!Array.isArray(item.labels))
|
|
39173
39264
|
return [];
|
|
@@ -39213,7 +39304,8 @@ async function vetAndClassify(item, vetter, roadmapRefs) {
|
|
|
39213
39304
|
return { ...candidate, horizon };
|
|
39214
39305
|
}
|
|
39215
39306
|
async function discoverFeatures(opts) {
|
|
39216
|
-
const
|
|
39307
|
+
const includeRepo = buildRepoExclusionFilter(opts);
|
|
39308
|
+
const anchorRepos = resolveAnchorRepos(opts.repoScores, opts.anchorThreshold).filter(includeRepo);
|
|
39217
39309
|
if (anchorRepos.length === 0) {
|
|
39218
39310
|
return {
|
|
39219
39311
|
quickWins: [],
|
|
@@ -39320,7 +39412,11 @@ async function discoverFeaturesBroad(opts) {
|
|
|
39320
39412
|
merged.push(raw);
|
|
39321
39413
|
}
|
|
39322
39414
|
}
|
|
39323
|
-
|
|
39415
|
+
const includeRepo = buildRepoExclusionFilter(opts);
|
|
39416
|
+
items = merged.filter((it) => !it.pull_request && !it.assignee && isFeatureIssue(it)).filter((it) => {
|
|
39417
|
+
const repo = extractRepoFromUrl(it.html_url);
|
|
39418
|
+
return repo === null || includeRepo(repo);
|
|
39419
|
+
}).slice(0, maxToVet);
|
|
39324
39420
|
} catch (err) {
|
|
39325
39421
|
rethrowIfFatal(err);
|
|
39326
39422
|
warn(MODULE13, `broad feature search failed: ${errorMessage(err)}`);
|
|
@@ -39654,6 +39750,13 @@ function mergeStates(local, remote) {
|
|
|
39654
39750
|
const localPrefsWin = localPrefsTs !== void 0 && (remotePrefsTs === void 0 || localPrefsTs > remotePrefsTs);
|
|
39655
39751
|
const preferences = localPrefsWin ? local.preferences : remote.preferences;
|
|
39656
39752
|
const preferencesUpdatedAt = pickFresherTimestamp(localPrefsTs, remotePrefsTs);
|
|
39753
|
+
const mergedPRsFinal = unionByUrl(local.mergedPRs, remote.mergedPRs);
|
|
39754
|
+
const closedPRsFinal = unionByUrl(local.closedPRs, remote.closedPRs);
|
|
39755
|
+
const resolvedUrls = /* @__PURE__ */ new Set([
|
|
39756
|
+
...mergedPRsFinal.map((p) => p.url),
|
|
39757
|
+
...closedPRsFinal.map((p) => p.url)
|
|
39758
|
+
]);
|
|
39759
|
+
const openPRsFinal = unionByUrl(local.openPRs ?? [], remote.openPRs ?? []).filter((p) => !resolvedUrls.has(p.url));
|
|
39657
39760
|
return {
|
|
39658
39761
|
...local,
|
|
39659
39762
|
...remote,
|
|
@@ -39664,9 +39767,9 @@ function mergeStates(local, remote) {
|
|
|
39664
39767
|
repoScores: mergeRepoScores(local.repoScores, remote.repoScores),
|
|
39665
39768
|
starredRepos: mergeStarredRepos(local, remote),
|
|
39666
39769
|
starredReposLastFetched: pickFresherTimestamp(local.starredReposLastFetched, remote.starredReposLastFetched),
|
|
39667
|
-
mergedPRs:
|
|
39668
|
-
closedPRs:
|
|
39669
|
-
openPRs:
|
|
39770
|
+
mergedPRs: mergedPRsFinal,
|
|
39771
|
+
closedPRs: closedPRsFinal,
|
|
39772
|
+
openPRs: openPRsFinal,
|
|
39670
39773
|
savedResults: savedResultsFinal,
|
|
39671
39774
|
skippedIssues,
|
|
39672
39775
|
lastSearchAt: pickFresherTimestamp(local.lastSearchAt, remote.lastSearchAt),
|
|
@@ -40038,12 +40141,16 @@ var OssScout = class {
|
|
|
40038
40141
|
languages: this.state.preferences.languages,
|
|
40039
40142
|
excludeRepos: this.state.preferences.excludeRepos,
|
|
40040
40143
|
excludeOrgs: this.state.preferences.excludeOrgs,
|
|
40144
|
+
aiPolicyBlocklist: this.state.preferences.aiPolicyBlocklist,
|
|
40041
40145
|
splitRatio: options?.splitRatio ?? this.state.preferences.featuresSplitRatio
|
|
40042
40146
|
}) : await discoverFeatures({
|
|
40043
40147
|
octokit,
|
|
40044
40148
|
vetter,
|
|
40045
40149
|
repoScores: this.state.repoScores ?? {},
|
|
40046
40150
|
count,
|
|
40151
|
+
excludeRepos: this.state.preferences.excludeRepos,
|
|
40152
|
+
excludeOrgs: this.state.preferences.excludeOrgs,
|
|
40153
|
+
aiPolicyBlocklist: this.state.preferences.aiPolicyBlocklist,
|
|
40047
40154
|
anchorThreshold: options?.anchorThreshold ?? this.state.preferences.featuresAnchorThreshold,
|
|
40048
40155
|
splitRatio: options?.splitRatio ?? this.state.preferences.featuresSplitRatio
|
|
40049
40156
|
});
|
|
@@ -40585,6 +40692,7 @@ var FIELD_CONFIGS = {
|
|
|
40585
40692
|
scope: { type: "enum-array", validValues: IssueScopeSchema.options },
|
|
40586
40693
|
excludeRepos: { type: "array" },
|
|
40587
40694
|
excludeOrgs: { type: "array" },
|
|
40695
|
+
preferredOrgs: { type: "array" },
|
|
40588
40696
|
aiPolicyBlocklist: { type: "array" },
|
|
40589
40697
|
projectCategories: {
|
|
40590
40698
|
type: "enum-array",
|
|
@@ -40746,9 +40854,9 @@ function registerTools(server, scout) {
|
|
|
40746
40854
|
"search",
|
|
40747
40855
|
"Search for open source issues matching your preferences",
|
|
40748
40856
|
{
|
|
40749
|
-
maxResults: external_exports3.number().int().min(1).max(
|
|
40857
|
+
maxResults: external_exports3.number().int().min(1).max(100).optional().describe("Maximum number of results to return (1-100, default 10)"),
|
|
40750
40858
|
strategies: external_exports3.string().optional().describe(
|
|
40751
|
-
"Comma-separated search strategies: merged, starred, broad, maintained, all"
|
|
40859
|
+
"Comma-separated search strategies: merged, orgs, starred, broad, maintained, all"
|
|
40752
40860
|
),
|
|
40753
40861
|
preferLanguages: external_exports3.array(external_exports3.string()).optional().describe(
|
|
40754
40862
|
"Soft-boost ranking for candidates whose repo language matches one of these (case-insensitive). Personalization tier between recommendation and viabilityScore (#1244). Not a filter; only reorders."
|
|
@@ -41095,7 +41203,7 @@ function registerResources(server, scout) {
|
|
|
41095
41203
|
// src/index.ts
|
|
41096
41204
|
var SERVER_INFO = {
|
|
41097
41205
|
name: "oss-scout-mcp",
|
|
41098
|
-
version: "0.
|
|
41206
|
+
version: "0.11.0"
|
|
41099
41207
|
// x-release-please-version
|
|
41100
41208
|
};
|
|
41101
41209
|
function createServer(scout) {
|
package/dist/tools.js
CHANGED
|
@@ -35,13 +35,16 @@ export function registerTools(server, scout) {
|
|
|
35
35
|
.number()
|
|
36
36
|
.int()
|
|
37
37
|
.min(1)
|
|
38
|
-
|
|
38
|
+
// 100, not 50: the CLI's search count is unbounded, so a tight MCP
|
|
39
|
+
// cap made the two surfaces disagree for 1.5.0 clients while still
|
|
40
|
+
// needing SOME ceiling against absurd values (#317).
|
|
41
|
+
.max(100)
|
|
39
42
|
.optional()
|
|
40
|
-
.describe("Maximum number of results to return (1-
|
|
43
|
+
.describe("Maximum number of results to return (1-100, default 10)"),
|
|
41
44
|
strategies: z
|
|
42
45
|
.string()
|
|
43
46
|
.optional()
|
|
44
|
-
.describe("Comma-separated search strategies: merged, starred, broad, maintained, all"),
|
|
47
|
+
.describe("Comma-separated search strategies: merged, orgs, starred, broad, maintained, all"),
|
|
45
48
|
preferLanguages: z
|
|
46
49
|
.array(z.string())
|
|
47
50
|
.optional()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oss-scout/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.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": "^1.
|
|
21
|
+
"@oss-scout/core": "^1.6.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/node": "^25.5.0",
|