terminalhire 0.35.4 → 0.36.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.
@@ -1748,6 +1748,32 @@ async function openPRClosingRefs(owner, name, token, signal, governor) {
1748
1748
  return null;
1749
1749
  }
1750
1750
  }
1751
+ async function issueCrossRefPRAttempts(owner, name, issueNumber, token, signal, governor) {
1752
+ const url = `https://api.github.com/repos/${owner}/${name}/issues/${issueNumber}/timeline?per_page=100`;
1753
+ try {
1754
+ const res = governor ? await governor.get(url, { headers: ghHeaders(token), signal }) : await fetch(url, { headers: ghHeaders(token), signal });
1755
+ if (!res || !res.ok) return null;
1756
+ const link = res.headers?.get("link") ?? null;
1757
+ const hasNextPage = link != null && /\brel="next"/.test(link);
1758
+ const events = await res.json();
1759
+ if (!Array.isArray(events)) return null;
1760
+ if (hasNextPage || events.length >= 100) return null;
1761
+ let hasOpenPR = false;
1762
+ let hasClosedPR = false;
1763
+ const prNumbers = [];
1764
+ for (const ev of events) {
1765
+ if (ev?.event !== "cross-referenced") continue;
1766
+ const src = ev.source?.issue;
1767
+ if (!src || src.pull_request == null) continue;
1768
+ if (typeof src.number === "number") prNumbers.push(src.number);
1769
+ if (src.state === "open") hasOpenPR = true;
1770
+ else if (src.state === "closed") hasClosedPR = true;
1771
+ }
1772
+ return { hasOpenPR, hasClosedPR, prNumbers };
1773
+ } catch {
1774
+ return null;
1775
+ }
1776
+ }
1751
1777
  function makeScoringGovernor(governor) {
1752
1778
  return governor ?? makeGitHubGovernor(
1753
1779
  ((url, init) => fetch(url, init)),
@@ -3925,6 +3951,30 @@ var init_feeds = __esm({
3925
3951
  });
3926
3952
 
3927
3953
  // ../../packages/core/src/feeds/contributions.ts
3954
+ function readSearchMaxPages() {
3955
+ const raw = process.env["CONTRIB_SEARCH_MAX_PAGES"];
3956
+ if (raw == null) return DEFAULT_SEARCH_MAX_PAGES;
3957
+ const n = Number.parseInt(raw, 10);
3958
+ if (Number.isNaN(n)) return DEFAULT_SEARCH_MAX_PAGES;
3959
+ return Math.min(Math.max(n, MIN_SEARCH_MAX_PAGES), MAX_SEARCH_MAX_PAGES);
3960
+ }
3961
+ function readMaxContribItems() {
3962
+ const raw = process.env["CONTRIB_MAX_ITEMS"];
3963
+ if (raw == null) return DEFAULT_MAX_CONTRIB_ITEMS;
3964
+ const n = Number.parseInt(raw, 10);
3965
+ if (Number.isNaN(n)) return DEFAULT_MAX_CONTRIB_ITEMS;
3966
+ return Math.min(Math.max(n, MIN_MAX_CONTRIB_ITEMS), MAX_MAX_CONTRIB_ITEMS);
3967
+ }
3968
+ function readMaxContribIssuesScanned() {
3969
+ const raw = process.env["CONTRIB_MAX_ISSUES_SCANNED"];
3970
+ if (raw == null) return DEFAULT_MAX_CONTRIB_ISSUES_SCANNED;
3971
+ const n = Number.parseInt(raw, 10);
3972
+ if (Number.isNaN(n)) return DEFAULT_MAX_CONTRIB_ISSUES_SCANNED;
3973
+ return Math.min(
3974
+ Math.max(n, MIN_MAX_CONTRIB_ISSUES_SCANNED),
3975
+ MAX_MAX_CONTRIB_ISSUES_SCANNED
3976
+ );
3977
+ }
3928
3978
  function authHeaders2() {
3929
3979
  const token = process.env["GITHUB_TOKEN"] ?? process.env["GH_TOKEN"];
3930
3980
  const h = {
@@ -4012,13 +4062,21 @@ async function fetchRateLimit(client) {
4012
4062
  }
4013
4063
  async function searchContribIssues(client, queries) {
4014
4064
  const byUrl = /* @__PURE__ */ new Map();
4065
+ const maxPages = readSearchMaxPages();
4015
4066
  for (const q of queries) {
4016
- const res = await client.json(
4017
- `/search/issues?q=${encodeURIComponent(q)}&sort=created&order=desc&per_page=${SEARCH_PER_PAGE2}`
4018
- );
4019
- for (const it of res?.items ?? []) {
4020
- if (it.pull_request) continue;
4021
- if (!byUrl.has(it.html_url)) byUrl.set(it.html_url, it);
4067
+ for (let page = 1; page <= maxPages; page++) {
4068
+ const res = await client.json(
4069
+ `/search/issues?q=${encodeURIComponent(q)}&sort=created&order=desc&per_page=${SEARCH_PER_PAGE2}&page=${page}`
4070
+ );
4071
+ const items = res?.items;
4072
+ if (items == null) break;
4073
+ for (const it of items) {
4074
+ if (it.pull_request) continue;
4075
+ if (!byUrl.has(it.html_url)) byUrl.set(it.html_url, it);
4076
+ }
4077
+ if (items.length < SEARCH_PER_PAGE2) break;
4078
+ const stats = client.getStats();
4079
+ if (stats.budgetAborted || stats.secondaryAborted) break;
4022
4080
  }
4023
4081
  }
4024
4082
  return [...byUrl.values()].sort(
@@ -4079,10 +4137,14 @@ async function aggregateContributions(opts = {}) {
4079
4137
  probeTimeoutMs: opts.fetchImpl ? null : PROBE_TIMEOUT_MS
4080
4138
  });
4081
4139
  const queries = opts.queries ?? CONTRIB_SEARCH_QUERIES;
4140
+ const maxContribItems = readMaxContribItems();
4082
4141
  const startRl = await fetchRateLimit(client);
4083
4142
  const coreHealthyAtStart = (startRl?.core?.remaining ?? 0) >= 500;
4084
4143
  client.setSecondaryHint(coreHealthyAtStart);
4085
- const issues = (await searchContribIssues(client, queries)).slice(0, MAX_CONTRIB_ISSUES_SCANNED);
4144
+ const issues = (await searchContribIssues(client, queries)).slice(
4145
+ 0,
4146
+ readMaxContribIssuesScanned()
4147
+ );
4086
4148
  const repoCache = /* @__PURE__ */ new Map();
4087
4149
  const contribCache = /* @__PURE__ */ new Map();
4088
4150
  const prRefsCache = /* @__PURE__ */ new Map();
@@ -4172,7 +4234,7 @@ async function aggregateContributions(opts = {}) {
4172
4234
  let contribUndefined = 0;
4173
4235
  let prRefsNull = 0;
4174
4236
  for (const issue of issues) {
4175
- if (jobs.length >= MAX_CONTRIB_ITEMS) break;
4237
+ if (jobs.length >= maxContribItems) break;
4176
4238
  const fullName = repoFullNameFromApiUrl2(issue.repository_url);
4177
4239
  if (!fullName) continue;
4178
4240
  const id = `contribute:${repoKey(fullName)}#${issue.number}`;
@@ -4230,7 +4292,7 @@ async function aggregateContributions(opts = {}) {
4230
4292
  const doDiscovery = opts.discoverRepos ?? (opts.trendingSlugs != null || opts.vocabTerms != null);
4231
4293
  let discoveredEmitted = 0;
4232
4294
  let discoveryBudgetStopped = false;
4233
- if (doDiscovery && jobs.length < MAX_CONTRIB_ITEMS) {
4295
+ if (doDiscovery && jobs.length < maxContribItems) {
4234
4296
  const maxRepos = Math.min(
4235
4297
  Math.max(0, opts.maxDiscoveredRepos ?? MAX_DISCOVERED_REPOS),
4236
4298
  MAX_DISCOVERED_REPOS
@@ -4283,7 +4345,7 @@ async function aggregateContributions(opts = {}) {
4283
4345
  }
4284
4346
  const scanned = candidates.slice(0, maxRepos);
4285
4347
  for (const fullName of scanned) {
4286
- if (jobs.length >= MAX_CONTRIB_ITEMS) break;
4348
+ if (jobs.length >= maxContribItems) break;
4287
4349
  if (client.getStats().budgetAborted || client.getStats().secondaryAborted) {
4288
4350
  discoveryBudgetStopped = true;
4289
4351
  break;
@@ -4308,7 +4370,7 @@ async function aggregateContributions(opts = {}) {
4308
4370
  const repoIssues = (searchRes?.items ?? []).filter((it) => !it.pull_request);
4309
4371
  let perRepoDiscovered = 0;
4310
4372
  for (const issue of repoIssues) {
4311
- if (jobs.length >= MAX_CONTRIB_ITEMS) break;
4373
+ if (jobs.length >= maxContribItems) break;
4312
4374
  if (perRepoDiscovered >= MAX_ISSUES_PER_DISCOVERED_REPO) break;
4313
4375
  if ((perRepo.get(repoKey(fullName)) ?? 0) >= MAX_BOUNTIES_PER_DISCOVERED_REPO) break;
4314
4376
  const id = `contribute:${repoKey(fullName)}#${issue.number}`;
@@ -4373,7 +4435,7 @@ async function aggregateContributions(opts = {}) {
4373
4435
  }
4374
4436
  return jobs;
4375
4437
  }
4376
- var GITHUB_API2, CONTRIB_LABEL_QUERIES, CONTRIB_LANGUAGE_QUERIES, CONTRIB_SEARCH_QUERIES, SEARCH_PER_PAGE2, MAX_CONTRIB_ITEMS, MAX_CONTRIB_ISSUES_SCANNED, MAX_DISCOVERED_REPOS, MAX_ISSUES_PER_DISCOVERED_REPO, DISCOVERY_REPOS_PER_TERM, DISCOVERY_VOCAB_TERMS, DISCOVERY_ISSUE_LABELS, repoKey;
4438
+ var GITHUB_API2, CONTRIB_LABEL_QUERIES, CONTRIB_LANGUAGE_QUERIES, CONTRIB_SEARCH_QUERIES, SEARCH_PER_PAGE2, DEFAULT_SEARCH_MAX_PAGES, MIN_SEARCH_MAX_PAGES, MAX_SEARCH_MAX_PAGES, DEFAULT_MAX_CONTRIB_ITEMS, MIN_MAX_CONTRIB_ITEMS, MAX_MAX_CONTRIB_ITEMS, DEFAULT_MAX_CONTRIB_ISSUES_SCANNED, MIN_MAX_CONTRIB_ISSUES_SCANNED, MAX_MAX_CONTRIB_ISSUES_SCANNED, MAX_DISCOVERED_REPOS, MAX_ISSUES_PER_DISCOVERED_REPO, DISCOVERY_REPOS_PER_TERM, DISCOVERY_VOCAB_TERMS, DISCOVERY_ISSUE_LABELS, repoKey;
4377
4439
  var init_contributions = __esm({
4378
4440
  "../../packages/core/src/feeds/contributions.ts"() {
4379
4441
  "use strict";
@@ -4392,7 +4454,11 @@ var init_contributions = __esm({
4392
4454
  'label:"good-first-issue" type:issue state:open',
4393
4455
  'label:"help wanted" type:issue state:open',
4394
4456
  'label:"help-wanted" type:issue state:open',
4395
- 'label:"up-for-grabs" type:issue state:open'
4457
+ 'label:"up-for-grabs" type:issue state:open',
4458
+ // supply-expansion D: two more first-contribution label families widen the
4459
+ // global newest-first slice WITHOUT relaxing the credential gate.
4460
+ 'label:"beginner-friendly" type:issue state:open',
4461
+ 'label:"first-timers-only" type:issue state:open'
4396
4462
  ];
4397
4463
  CONTRIB_LANGUAGE_QUERIES = [
4398
4464
  ...["rust", "go", "python", "c++", "ruby"].map(
@@ -4400,12 +4466,30 @@ var init_contributions = __esm({
4400
4466
  ),
4401
4467
  ...["rust", "go"].map(
4402
4468
  (lang) => `label:"good first issue" language:${lang} type:issue state:open`
4469
+ ),
4470
+ // supply-expansion D: cover the high-volume web/enterprise ecosystems the
4471
+ // original set omitted. TS/JS were previously left out of "good first issue"
4472
+ // (the global slice over-represented them) but a LANGUAGE-scoped page surfaces
4473
+ // DIFFERENT repos than the global newest-first slice, so re-including them widens
4474
+ // distinct-repo coverage rather than duplicating it.
4475
+ ...["typescript", "javascript", "java", "python"].map(
4476
+ (lang) => `label:"good first issue" language:${lang} type:issue state:open`
4477
+ ),
4478
+ ...["typescript", "javascript", "c#", "php"].map(
4479
+ (lang) => `label:"help wanted" language:${lang} type:issue state:open`
4403
4480
  )
4404
4481
  ];
4405
4482
  CONTRIB_SEARCH_QUERIES = [...CONTRIB_LABEL_QUERIES, ...CONTRIB_LANGUAGE_QUERIES];
4406
4483
  SEARCH_PER_PAGE2 = 100;
4407
- MAX_CONTRIB_ITEMS = 150;
4408
- MAX_CONTRIB_ISSUES_SCANNED = 600;
4484
+ DEFAULT_SEARCH_MAX_PAGES = 1;
4485
+ MIN_SEARCH_MAX_PAGES = 1;
4486
+ MAX_SEARCH_MAX_PAGES = 5;
4487
+ DEFAULT_MAX_CONTRIB_ITEMS = 400;
4488
+ MIN_MAX_CONTRIB_ITEMS = 50;
4489
+ MAX_MAX_CONTRIB_ITEMS = 1e3;
4490
+ DEFAULT_MAX_CONTRIB_ISSUES_SCANNED = 1500;
4491
+ MIN_MAX_CONTRIB_ISSUES_SCANNED = 100;
4492
+ MAX_MAX_CONTRIB_ISSUES_SCANNED = 5e3;
4409
4493
  MAX_DISCOVERED_REPOS = 15;
4410
4494
  MAX_ISSUES_PER_DISCOVERED_REPO = 3;
4411
4495
  DISCOVERY_REPOS_PER_TERM = 20;
@@ -8387,6 +8471,7 @@ __export(src_exports, {
8387
8471
  isOverIntroLimit: () => isOverIntroLimit,
8388
8472
  isTrivialPRTitle: () => isTrivialPRTitle,
8389
8473
  isWinnableIssue: () => isWinnableIssue,
8474
+ issueCrossRefPRAttempts: () => issueCrossRefPRAttempts,
8390
8475
  jobShortToken: () => jobShortToken,
8391
8476
  joinLabels: () => joinLabels,
8392
8477
  labelFor: () => labelFor,
@@ -892,7 +892,11 @@ var init_contributions = __esm({
892
892
  'label:"good-first-issue" type:issue state:open',
893
893
  'label:"help wanted" type:issue state:open',
894
894
  'label:"help-wanted" type:issue state:open',
895
- 'label:"up-for-grabs" type:issue state:open'
895
+ 'label:"up-for-grabs" type:issue state:open',
896
+ // supply-expansion D: two more first-contribution label families widen the
897
+ // global newest-first slice WITHOUT relaxing the credential gate.
898
+ 'label:"beginner-friendly" type:issue state:open',
899
+ 'label:"first-timers-only" type:issue state:open'
896
900
  ];
897
901
  CONTRIB_LANGUAGE_QUERIES = [
898
902
  ...["rust", "go", "python", "c++", "ruby"].map(
@@ -900,6 +904,17 @@ var init_contributions = __esm({
900
904
  ),
901
905
  ...["rust", "go"].map(
902
906
  (lang) => `label:"good first issue" language:${lang} type:issue state:open`
907
+ ),
908
+ // supply-expansion D: cover the high-volume web/enterprise ecosystems the
909
+ // original set omitted. TS/JS were previously left out of "good first issue"
910
+ // (the global slice over-represented them) but a LANGUAGE-scoped page surfaces
911
+ // DIFFERENT repos than the global newest-first slice, so re-including them widens
912
+ // distinct-repo coverage rather than duplicating it.
913
+ ...["typescript", "javascript", "java", "python"].map(
914
+ (lang) => `label:"good first issue" language:${lang} type:issue state:open`
915
+ ),
916
+ ...["typescript", "javascript", "c#", "php"].map(
917
+ (lang) => `label:"help wanted" language:${lang} type:issue state:open`
903
918
  )
904
919
  ];
905
920
  CONTRIB_SEARCH_QUERIES = [...CONTRIB_LABEL_QUERIES, ...CONTRIB_LANGUAGE_QUERIES];
@@ -909,7 +909,11 @@ var init_contributions = __esm({
909
909
  'label:"good-first-issue" type:issue state:open',
910
910
  'label:"help wanted" type:issue state:open',
911
911
  'label:"help-wanted" type:issue state:open',
912
- 'label:"up-for-grabs" type:issue state:open'
912
+ 'label:"up-for-grabs" type:issue state:open',
913
+ // supply-expansion D: two more first-contribution label families widen the
914
+ // global newest-first slice WITHOUT relaxing the credential gate.
915
+ 'label:"beginner-friendly" type:issue state:open',
916
+ 'label:"first-timers-only" type:issue state:open'
913
917
  ];
914
918
  CONTRIB_LANGUAGE_QUERIES = [
915
919
  ...["rust", "go", "python", "c++", "ruby"].map(
@@ -917,6 +921,17 @@ var init_contributions = __esm({
917
921
  ),
918
922
  ...["rust", "go"].map(
919
923
  (lang) => `label:"good first issue" language:${lang} type:issue state:open`
924
+ ),
925
+ // supply-expansion D: cover the high-volume web/enterprise ecosystems the
926
+ // original set omitted. TS/JS were previously left out of "good first issue"
927
+ // (the global slice over-represented them) but a LANGUAGE-scoped page surfaces
928
+ // DIFFERENT repos than the global newest-first slice, so re-including them widens
929
+ // distinct-repo coverage rather than duplicating it.
930
+ ...["typescript", "javascript", "java", "python"].map(
931
+ (lang) => `label:"good first issue" language:${lang} type:issue state:open`
932
+ ),
933
+ ...["typescript", "javascript", "c#", "php"].map(
934
+ (lang) => `label:"help wanted" language:${lang} type:issue state:open`
920
935
  )
921
936
  ];
922
937
  CONTRIB_SEARCH_QUERIES = [...CONTRIB_LABEL_QUERIES, ...CONTRIB_LANGUAGE_QUERIES];
@@ -1748,6 +1748,32 @@ async function openPRClosingRefs(owner, name, token, signal, governor) {
1748
1748
  return null;
1749
1749
  }
1750
1750
  }
1751
+ async function issueCrossRefPRAttempts(owner, name, issueNumber, token, signal, governor) {
1752
+ const url = `https://api.github.com/repos/${owner}/${name}/issues/${issueNumber}/timeline?per_page=100`;
1753
+ try {
1754
+ const res = governor ? await governor.get(url, { headers: ghHeaders(token), signal }) : await fetch(url, { headers: ghHeaders(token), signal });
1755
+ if (!res || !res.ok) return null;
1756
+ const link = res.headers?.get("link") ?? null;
1757
+ const hasNextPage = link != null && /\brel="next"/.test(link);
1758
+ const events = await res.json();
1759
+ if (!Array.isArray(events)) return null;
1760
+ if (hasNextPage || events.length >= 100) return null;
1761
+ let hasOpenPR = false;
1762
+ let hasClosedPR = false;
1763
+ const prNumbers = [];
1764
+ for (const ev of events) {
1765
+ if (ev?.event !== "cross-referenced") continue;
1766
+ const src = ev.source?.issue;
1767
+ if (!src || src.pull_request == null) continue;
1768
+ if (typeof src.number === "number") prNumbers.push(src.number);
1769
+ if (src.state === "open") hasOpenPR = true;
1770
+ else if (src.state === "closed") hasClosedPR = true;
1771
+ }
1772
+ return { hasOpenPR, hasClosedPR, prNumbers };
1773
+ } catch {
1774
+ return null;
1775
+ }
1776
+ }
1751
1777
  function makeScoringGovernor(governor) {
1752
1778
  return governor ?? makeGitHubGovernor(
1753
1779
  ((url, init) => fetch(url, init)),
@@ -3925,6 +3951,30 @@ var init_feeds = __esm({
3925
3951
  });
3926
3952
 
3927
3953
  // ../../packages/core/src/feeds/contributions.ts
3954
+ function readSearchMaxPages() {
3955
+ const raw = process.env["CONTRIB_SEARCH_MAX_PAGES"];
3956
+ if (raw == null) return DEFAULT_SEARCH_MAX_PAGES;
3957
+ const n = Number.parseInt(raw, 10);
3958
+ if (Number.isNaN(n)) return DEFAULT_SEARCH_MAX_PAGES;
3959
+ return Math.min(Math.max(n, MIN_SEARCH_MAX_PAGES), MAX_SEARCH_MAX_PAGES);
3960
+ }
3961
+ function readMaxContribItems() {
3962
+ const raw = process.env["CONTRIB_MAX_ITEMS"];
3963
+ if (raw == null) return DEFAULT_MAX_CONTRIB_ITEMS;
3964
+ const n = Number.parseInt(raw, 10);
3965
+ if (Number.isNaN(n)) return DEFAULT_MAX_CONTRIB_ITEMS;
3966
+ return Math.min(Math.max(n, MIN_MAX_CONTRIB_ITEMS), MAX_MAX_CONTRIB_ITEMS);
3967
+ }
3968
+ function readMaxContribIssuesScanned() {
3969
+ const raw = process.env["CONTRIB_MAX_ISSUES_SCANNED"];
3970
+ if (raw == null) return DEFAULT_MAX_CONTRIB_ISSUES_SCANNED;
3971
+ const n = Number.parseInt(raw, 10);
3972
+ if (Number.isNaN(n)) return DEFAULT_MAX_CONTRIB_ISSUES_SCANNED;
3973
+ return Math.min(
3974
+ Math.max(n, MIN_MAX_CONTRIB_ISSUES_SCANNED),
3975
+ MAX_MAX_CONTRIB_ISSUES_SCANNED
3976
+ );
3977
+ }
3928
3978
  function authHeaders2() {
3929
3979
  const token = process.env["GITHUB_TOKEN"] ?? process.env["GH_TOKEN"];
3930
3980
  const h = {
@@ -4012,13 +4062,21 @@ async function fetchRateLimit(client) {
4012
4062
  }
4013
4063
  async function searchContribIssues(client, queries) {
4014
4064
  const byUrl = /* @__PURE__ */ new Map();
4065
+ const maxPages = readSearchMaxPages();
4015
4066
  for (const q of queries) {
4016
- const res = await client.json(
4017
- `/search/issues?q=${encodeURIComponent(q)}&sort=created&order=desc&per_page=${SEARCH_PER_PAGE2}`
4018
- );
4019
- for (const it of res?.items ?? []) {
4020
- if (it.pull_request) continue;
4021
- if (!byUrl.has(it.html_url)) byUrl.set(it.html_url, it);
4067
+ for (let page = 1; page <= maxPages; page++) {
4068
+ const res = await client.json(
4069
+ `/search/issues?q=${encodeURIComponent(q)}&sort=created&order=desc&per_page=${SEARCH_PER_PAGE2}&page=${page}`
4070
+ );
4071
+ const items = res?.items;
4072
+ if (items == null) break;
4073
+ for (const it of items) {
4074
+ if (it.pull_request) continue;
4075
+ if (!byUrl.has(it.html_url)) byUrl.set(it.html_url, it);
4076
+ }
4077
+ if (items.length < SEARCH_PER_PAGE2) break;
4078
+ const stats = client.getStats();
4079
+ if (stats.budgetAborted || stats.secondaryAborted) break;
4022
4080
  }
4023
4081
  }
4024
4082
  return [...byUrl.values()].sort(
@@ -4079,10 +4137,14 @@ async function aggregateContributions(opts = {}) {
4079
4137
  probeTimeoutMs: opts.fetchImpl ? null : PROBE_TIMEOUT_MS
4080
4138
  });
4081
4139
  const queries = opts.queries ?? CONTRIB_SEARCH_QUERIES;
4140
+ const maxContribItems = readMaxContribItems();
4082
4141
  const startRl = await fetchRateLimit(client);
4083
4142
  const coreHealthyAtStart = (startRl?.core?.remaining ?? 0) >= 500;
4084
4143
  client.setSecondaryHint(coreHealthyAtStart);
4085
- const issues = (await searchContribIssues(client, queries)).slice(0, MAX_CONTRIB_ISSUES_SCANNED);
4144
+ const issues = (await searchContribIssues(client, queries)).slice(
4145
+ 0,
4146
+ readMaxContribIssuesScanned()
4147
+ );
4086
4148
  const repoCache = /* @__PURE__ */ new Map();
4087
4149
  const contribCache = /* @__PURE__ */ new Map();
4088
4150
  const prRefsCache = /* @__PURE__ */ new Map();
@@ -4172,7 +4234,7 @@ async function aggregateContributions(opts = {}) {
4172
4234
  let contribUndefined = 0;
4173
4235
  let prRefsNull = 0;
4174
4236
  for (const issue of issues) {
4175
- if (jobs.length >= MAX_CONTRIB_ITEMS) break;
4237
+ if (jobs.length >= maxContribItems) break;
4176
4238
  const fullName = repoFullNameFromApiUrl2(issue.repository_url);
4177
4239
  if (!fullName) continue;
4178
4240
  const id = `contribute:${repoKey(fullName)}#${issue.number}`;
@@ -4230,7 +4292,7 @@ async function aggregateContributions(opts = {}) {
4230
4292
  const doDiscovery = opts.discoverRepos ?? (opts.trendingSlugs != null || opts.vocabTerms != null);
4231
4293
  let discoveredEmitted = 0;
4232
4294
  let discoveryBudgetStopped = false;
4233
- if (doDiscovery && jobs.length < MAX_CONTRIB_ITEMS) {
4295
+ if (doDiscovery && jobs.length < maxContribItems) {
4234
4296
  const maxRepos = Math.min(
4235
4297
  Math.max(0, opts.maxDiscoveredRepos ?? MAX_DISCOVERED_REPOS),
4236
4298
  MAX_DISCOVERED_REPOS
@@ -4283,7 +4345,7 @@ async function aggregateContributions(opts = {}) {
4283
4345
  }
4284
4346
  const scanned = candidates.slice(0, maxRepos);
4285
4347
  for (const fullName of scanned) {
4286
- if (jobs.length >= MAX_CONTRIB_ITEMS) break;
4348
+ if (jobs.length >= maxContribItems) break;
4287
4349
  if (client.getStats().budgetAborted || client.getStats().secondaryAborted) {
4288
4350
  discoveryBudgetStopped = true;
4289
4351
  break;
@@ -4308,7 +4370,7 @@ async function aggregateContributions(opts = {}) {
4308
4370
  const repoIssues = (searchRes?.items ?? []).filter((it) => !it.pull_request);
4309
4371
  let perRepoDiscovered = 0;
4310
4372
  for (const issue of repoIssues) {
4311
- if (jobs.length >= MAX_CONTRIB_ITEMS) break;
4373
+ if (jobs.length >= maxContribItems) break;
4312
4374
  if (perRepoDiscovered >= MAX_ISSUES_PER_DISCOVERED_REPO) break;
4313
4375
  if ((perRepo.get(repoKey(fullName)) ?? 0) >= MAX_BOUNTIES_PER_DISCOVERED_REPO) break;
4314
4376
  const id = `contribute:${repoKey(fullName)}#${issue.number}`;
@@ -4373,7 +4435,7 @@ async function aggregateContributions(opts = {}) {
4373
4435
  }
4374
4436
  return jobs;
4375
4437
  }
4376
- var GITHUB_API2, CONTRIB_LABEL_QUERIES, CONTRIB_LANGUAGE_QUERIES, CONTRIB_SEARCH_QUERIES, SEARCH_PER_PAGE2, MAX_CONTRIB_ITEMS, MAX_CONTRIB_ISSUES_SCANNED, MAX_DISCOVERED_REPOS, MAX_ISSUES_PER_DISCOVERED_REPO, DISCOVERY_REPOS_PER_TERM, DISCOVERY_VOCAB_TERMS, DISCOVERY_ISSUE_LABELS, repoKey;
4438
+ var GITHUB_API2, CONTRIB_LABEL_QUERIES, CONTRIB_LANGUAGE_QUERIES, CONTRIB_SEARCH_QUERIES, SEARCH_PER_PAGE2, DEFAULT_SEARCH_MAX_PAGES, MIN_SEARCH_MAX_PAGES, MAX_SEARCH_MAX_PAGES, DEFAULT_MAX_CONTRIB_ITEMS, MIN_MAX_CONTRIB_ITEMS, MAX_MAX_CONTRIB_ITEMS, DEFAULT_MAX_CONTRIB_ISSUES_SCANNED, MIN_MAX_CONTRIB_ISSUES_SCANNED, MAX_MAX_CONTRIB_ISSUES_SCANNED, MAX_DISCOVERED_REPOS, MAX_ISSUES_PER_DISCOVERED_REPO, DISCOVERY_REPOS_PER_TERM, DISCOVERY_VOCAB_TERMS, DISCOVERY_ISSUE_LABELS, repoKey;
4377
4439
  var init_contributions = __esm({
4378
4440
  "../../packages/core/src/feeds/contributions.ts"() {
4379
4441
  "use strict";
@@ -4392,7 +4454,11 @@ var init_contributions = __esm({
4392
4454
  'label:"good-first-issue" type:issue state:open',
4393
4455
  'label:"help wanted" type:issue state:open',
4394
4456
  'label:"help-wanted" type:issue state:open',
4395
- 'label:"up-for-grabs" type:issue state:open'
4457
+ 'label:"up-for-grabs" type:issue state:open',
4458
+ // supply-expansion D: two more first-contribution label families widen the
4459
+ // global newest-first slice WITHOUT relaxing the credential gate.
4460
+ 'label:"beginner-friendly" type:issue state:open',
4461
+ 'label:"first-timers-only" type:issue state:open'
4396
4462
  ];
4397
4463
  CONTRIB_LANGUAGE_QUERIES = [
4398
4464
  ...["rust", "go", "python", "c++", "ruby"].map(
@@ -4400,12 +4466,30 @@ var init_contributions = __esm({
4400
4466
  ),
4401
4467
  ...["rust", "go"].map(
4402
4468
  (lang) => `label:"good first issue" language:${lang} type:issue state:open`
4469
+ ),
4470
+ // supply-expansion D: cover the high-volume web/enterprise ecosystems the
4471
+ // original set omitted. TS/JS were previously left out of "good first issue"
4472
+ // (the global slice over-represented them) but a LANGUAGE-scoped page surfaces
4473
+ // DIFFERENT repos than the global newest-first slice, so re-including them widens
4474
+ // distinct-repo coverage rather than duplicating it.
4475
+ ...["typescript", "javascript", "java", "python"].map(
4476
+ (lang) => `label:"good first issue" language:${lang} type:issue state:open`
4477
+ ),
4478
+ ...["typescript", "javascript", "c#", "php"].map(
4479
+ (lang) => `label:"help wanted" language:${lang} type:issue state:open`
4403
4480
  )
4404
4481
  ];
4405
4482
  CONTRIB_SEARCH_QUERIES = [...CONTRIB_LABEL_QUERIES, ...CONTRIB_LANGUAGE_QUERIES];
4406
4483
  SEARCH_PER_PAGE2 = 100;
4407
- MAX_CONTRIB_ITEMS = 150;
4408
- MAX_CONTRIB_ISSUES_SCANNED = 600;
4484
+ DEFAULT_SEARCH_MAX_PAGES = 1;
4485
+ MIN_SEARCH_MAX_PAGES = 1;
4486
+ MAX_SEARCH_MAX_PAGES = 5;
4487
+ DEFAULT_MAX_CONTRIB_ITEMS = 400;
4488
+ MIN_MAX_CONTRIB_ITEMS = 50;
4489
+ MAX_MAX_CONTRIB_ITEMS = 1e3;
4490
+ DEFAULT_MAX_CONTRIB_ISSUES_SCANNED = 1500;
4491
+ MIN_MAX_CONTRIB_ISSUES_SCANNED = 100;
4492
+ MAX_MAX_CONTRIB_ISSUES_SCANNED = 5e3;
4409
4493
  MAX_DISCOVERED_REPOS = 15;
4410
4494
  MAX_ISSUES_PER_DISCOVERED_REPO = 3;
4411
4495
  DISCOVERY_REPOS_PER_TERM = 20;
@@ -8387,6 +8471,7 @@ __export(src_exports, {
8387
8471
  isOverIntroLimit: () => isOverIntroLimit,
8388
8472
  isTrivialPRTitle: () => isTrivialPRTitle,
8389
8473
  isWinnableIssue: () => isWinnableIssue,
8474
+ issueCrossRefPRAttempts: () => issueCrossRefPRAttempts,
8390
8475
  jobShortToken: () => jobShortToken,
8391
8476
  joinLabels: () => joinLabels,
8392
8477
  labelFor: () => labelFor,
@@ -1280,7 +1280,11 @@ var init_contributions = __esm({
1280
1280
  'label:"good-first-issue" type:issue state:open',
1281
1281
  'label:"help wanted" type:issue state:open',
1282
1282
  'label:"help-wanted" type:issue state:open',
1283
- 'label:"up-for-grabs" type:issue state:open'
1283
+ 'label:"up-for-grabs" type:issue state:open',
1284
+ // supply-expansion D: two more first-contribution label families widen the
1285
+ // global newest-first slice WITHOUT relaxing the credential gate.
1286
+ 'label:"beginner-friendly" type:issue state:open',
1287
+ 'label:"first-timers-only" type:issue state:open'
1284
1288
  ];
1285
1289
  CONTRIB_LANGUAGE_QUERIES = [
1286
1290
  ...["rust", "go", "python", "c++", "ruby"].map(
@@ -1288,6 +1292,17 @@ var init_contributions = __esm({
1288
1292
  ),
1289
1293
  ...["rust", "go"].map(
1290
1294
  (lang) => `label:"good first issue" language:${lang} type:issue state:open`
1295
+ ),
1296
+ // supply-expansion D: cover the high-volume web/enterprise ecosystems the
1297
+ // original set omitted. TS/JS were previously left out of "good first issue"
1298
+ // (the global slice over-represented them) but a LANGUAGE-scoped page surfaces
1299
+ // DIFFERENT repos than the global newest-first slice, so re-including them widens
1300
+ // distinct-repo coverage rather than duplicating it.
1301
+ ...["typescript", "javascript", "java", "python"].map(
1302
+ (lang) => `label:"good first issue" language:${lang} type:issue state:open`
1303
+ ),
1304
+ ...["typescript", "javascript", "c#", "php"].map(
1305
+ (lang) => `label:"help wanted" language:${lang} type:issue state:open`
1291
1306
  )
1292
1307
  ];
1293
1308
  CONTRIB_SEARCH_QUERIES = [...CONTRIB_LABEL_QUERIES, ...CONTRIB_LANGUAGE_QUERIES];