@pratik7368patil/anchor-core 0.1.12 → 0.1.13
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 +49 -2
- package/dist/index.js +191 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -244,6 +244,14 @@ type FetchPullRequestsProgress = {
|
|
|
244
244
|
total: number;
|
|
245
245
|
prNumber: number;
|
|
246
246
|
detailConcurrency: number;
|
|
247
|
+
} | {
|
|
248
|
+
stage: "github_rate_limited";
|
|
249
|
+
repo: string;
|
|
250
|
+
waitSeconds: number;
|
|
251
|
+
retryAt: string;
|
|
252
|
+
reason: string;
|
|
253
|
+
request: string;
|
|
254
|
+
attempt: number;
|
|
247
255
|
};
|
|
248
256
|
type IndexPullRequestsProgress = {
|
|
249
257
|
stage: "indexing_pull_request";
|
|
@@ -738,6 +746,45 @@ declare const DEMO_CODE_FILES: Record<string, string>;
|
|
|
738
746
|
|
|
739
747
|
declare function createGitHubClient(token: string): Octokit;
|
|
740
748
|
|
|
749
|
+
type GitHubRateLimitProgress = {
|
|
750
|
+
waitSeconds: number;
|
|
751
|
+
retryAt: string;
|
|
752
|
+
reason: string;
|
|
753
|
+
request: string;
|
|
754
|
+
attempt: number;
|
|
755
|
+
};
|
|
756
|
+
type GitHubRateLimitController = {
|
|
757
|
+
onRateLimit?: (progress: GitHubRateLimitProgress) => void;
|
|
758
|
+
sleep?: (milliseconds: number) => Promise<void>;
|
|
759
|
+
now?: () => number;
|
|
760
|
+
blockedUntilMs?: number;
|
|
761
|
+
};
|
|
762
|
+
type GitHubRateLimitErrorLike = {
|
|
763
|
+
status?: number;
|
|
764
|
+
message?: string;
|
|
765
|
+
response?: {
|
|
766
|
+
headers?: Record<string, string | number | undefined>;
|
|
767
|
+
};
|
|
768
|
+
};
|
|
769
|
+
type GitHubResponse<T> = {
|
|
770
|
+
data: T;
|
|
771
|
+
headers: Record<string, string | number | undefined>;
|
|
772
|
+
};
|
|
773
|
+
declare function isGitHubRateLimitError(error: unknown): error is GitHubRateLimitErrorLike;
|
|
774
|
+
declare function getGitHubRateLimitDelayMs(error: GitHubRateLimitErrorLike, attempt: number, now?: number): {
|
|
775
|
+
delayMs: number;
|
|
776
|
+
reason: string;
|
|
777
|
+
};
|
|
778
|
+
declare function requestWithGitHubRateLimit<T>(request: () => Promise<T>, options: {
|
|
779
|
+
controller: GitHubRateLimitController;
|
|
780
|
+
requestName: string;
|
|
781
|
+
maxRetries?: number;
|
|
782
|
+
}): Promise<T>;
|
|
783
|
+
declare function paginateWithGitHubRateLimit<T>(requestPage: (page: number) => Promise<GitHubResponse<T[]>>, options: {
|
|
784
|
+
controller: GitHubRateLimitController;
|
|
785
|
+
requestName: string;
|
|
786
|
+
}): Promise<T[]>;
|
|
787
|
+
|
|
741
788
|
type FetchPullRequestsOptions = {
|
|
742
789
|
token: string;
|
|
743
790
|
repo: string;
|
|
@@ -751,7 +798,7 @@ declare function resolvePullRequestFetchLimit(options: Pick<FetchPullRequestsOpt
|
|
|
751
798
|
declare function resolvePullRequestDetailConcurrency(options: Pick<FetchPullRequestsOptions, "detailConcurrency">): number;
|
|
752
799
|
declare function fetchMergedPullRequests(options: FetchPullRequestsOptions): Promise<PullRequestRecord[]>;
|
|
753
800
|
|
|
754
|
-
declare function fetchPullRequestDetails(octokit: Octokit, repoFullName: string, pullNumber: number): Promise<PullRequestRecord>;
|
|
801
|
+
declare function fetchPullRequestDetails(octokit: Octokit, repoFullName: string, pullNumber: number, controller?: GitHubRateLimitController): Promise<PullRequestRecord>;
|
|
755
802
|
|
|
756
803
|
type DoctorOptions = {
|
|
757
804
|
cwd: string;
|
|
@@ -766,4 +813,4 @@ declare function getAnchorIndexHealth(cwd: string): AnchorIndexHealth & {
|
|
|
766
813
|
indexStatus: IndexStatus;
|
|
767
814
|
};
|
|
768
815
|
|
|
769
|
-
export { ANCHOR_CURSOR_RULE, type AnchorContextInput, type AnchorDatabase, type AnchorExplainFileInput, type AnchorIndexHealth, type AnchorReviewDiffInput, type ArchitectureArea, type ArchitectureCheckInput, type ArchitectureComponent, type ArchitectureContextInput, type ArchitectureIndexData, type ArchitecturePattern, type ArchitectureQueryInput, type ChunkableCodeFile, type CodeChunk, type CodeFileDiscoveryResult, type CodeFileRecord, type CodeImport, type CodeIndexProgress, type CodeIndexSummary, type ConfidenceLevel, type CoverageGrade, type CoverageInput, type CoverageReport, type CurrentCodeSnapshot, type CursorMcpConfig, DEFAULT_MAX_CODE_FILE_BYTES, DEMO_CODE_FILES, DEMO_PULL_REQUESTS, DEMO_REPO, type DiscoveredCodeFile, type DoctorCheck, type DoctorOptions, type DoctorReport, type EvidenceRef, type FetchPullRequestsOptions, type FetchPullRequestsProgress, type FormattedResult, type FreshnessResult, type FreshnessStatus, type GitHubRepo, type GitHubTokenResolution, type GitHubTokenResolverOptions, type GitHubTokenSource, type IndexPullRequestsProgress, type IndexRunRecord, type IndexStatus, type IndexSummary, type LocalEmbeddingProvider, type PullRequestComment, type PullRequestCommit, type PullRequestFile, type PullRequestPerson, type PullRequestRecord, type RankedArchitecturePattern, type RankedCodeChunk, type RankedRegressionEvent, type RankedTeamRule, type RankedTestFile, type RankedWisdomUnit, type RegressionEvent, type RulesAddInput, type RulesAddResult, type RulesEvidenceCheckResult, type RulesInitResult, type RulesSuggestOptions, SCHEMA_SQL, type SearchHistoryInput, type SemanticStatus, type SourceType, type SuggestedPrompt, TEAM_RULES_FILE, type TeamRule, type TeamRuleSuggestion, type TeamRulesValidationResult, type TestFileRecord, type TestLink, type WisdomCategory, type WisdomUnit, addTeamRule, anchorMcpEntry, architectureFilesFromDiff, buildAnchorContextResult, buildArchitectureIndex, buildFtsQuery, buildQueryTerms, calculateCoverage, canonicalizeText, categorizeWisdom, checkArchitecture, checkSchema, checkTeamRuleEvidence, chunkCodeFile, chunkHistoricalText, claimKeyFor, clampMaxResults, classifyArchitectureArea, clipSentence, confidenceAtLeast, confidenceLevelFor, confidenceRank, confidenceReasonsFor, countValidTeamRules, createGitHubClient, defaultDatabasePath, detectGitHubRepo, detectGitRoot, discoverCodeFiles, emptyCodeIndexSummary, ensureAnchorGitExclude, ensureCursorConfig, ensureCursorRule, ensureRepository, ensureTeamRulesFile, evaluateFreshness, evaluateIndexHealth, evidenceForWisdom, explainFile, extractCodeImports, extractCodeSymbols, extractRegressionEvents, extractSymbols, extractWisdomUnits, fetchMergedPullRequests, fetchPullRequestDetails, filesFromDiff, formatAnchorContext, formatIndexStatus, formatSearchHistory, getAnchorIndexHealth, getArchitectureContext, getIndexStatus, getLastSyncTime, getSemanticStatus, getSuggestedPromptTexts, getSuggestedPrompts, getWisdomCategoryCounts, githubAuthFixMessage, hasHighSignalLanguage, indexCodebase, indexPullRequests, inferTestAwareness, initializeSchema, isHardExcludedCodePath, isTestFilePath, loadCurrentCodeSnapshot, loadTeamRulesFile, mergeAnchorMcpConfig, normalizePullRequest, openAnchorDatabase, parseGitHubRemote, rankArchitecturePatterns, rankCodeChunks, rankRegressionEvents, rankRelevantTests, rankTeamRules, rankWisdomUnits, recordIndexRun, redactSecrets, redactedHistoricalText, replaceCodeIndex, resolveGitHubToken, resolvePullRequestDetailConcurrency, resolvePullRequestFetchLimit, reviewDiff, runDoctor, sanitizeHistoricalText, shouldSyncSince, sourceTypeLabel, stripPromptInjection, suggestTeamRules, tokenizeSearchText, truncateText, uniqueStrings, updateSyncState, upsertPullRequest, validateTeamRulesFile };
|
|
816
|
+
export { ANCHOR_CURSOR_RULE, type AnchorContextInput, type AnchorDatabase, type AnchorExplainFileInput, type AnchorIndexHealth, type AnchorReviewDiffInput, type ArchitectureArea, type ArchitectureCheckInput, type ArchitectureComponent, type ArchitectureContextInput, type ArchitectureIndexData, type ArchitecturePattern, type ArchitectureQueryInput, type ChunkableCodeFile, type CodeChunk, type CodeFileDiscoveryResult, type CodeFileRecord, type CodeImport, type CodeIndexProgress, type CodeIndexSummary, type ConfidenceLevel, type CoverageGrade, type CoverageInput, type CoverageReport, type CurrentCodeSnapshot, type CursorMcpConfig, DEFAULT_MAX_CODE_FILE_BYTES, DEMO_CODE_FILES, DEMO_PULL_REQUESTS, DEMO_REPO, type DiscoveredCodeFile, type DoctorCheck, type DoctorOptions, type DoctorReport, type EvidenceRef, type FetchPullRequestsOptions, type FetchPullRequestsProgress, type FormattedResult, type FreshnessResult, type FreshnessStatus, type GitHubRateLimitController, type GitHubRateLimitErrorLike, type GitHubRateLimitProgress, type GitHubRepo, type GitHubTokenResolution, type GitHubTokenResolverOptions, type GitHubTokenSource, type IndexPullRequestsProgress, type IndexRunRecord, type IndexStatus, type IndexSummary, type LocalEmbeddingProvider, type PullRequestComment, type PullRequestCommit, type PullRequestFile, type PullRequestPerson, type PullRequestRecord, type RankedArchitecturePattern, type RankedCodeChunk, type RankedRegressionEvent, type RankedTeamRule, type RankedTestFile, type RankedWisdomUnit, type RegressionEvent, type RulesAddInput, type RulesAddResult, type RulesEvidenceCheckResult, type RulesInitResult, type RulesSuggestOptions, SCHEMA_SQL, type SearchHistoryInput, type SemanticStatus, type SourceType, type SuggestedPrompt, TEAM_RULES_FILE, type TeamRule, type TeamRuleSuggestion, type TeamRulesValidationResult, type TestFileRecord, type TestLink, type WisdomCategory, type WisdomUnit, addTeamRule, anchorMcpEntry, architectureFilesFromDiff, buildAnchorContextResult, buildArchitectureIndex, buildFtsQuery, buildQueryTerms, calculateCoverage, canonicalizeText, categorizeWisdom, checkArchitecture, checkSchema, checkTeamRuleEvidence, chunkCodeFile, chunkHistoricalText, claimKeyFor, clampMaxResults, classifyArchitectureArea, clipSentence, confidenceAtLeast, confidenceLevelFor, confidenceRank, confidenceReasonsFor, countValidTeamRules, createGitHubClient, defaultDatabasePath, detectGitHubRepo, detectGitRoot, discoverCodeFiles, emptyCodeIndexSummary, ensureAnchorGitExclude, ensureCursorConfig, ensureCursorRule, ensureRepository, ensureTeamRulesFile, evaluateFreshness, evaluateIndexHealth, evidenceForWisdom, explainFile, extractCodeImports, extractCodeSymbols, extractRegressionEvents, extractSymbols, extractWisdomUnits, fetchMergedPullRequests, fetchPullRequestDetails, filesFromDiff, formatAnchorContext, formatIndexStatus, formatSearchHistory, getAnchorIndexHealth, getArchitectureContext, getGitHubRateLimitDelayMs, getIndexStatus, getLastSyncTime, getSemanticStatus, getSuggestedPromptTexts, getSuggestedPrompts, getWisdomCategoryCounts, githubAuthFixMessage, hasHighSignalLanguage, indexCodebase, indexPullRequests, inferTestAwareness, initializeSchema, isGitHubRateLimitError, isHardExcludedCodePath, isTestFilePath, loadCurrentCodeSnapshot, loadTeamRulesFile, mergeAnchorMcpConfig, normalizePullRequest, openAnchorDatabase, paginateWithGitHubRateLimit, parseGitHubRemote, rankArchitecturePatterns, rankCodeChunks, rankRegressionEvents, rankRelevantTests, rankTeamRules, rankWisdomUnits, recordIndexRun, redactSecrets, redactedHistoricalText, replaceCodeIndex, requestWithGitHubRateLimit, resolveGitHubToken, resolvePullRequestDetailConcurrency, resolvePullRequestFetchLimit, reviewDiff, runDoctor, sanitizeHistoricalText, shouldSyncSince, sourceTypeLabel, stripPromptInjection, suggestTeamRules, tokenizeSearchText, truncateText, uniqueStrings, updateSyncState, upsertPullRequest, validateTeamRulesFile };
|
package/dist/index.js
CHANGED
|
@@ -4554,28 +4554,168 @@ function createGitHubClient(token) {
|
|
|
4554
4554
|
});
|
|
4555
4555
|
}
|
|
4556
4556
|
|
|
4557
|
+
// src/github/rate-limit.ts
|
|
4558
|
+
function isGitHubRateLimitError(error) {
|
|
4559
|
+
const candidate = error;
|
|
4560
|
+
if (candidate.status !== 403 && candidate.status !== 429) return false;
|
|
4561
|
+
const message = candidate.message?.toLowerCase() ?? "";
|
|
4562
|
+
const headers = candidate.response?.headers ?? {};
|
|
4563
|
+
return candidate.status === 429 || headers["retry-after"] !== void 0 || headers["x-ratelimit-remaining"] === "0" || message.includes("rate limit") || message.includes("secondary limit");
|
|
4564
|
+
}
|
|
4565
|
+
function getGitHubRateLimitDelayMs(error, attempt, now = Date.now()) {
|
|
4566
|
+
const headers = error.response?.headers ?? {};
|
|
4567
|
+
const retryAfter = Number(headers["retry-after"]);
|
|
4568
|
+
if (Number.isFinite(retryAfter) && retryAfter > 0) {
|
|
4569
|
+
return {
|
|
4570
|
+
delayMs: Math.ceil(retryAfter * 1e3),
|
|
4571
|
+
reason: `retry-after header requested ${Math.ceil(retryAfter)} seconds`
|
|
4572
|
+
};
|
|
4573
|
+
}
|
|
4574
|
+
const remaining = String(headers["x-ratelimit-remaining"] ?? "");
|
|
4575
|
+
const reset = Number(headers["x-ratelimit-reset"]);
|
|
4576
|
+
if (remaining === "0" && Number.isFinite(reset) && reset > 0) {
|
|
4577
|
+
const resetDelayMs = Math.max(0, reset * 1e3 - now);
|
|
4578
|
+
return {
|
|
4579
|
+
delayMs: Math.ceil(resetDelayMs + 2e3),
|
|
4580
|
+
reason: `primary rate limit resets at ${new Date(reset * 1e3).toISOString()}`
|
|
4581
|
+
};
|
|
4582
|
+
}
|
|
4583
|
+
const backoffSeconds = Math.min(900, 60 * 2 ** Math.max(0, attempt - 1));
|
|
4584
|
+
return {
|
|
4585
|
+
delayMs: backoffSeconds * 1e3,
|
|
4586
|
+
reason: `secondary rate limit backoff for ${backoffSeconds} seconds`
|
|
4587
|
+
};
|
|
4588
|
+
}
|
|
4589
|
+
async function sleep(milliseconds) {
|
|
4590
|
+
await new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
4591
|
+
}
|
|
4592
|
+
async function waitForGlobalBlock(controller) {
|
|
4593
|
+
const now = controller.now?.() ?? Date.now();
|
|
4594
|
+
const waitMs = Math.max(0, (controller.blockedUntilMs ?? 0) - now);
|
|
4595
|
+
if (waitMs > 0) {
|
|
4596
|
+
await (controller.sleep ?? sleep)(waitMs);
|
|
4597
|
+
}
|
|
4598
|
+
}
|
|
4599
|
+
async function requestWithGitHubRateLimit(request, options) {
|
|
4600
|
+
const maxRetries = options.maxRetries ?? 8;
|
|
4601
|
+
for (let attempt = 1; ; attempt += 1) {
|
|
4602
|
+
await waitForGlobalBlock(options.controller);
|
|
4603
|
+
try {
|
|
4604
|
+
return await request();
|
|
4605
|
+
} catch (error) {
|
|
4606
|
+
if (!isGitHubRateLimitError(error) || attempt > maxRetries) throw error;
|
|
4607
|
+
const now = options.controller.now?.() ?? Date.now();
|
|
4608
|
+
const { delayMs, reason } = getGitHubRateLimitDelayMs(error, attempt, now);
|
|
4609
|
+
const retryAtMs = now + delayMs;
|
|
4610
|
+
options.controller.blockedUntilMs = Math.max(
|
|
4611
|
+
options.controller.blockedUntilMs ?? 0,
|
|
4612
|
+
retryAtMs
|
|
4613
|
+
);
|
|
4614
|
+
options.controller.onRateLimit?.({
|
|
4615
|
+
waitSeconds: Math.ceil(delayMs / 1e3),
|
|
4616
|
+
retryAt: new Date(retryAtMs).toISOString(),
|
|
4617
|
+
reason,
|
|
4618
|
+
request: options.requestName,
|
|
4619
|
+
attempt
|
|
4620
|
+
});
|
|
4621
|
+
await (options.controller.sleep ?? sleep)(delayMs);
|
|
4622
|
+
}
|
|
4623
|
+
}
|
|
4624
|
+
}
|
|
4625
|
+
function hasNextPage(headers) {
|
|
4626
|
+
return String(headers.link ?? "").includes('rel="next"');
|
|
4627
|
+
}
|
|
4628
|
+
async function paginateWithGitHubRateLimit(requestPage, options) {
|
|
4629
|
+
const results = [];
|
|
4630
|
+
for (let page = 1; ; page += 1) {
|
|
4631
|
+
const response = await requestWithGitHubRateLimit(() => requestPage(page), {
|
|
4632
|
+
controller: options.controller,
|
|
4633
|
+
requestName: `${options.requestName} page ${page}`
|
|
4634
|
+
});
|
|
4635
|
+
results.push(...response.data);
|
|
4636
|
+
if (!hasNextPage(response.headers) && response.data.length < 100) break;
|
|
4637
|
+
if (!hasNextPage(response.headers) && response.data.length === 0) break;
|
|
4638
|
+
if (!hasNextPage(response.headers)) break;
|
|
4639
|
+
}
|
|
4640
|
+
return results;
|
|
4641
|
+
}
|
|
4642
|
+
|
|
4557
4643
|
// src/github/fetch-pr-details.ts
|
|
4558
|
-
async function fetchPullRequestDetails(octokit, repoFullName, pullNumber) {
|
|
4644
|
+
async function fetchPullRequestDetails(octokit, repoFullName, pullNumber, controller = {}) {
|
|
4559
4645
|
const [owner, repo] = repoFullName.split("/");
|
|
4560
4646
|
if (!owner || !repo) throw new Error(`Invalid repo '${repoFullName}'. Expected owner/name.`);
|
|
4561
|
-
const
|
|
4562
|
-
octokit.pulls.get({ owner, repo, pull_number: pullNumber }),
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4647
|
+
const { data: pull } = await requestWithGitHubRateLimit(
|
|
4648
|
+
() => octokit.pulls.get({ owner, repo, pull_number: pullNumber }),
|
|
4649
|
+
{
|
|
4650
|
+
controller,
|
|
4651
|
+
requestName: `GET /repos/${repoFullName}/pulls/${pullNumber}`
|
|
4652
|
+
}
|
|
4653
|
+
);
|
|
4654
|
+
const files = await paginateWithGitHubRateLimit(
|
|
4655
|
+
(page) => octokit.pulls.listFiles({
|
|
4656
|
+
owner,
|
|
4657
|
+
repo,
|
|
4658
|
+
pull_number: pullNumber,
|
|
4659
|
+
per_page: 100,
|
|
4660
|
+
page
|
|
4661
|
+
}),
|
|
4662
|
+
{
|
|
4663
|
+
controller,
|
|
4664
|
+
requestName: `GET /repos/${repoFullName}/pulls/${pullNumber}/files`
|
|
4665
|
+
}
|
|
4666
|
+
);
|
|
4667
|
+
const reviews = await paginateWithGitHubRateLimit(
|
|
4668
|
+
(page) => octokit.pulls.listReviews({
|
|
4566
4669
|
owner,
|
|
4567
4670
|
repo,
|
|
4568
4671
|
pull_number: pullNumber,
|
|
4569
|
-
per_page: 100
|
|
4672
|
+
per_page: 100,
|
|
4673
|
+
page
|
|
4570
4674
|
}),
|
|
4571
|
-
|
|
4675
|
+
{
|
|
4676
|
+
controller,
|
|
4677
|
+
requestName: `GET /repos/${repoFullName}/pulls/${pullNumber}/reviews`
|
|
4678
|
+
}
|
|
4679
|
+
);
|
|
4680
|
+
const reviewComments = await paginateWithGitHubRateLimit(
|
|
4681
|
+
(page) => octokit.pulls.listReviewComments({
|
|
4682
|
+
owner,
|
|
4683
|
+
repo,
|
|
4684
|
+
pull_number: pullNumber,
|
|
4685
|
+
per_page: 100,
|
|
4686
|
+
page
|
|
4687
|
+
}),
|
|
4688
|
+
{
|
|
4689
|
+
controller,
|
|
4690
|
+
requestName: `GET /repos/${repoFullName}/pulls/${pullNumber}/comments`
|
|
4691
|
+
}
|
|
4692
|
+
);
|
|
4693
|
+
const issueComments = await paginateWithGitHubRateLimit(
|
|
4694
|
+
(page) => octokit.issues.listComments({
|
|
4572
4695
|
owner,
|
|
4573
4696
|
repo,
|
|
4574
4697
|
issue_number: pullNumber,
|
|
4575
|
-
per_page: 100
|
|
4698
|
+
per_page: 100,
|
|
4699
|
+
page
|
|
4576
4700
|
}),
|
|
4577
|
-
|
|
4578
|
-
|
|
4701
|
+
{
|
|
4702
|
+
controller,
|
|
4703
|
+
requestName: `GET /repos/${repoFullName}/issues/${pullNumber}/comments`
|
|
4704
|
+
}
|
|
4705
|
+
);
|
|
4706
|
+
const commits = await paginateWithGitHubRateLimit(
|
|
4707
|
+
(page) => octokit.pulls.listCommits({
|
|
4708
|
+
owner,
|
|
4709
|
+
repo,
|
|
4710
|
+
pull_number: pullNumber,
|
|
4711
|
+
per_page: 100,
|
|
4712
|
+
page
|
|
4713
|
+
}),
|
|
4714
|
+
{
|
|
4715
|
+
controller,
|
|
4716
|
+
requestName: `GET /repos/${repoFullName}/pulls/${pullNumber}/commits`
|
|
4717
|
+
}
|
|
4718
|
+
);
|
|
4579
4719
|
return {
|
|
4580
4720
|
repo: repoFullName,
|
|
4581
4721
|
number: pull.number,
|
|
@@ -4648,7 +4788,12 @@ async function fetchPullRequestDetailsConcurrently(options) {
|
|
|
4648
4788
|
prNumber: pullNumber,
|
|
4649
4789
|
detailConcurrency: options.detailConcurrency
|
|
4650
4790
|
});
|
|
4651
|
-
results[index] = await fetchPullRequestDetails(
|
|
4791
|
+
results[index] = await fetchPullRequestDetails(
|
|
4792
|
+
options.octokit,
|
|
4793
|
+
options.repo,
|
|
4794
|
+
pullNumber,
|
|
4795
|
+
options.controller
|
|
4796
|
+
);
|
|
4652
4797
|
completed += 1;
|
|
4653
4798
|
options.onProgress?.({
|
|
4654
4799
|
stage: "fetched_pull_request_details",
|
|
@@ -4674,10 +4819,18 @@ async function fetchMergedPullRequests(options) {
|
|
|
4674
4819
|
const octokit = createGitHubClient(options.token);
|
|
4675
4820
|
const limit = resolvePullRequestFetchLimit(options);
|
|
4676
4821
|
const detailConcurrency = resolvePullRequestDetailConcurrency(options);
|
|
4822
|
+
const rateLimitController = {
|
|
4823
|
+
onRateLimit: (progress) => options.onProgress?.({
|
|
4824
|
+
stage: "github_rate_limited",
|
|
4825
|
+
repo: options.repo,
|
|
4826
|
+
...progress
|
|
4827
|
+
})
|
|
4828
|
+
};
|
|
4677
4829
|
const sinceTime = options.since ? Date.parse(options.since) : void 0;
|
|
4678
4830
|
const pullNumbers = [];
|
|
4679
4831
|
let scannedPullRequests = 0;
|
|
4680
4832
|
let reachedSinceBoundary = false;
|
|
4833
|
+
let page = 1;
|
|
4681
4834
|
options.onProgress?.({
|
|
4682
4835
|
stage: "discovering_pull_requests",
|
|
4683
4836
|
repo: options.repo,
|
|
@@ -4685,14 +4838,22 @@ async function fetchMergedPullRequests(options) {
|
|
|
4685
4838
|
limit,
|
|
4686
4839
|
since: options.since
|
|
4687
4840
|
});
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4841
|
+
while (true) {
|
|
4842
|
+
const response = await requestWithGitHubRateLimit(
|
|
4843
|
+
() => octokit.pulls.list({
|
|
4844
|
+
owner,
|
|
4845
|
+
repo,
|
|
4846
|
+
state: "closed",
|
|
4847
|
+
sort: "updated",
|
|
4848
|
+
direction: "desc",
|
|
4849
|
+
per_page: 100,
|
|
4850
|
+
page
|
|
4851
|
+
}),
|
|
4852
|
+
{
|
|
4853
|
+
controller: rateLimitController,
|
|
4854
|
+
requestName: `GET /repos/${options.repo}/pulls page ${page}`
|
|
4855
|
+
}
|
|
4856
|
+
);
|
|
4696
4857
|
scannedPullRequests += response.data.length;
|
|
4697
4858
|
for (const pull of response.data) {
|
|
4698
4859
|
if (sinceTime && Date.parse(pull.updated_at) < sinceTime) {
|
|
@@ -4711,7 +4872,11 @@ async function fetchMergedPullRequests(options) {
|
|
|
4711
4872
|
scannedPullRequests,
|
|
4712
4873
|
matchedMergedPullRequests: pullNumbers.length
|
|
4713
4874
|
});
|
|
4714
|
-
|
|
4875
|
+
const hasNextPage2 = String(response.headers.link ?? "").includes('rel="next"');
|
|
4876
|
+
if (reachedSinceBoundary || limit !== void 0 && pullNumbers.length >= limit || !hasNextPage2) {
|
|
4877
|
+
break;
|
|
4878
|
+
}
|
|
4879
|
+
page += 1;
|
|
4715
4880
|
}
|
|
4716
4881
|
options.onProgress?.({
|
|
4717
4882
|
stage: "discovered_pull_requests",
|
|
@@ -4726,6 +4891,7 @@ async function fetchMergedPullRequests(options) {
|
|
|
4726
4891
|
repo: options.repo,
|
|
4727
4892
|
pullNumbers,
|
|
4728
4893
|
detailConcurrency,
|
|
4894
|
+
controller: rateLimitController,
|
|
4729
4895
|
onProgress: options.onProgress
|
|
4730
4896
|
});
|
|
4731
4897
|
}
|
|
@@ -4974,6 +5140,7 @@ export {
|
|
|
4974
5140
|
formatSearchHistory,
|
|
4975
5141
|
getAnchorIndexHealth,
|
|
4976
5142
|
getArchitectureContext,
|
|
5143
|
+
getGitHubRateLimitDelayMs,
|
|
4977
5144
|
getIndexStatus,
|
|
4978
5145
|
getLastSyncTime,
|
|
4979
5146
|
getSemanticStatus,
|
|
@@ -4986,6 +5153,7 @@ export {
|
|
|
4986
5153
|
indexPullRequests,
|
|
4987
5154
|
inferTestAwareness,
|
|
4988
5155
|
initializeSchema,
|
|
5156
|
+
isGitHubRateLimitError,
|
|
4989
5157
|
isHardExcludedCodePath,
|
|
4990
5158
|
isTestFilePath,
|
|
4991
5159
|
loadCurrentCodeSnapshot,
|
|
@@ -4993,6 +5161,7 @@ export {
|
|
|
4993
5161
|
mergeAnchorMcpConfig,
|
|
4994
5162
|
normalizePullRequest,
|
|
4995
5163
|
openAnchorDatabase,
|
|
5164
|
+
paginateWithGitHubRateLimit,
|
|
4996
5165
|
parseGitHubRemote,
|
|
4997
5166
|
rankArchitecturePatterns,
|
|
4998
5167
|
rankCodeChunks,
|
|
@@ -5004,6 +5173,7 @@ export {
|
|
|
5004
5173
|
redactSecrets,
|
|
5005
5174
|
redactedHistoricalText,
|
|
5006
5175
|
replaceCodeIndex,
|
|
5176
|
+
requestWithGitHubRateLimit,
|
|
5007
5177
|
resolveGitHubToken,
|
|
5008
5178
|
resolvePullRequestDetailConcurrency,
|
|
5009
5179
|
resolvePullRequestFetchLimit,
|