New:Socket for Asana Is Now Available.Learn more
Sign In

@oss-scout/core

Package Overview
Dependencies
Maintainers
1
Versions
29
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@oss-scout/core - npm Package Compare versions

Comparing version
1.5.2
to
1.6.0
+1
-0
dist/commands/config.js

@@ -29,2 +29,3 @@ /**

console.log(` excludeOrgs: ${formatArray(prefs.excludeOrgs)}`);
console.log(` preferredOrgs: ${formatArray(prefs.preferredOrgs)}`);
console.log(` aiPolicyBlocklist: ${formatArray(prefs.aiPolicyBlocklist)}`);

@@ -31,0 +32,0 @@ console.log(` defaultStrategy: ${prefs.defaultStrategy ? formatArray(prefs.defaultStrategy) : "(all)"}`);

@@ -104,2 +104,44 @@ /**

}
/**
* Cap on org: qualifiers in the orgs-phase query. Each qualifier consumes one
* of GitHub Search's boolean operators, so an unbounded preferredOrgs list
* would starve (or disable) label filtering. Orgs beyond the cap are dropped
* with a warning.
*/
const MAX_QUERY_ORGS = 8;
/** Orgs phase: broad-style search scoped to the user's preferred orgs. */
async function runPhaseOrgs(octokit, vetter, orgs, languages, isAnyLanguage, labels, maxResults, minStars, phase0RepoSet, starredRepoSet, filterIssues, tracker) {
info(MODULE, `Orgs phase: searching ${orgs.length} preferred org(s)...`);
const queryOrgs = orgs.slice(0, MAX_QUERY_ORGS);
if (orgs.length > MAX_QUERY_ORGS) {
warn(MODULE, `Orgs phase: capping query at ${MAX_QUERY_ORGS} org(s); dropping ${orgs
.slice(MAX_QUERY_ORGS)
.join(", ")}`);
}
// Multiple org: qualifiers OR together in the GitHub search syntax.
const orgQuery = queryOrgs.map((o) => `org:${o}`).join(" ");
try {
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);
const { candidates, allVetFailed, rateLimitHit } = await filterVetAndScore(vetter, allItems, filterIssues, [phase0RepoSet, starredRepoSet], maxResults, minStars, "Orgs phase");
info(MODULE, `Found ${candidates.length} candidates from preferred orgs`);
return {
candidates,
error: allVetFailed ? "all vetting failed" : null,
rateLimitHit,
};
}
catch (error) {
if (getHttpStatusCode(error) === 401)
throw error;
const errMsg = errorMessage(error);
warn(MODULE, `Error in preferred-orgs search: ${errMsg}`);
return {
candidates: [],
error: errMsg,
rateLimitHit: isRateLimitError(error),
};
}
}
/** Phase 1: Search starred repos. */

@@ -464,3 +506,6 @@ async function runPhase1(octokit, vetter, repos, labels, maxResults, filterIssues) {

// reservation would just shrink the result set with nothing to fill it.
const otherStrategiesCanRun = (starredRepos.length > 0 && enabledStrategies.has("starred")) ||
const preferredOrgs = config.preferredOrgs ?? [];
const orgsPhaseEnabled = preferredOrgs.length > 0 && enabledStrategies.has("orgs");
const otherStrategiesCanRun = orgsPhaseEnabled ||
(starredRepos.length > 0 && enabledStrategies.has("starred")) ||
enabledStrategies.has("broad") ||

@@ -481,2 +526,12 @@ enabledStrategies.has("maintained");

}
// Orgs phase: preferred organizations (broad-style search with org:
// qualifiers). Runs before starred so org results get first claim on the
// remaining budget — the user asked for these orgs explicitly.
if (allCandidates.length < maxResults && orgsPhaseEnabled) {
await applyInterPhaseDelay();
const remaining = maxResults - allCandidates.length;
const result = await runPhaseOrgs(this.octokit, this.vetter, preferredOrgs, languages, isAnyLanguage, labels, remaining, minStars, phase0RepoSet, starredRepoSet, filterIssues, tracker);
recordPhaseResult("orgs", result);
strategiesUsed.push("orgs");
}
// Phase 1: Starred repos

@@ -522,6 +577,10 @@ if (allCandidates.length < maxResults &&

// "enough NEW work" — not "we re-found issues in the same repos" and not
// "we found issues the vetter already ruled out" (#265).
// "we found issues the vetter already ruled out" (#265). Preferred-org
// candidates are affinity results too — the user named those orgs — so
// they must not gate off the broad phase either.
const preferredOrgSet = new Set(preferredOrgs.map((o) => o.toLowerCase()));
const newRepoCandidateCount = allCandidates.filter((c) => c.recommendation !== "skip" &&
!phase0RepoSet.has(c.issue.repo) &&
!starredRepoSet.has(c.issue.repo)).length;
!starredRepoSet.has(c.issue.repo) &&
!preferredOrgSet.has(c.issue.repo.split("/")[0]?.toLowerCase() ?? "")).length;
if (skipThreshold > 0 && newRepoCandidateCount >= skipThreshold) {

@@ -580,2 +639,5 @@ info(MODULE, `Skipping broad search: already found ${newRepoCandidateCount} viable candidate(s) from new repos (threshold: ${skipThreshold})`);

: null,
phaseErrors["orgs"]
? `Orgs phase (preferred orgs): ${phaseErrors["orgs"]}`
: null,
phaseErrors["1"]

@@ -582,0 +644,0 @@ ? `Phase 1 (starred repos): ${phaseErrors["1"]}`

@@ -20,2 +20,3 @@ /**

excludeOrgs: { type: "array" },
preferredOrgs: { type: "array" },
aiPolicyBlocklist: { type: "array" },

@@ -22,0 +23,0 @@ projectCategories: {

@@ -27,2 +27,3 @@ /**

merged: "merged";
orgs: "orgs";
starred: "starred";

@@ -33,3 +34,3 @@ broad: "broad";

/** All concrete strategies (excludes 'all' meta-strategy). */
export declare const CONCRETE_STRATEGIES: readonly ["merged", "starred", "broad", "maintained"];
export declare const CONCRETE_STRATEGIES: readonly ["merged", "orgs", "starred", "broad", "maintained"];
export declare const RepoSignalsSchema: z.ZodObject<{

@@ -250,2 +251,3 @@ hasActiveMaintainers: z.ZodBoolean;

excludeOrgs: z.ZodDefault<z.ZodArray<z.ZodString>>;
preferredOrgs: z.ZodDefault<z.ZodArray<z.ZodString>>;
aiPolicyBlocklist: z.ZodDefault<z.ZodArray<z.ZodString>>;

@@ -272,2 +274,3 @@ projectCategories: z.ZodDefault<z.ZodArray<z.ZodEnum<{

merged: "merged";
orgs: "orgs";
starred: "starred";

@@ -334,2 +337,3 @@ broad: "broad";

excludeOrgs: z.ZodDefault<z.ZodArray<z.ZodString>>;
preferredOrgs: z.ZodDefault<z.ZodArray<z.ZodString>>;
aiPolicyBlocklist: z.ZodDefault<z.ZodArray<z.ZodString>>;

@@ -356,2 +360,3 @@ projectCategories: z.ZodDefault<z.ZodArray<z.ZodEnum<{

merged: "merged";
orgs: "orgs";
starred: "starred";

@@ -358,0 +363,0 @@ broad: "broad";

@@ -30,2 +30,3 @@ /**

"merged",
"orgs",
"starred",

@@ -39,2 +40,3 @@ "broad",

"merged",
"orgs",
"starred",

@@ -180,2 +182,5 @@ "broad",

excludeOrgs: z.array(z.string()).default([]),
// Orgs whose repos the "orgs" search phase targets with org: qualifiers.
// Empty (the default) disables the phase even when the strategy is enabled.
preferredOrgs: z.array(z.string()).default([]),
aiPolicyBlocklist: z.array(z.string()).default(["matplotlib/matplotlib"]),

@@ -182,0 +187,0 @@ projectCategories: z.array(ProjectCategorySchema).default([]),

+3
-1

@@ -117,4 +117,6 @@ /**

* never needs clamping by the caller.
* @param reservedOps OR operators already consumed by repo/org filters in
* the base query, forwarded to label chunking
*/
export declare function searchAcrossLanguagesAndLabels(octokit: Octokit, languages: string[], isAnyLanguage: boolean, labels: string[], buildBaseQuery: (langQuery: string) => string, perPage: number, tracker?: SearchBudgetTracker, startOffset?: number): Promise<GitHubSearchItem[]>;
export declare function searchAcrossLanguagesAndLabels(octokit: Octokit, languages: string[], isAnyLanguage: boolean, labels: string[], buildBaseQuery: (langQuery: string) => string, perPage: number, tracker?: SearchBudgetTracker, startOffset?: number, reservedOps?: number): Promise<GitHubSearchItem[]>;
/**

@@ -121,0 +123,0 @@ * Shared pipeline: spam-filter, repo-exclusion, vetting, and star-count filter.

@@ -400,4 +400,6 @@ /**

* never needs clamping by the caller.
* @param reservedOps OR operators already consumed by repo/org filters in
* the base query, forwarded to label chunking
*/
export async function searchAcrossLanguagesAndLabels(octokit, languages, isAnyLanguage, labels, buildBaseQuery, perPage, tracker = getSearchBudgetTracker(), startOffset = 0) {
export async function searchAcrossLanguagesAndLabels(octokit, languages, isAnyLanguage, labels, buildBaseQuery, perPage, tracker = getSearchBudgetTracker(), startOffset = 0, reservedOps = 0) {
const variants = buildLanguageVariants(languages, isAnyLanguage, labels.length > 0);

@@ -411,3 +413,3 @@ const k = variants.length > 0 ? startOffset % variants.length : 0;

await sleep(GRAPHQL_INTER_QUERY_DELAY_MS);
const items = await searchWithChunkedLabels(octokit, labels, 0, (labelQ) => `${buildBaseQuery(langVariants[i])} ${labelQ}`
const items = await searchWithChunkedLabels(octokit, labels, reservedOps, (labelQ) => `${buildBaseQuery(langVariants[i])} ${labelQ}`
.replace(/ +/g, " ")

@@ -414,0 +416,0 @@ .trim(), perPage, tracker);

{
"name": "@oss-scout/core",
"version": "1.5.2",
"version": "1.6.0",
"description": "Personalized GitHub issue finder with multi-strategy search, deep vetting, and viability scoring — CLI, library, MCP server, and Claude Code plugin",

@@ -5,0 +5,0 @@ "type": "module",

Sorry, the diff of this file is too big to display