@oss-autopilot/core 0.44.2 → 0.44.15
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/cli-registry.js +61 -0
- package/dist/cli.bundle.cjs +101 -127
- package/dist/cli.bundle.cjs.map +4 -4
- package/dist/commands/daily.d.ts +6 -1
- package/dist/commands/daily.js +29 -64
- package/dist/commands/dashboard-data.d.ts +22 -1
- package/dist/commands/dashboard-data.js +85 -62
- package/dist/commands/dashboard-lifecycle.js +39 -2
- package/dist/commands/dashboard-scripts.d.ts +1 -1
- package/dist/commands/dashboard-scripts.js +2 -1
- package/dist/commands/dashboard-server.d.ts +2 -1
- package/dist/commands/dashboard-server.js +120 -81
- package/dist/commands/dashboard-templates.js +15 -69
- package/dist/commands/override.d.ts +21 -0
- package/dist/commands/override.js +35 -0
- package/dist/core/checklist-analysis.js +3 -1
- package/dist/core/daily-logic.d.ts +13 -10
- package/dist/core/daily-logic.js +79 -166
- package/dist/core/display-utils.d.ts +4 -0
- package/dist/core/display-utils.js +53 -54
- package/dist/core/errors.d.ts +8 -0
- package/dist/core/errors.js +26 -0
- package/dist/core/github-stats.d.ts +3 -3
- package/dist/core/github-stats.js +15 -7
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +2 -2
- package/dist/core/issue-conversation.js +2 -2
- package/dist/core/issue-discovery.d.ts +0 -5
- package/dist/core/issue-discovery.js +4 -11
- package/dist/core/issue-vetting.d.ts +0 -2
- package/dist/core/issue-vetting.js +31 -45
- package/dist/core/pr-monitor.d.ts +26 -3
- package/dist/core/pr-monitor.js +106 -93
- package/dist/core/state.d.ts +22 -1
- package/dist/core/state.js +50 -1
- package/dist/core/test-utils.js +6 -16
- package/dist/core/types.d.ts +51 -38
- package/dist/core/types.js +8 -0
- package/dist/core/utils.d.ts +2 -0
- package/dist/core/utils.js +5 -1
- package/dist/formatters/json.d.ts +1 -13
- package/dist/formatters/json.js +1 -13
- package/package.json +2 -2
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Extracted from PRMonitor to isolate statistics-gathering API calls (#263).
|
|
4
4
|
*/
|
|
5
5
|
import { extractOwnerRepo, parseGitHubUrl, isOwnRepo } from './utils.js';
|
|
6
|
+
import { isBelowMinStars } from './types.js';
|
|
6
7
|
import { debug, warn } from './logger.js';
|
|
7
8
|
import { getHttpCache } from './http-cache.js';
|
|
8
9
|
const MODULE = 'github-stats';
|
|
@@ -39,13 +40,14 @@ function isCachedPRCounts(v) {
|
|
|
39
40
|
* string (e.g. mergedAt or closedAt) used for monthly counts and daily activity.
|
|
40
41
|
* Return an empty string to skip histogram tracking for that item.
|
|
41
42
|
*/
|
|
42
|
-
async function fetchUserPRCounts(octokit, githubUsername, query, label, accumulateRepo) {
|
|
43
|
+
async function fetchUserPRCounts(octokit, githubUsername, query, label, accumulateRepo, starFilter) {
|
|
43
44
|
if (!githubUsername) {
|
|
44
45
|
return emptyPRCountsResult();
|
|
45
46
|
}
|
|
46
47
|
// Check for a fresh cached result (avoids 10-20 paginated API calls)
|
|
47
48
|
const cache = getHttpCache();
|
|
48
|
-
const
|
|
49
|
+
const minStarsSuffix = starFilter ? `:stars${starFilter.minStars}` : '';
|
|
50
|
+
const cacheKey = `pr-counts:v3:${label}:${githubUsername}${minStarsSuffix}`;
|
|
49
51
|
const cached = cache.getIfFresh(cacheKey, PR_COUNTS_CACHE_TTL_MS);
|
|
50
52
|
if (cached && isCachedPRCounts(cached)) {
|
|
51
53
|
debug(MODULE, `Using cached ${label} PR counts for @${githubUsername}`);
|
|
@@ -66,7 +68,7 @@ async function fetchUserPRCounts(octokit, githubUsername, query, label, accumula
|
|
|
66
68
|
let totalCount;
|
|
67
69
|
while (true) {
|
|
68
70
|
const { data } = await octokit.search.issuesAndPullRequests({
|
|
69
|
-
q: `is:pr ${query} author:${githubUsername}`,
|
|
71
|
+
q: `is:pr ${query} author:${githubUsername} -user:${githubUsername}`,
|
|
70
72
|
sort: 'updated',
|
|
71
73
|
order: 'desc',
|
|
72
74
|
per_page: 100,
|
|
@@ -86,6 +88,12 @@ async function fetchUserPRCounts(octokit, githubUsername, query, label, accumula
|
|
|
86
88
|
continue;
|
|
87
89
|
// Note: excludeRepos/excludeOrgs are intentionally NOT filtered here.
|
|
88
90
|
// Those filters control issue discovery/search, not historical statistics.
|
|
91
|
+
// Skip repos below the minimum star threshold (#576).
|
|
92
|
+
// Repos with unknown star counts (not yet fetched) are included — they'll be
|
|
93
|
+
// filtered on the next run once star data is cached in repoScores.
|
|
94
|
+
if (starFilter && isBelowMinStars(starFilter.knownStarCounts.get(repo), starFilter.minStars)) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
89
97
|
// Per-repo accumulation + get primary date for histograms
|
|
90
98
|
const primaryDate = accumulateRepo(repos, repo, item);
|
|
91
99
|
// Monthly histogram for primary date (merged/closed)
|
|
@@ -135,7 +143,7 @@ async function fetchUserPRCounts(octokit, githubUsername, query, label, accumula
|
|
|
135
143
|
* Fetch merged PR counts and latest merge dates per repository for the configured user.
|
|
136
144
|
* Also builds a monthly histogram of all merges for the contribution timeline.
|
|
137
145
|
*/
|
|
138
|
-
export function fetchUserMergedPRCounts(octokit, githubUsername) {
|
|
146
|
+
export function fetchUserMergedPRCounts(octokit, githubUsername, starFilter) {
|
|
139
147
|
return fetchUserPRCounts(octokit, githubUsername, 'is:merged', 'merged', (repos, repo, item) => {
|
|
140
148
|
if (!item.pull_request?.merged_at) {
|
|
141
149
|
warn(MODULE, `merged_at missing for merged PR ${item.html_url}${item.closed_at ? ', falling back to closed_at' : ', no date available'}`);
|
|
@@ -152,17 +160,17 @@ export function fetchUserMergedPRCounts(octokit, githubUsername) {
|
|
|
152
160
|
repos.set(repo, { count: 1, lastMergedAt: mergedAt });
|
|
153
161
|
}
|
|
154
162
|
return mergedAt;
|
|
155
|
-
});
|
|
163
|
+
}, starFilter);
|
|
156
164
|
}
|
|
157
165
|
/**
|
|
158
166
|
* Fetch closed-without-merge PR counts per repository for the configured user.
|
|
159
167
|
* Used to populate closedWithoutMergeCount in repo scores for accurate merge rate.
|
|
160
168
|
*/
|
|
161
|
-
export function fetchUserClosedPRCounts(octokit, githubUsername) {
|
|
169
|
+
export function fetchUserClosedPRCounts(octokit, githubUsername, starFilter) {
|
|
162
170
|
return fetchUserPRCounts(octokit, githubUsername, 'is:closed is:unmerged', 'closed', (repos, repo, item) => {
|
|
163
171
|
repos.set(repo, (repos.get(repo) || 0) + 1);
|
|
164
172
|
return item.closed_at || '';
|
|
165
|
-
});
|
|
173
|
+
}, starFilter);
|
|
166
174
|
}
|
|
167
175
|
/**
|
|
168
176
|
* Shared helper: search for recent PRs and filter out own repos, excluded repos/orgs.
|
package/dist/core/index.d.ts
CHANGED
|
@@ -8,8 +8,8 @@ export { IssueDiscovery, type IssueCandidate, type SearchPriority, isDocOnlyIssu
|
|
|
8
8
|
export { IssueConversationMonitor } from './issue-conversation.js';
|
|
9
9
|
export { isBotAuthor, isAcknowledgmentComment } from './comment-utils.js';
|
|
10
10
|
export { getOctokit, checkRateLimit, type RateLimitInfo } from './github.js';
|
|
11
|
-
export { parseGitHubUrl, daysBetween, splitRepo, isOwnRepo, getCLIVersion, getDataDir, getStatePath, getBackupDir, getCacheDir, getDashboardPath, formatRelativeTime, byDateDescending, getGitHubToken, getGitHubTokenAsync, requireGitHubToken, resetGitHubTokenCache, } from './utils.js';
|
|
12
|
-
export { OssAutopilotError, ConfigurationError, ValidationError, errorMessage, getHttpStatusCode } from './errors.js';
|
|
11
|
+
export { parseGitHubUrl, daysBetween, splitRepo, isOwnRepo, getCLIVersion, getDataDir, getStatePath, getBackupDir, getCacheDir, getDashboardPath, formatRelativeTime, byDateDescending, getGitHubToken, getGitHubTokenAsync, requireGitHubToken, resetGitHubTokenCache, DEFAULT_CONCURRENCY, } from './utils.js';
|
|
12
|
+
export { OssAutopilotError, ConfigurationError, ValidationError, errorMessage, getHttpStatusCode, isRateLimitError, isRateLimitOrAuthError, } from './errors.js';
|
|
13
13
|
export { enableDebug, isDebugEnabled, debug, info, warn, timed } from './logger.js';
|
|
14
14
|
export { HttpCache, getHttpCache, cachedRequest, type CacheEntry } from './http-cache.js';
|
|
15
15
|
export { CRITICAL_STATUSES, computeRepoSignals, groupPRsByRepo, assessCapacity, collectActionableIssues, computeActionMenu, toShelvedPRRef, formatActionHint, formatBriefSummary, formatSummary, printDigest, } from './daily-logic.js';
|
package/dist/core/index.js
CHANGED
|
@@ -8,8 +8,8 @@ export { IssueDiscovery, isDocOnlyIssue, applyPerRepoCap, DOC_ONLY_LABELS, } fro
|
|
|
8
8
|
export { IssueConversationMonitor } from './issue-conversation.js';
|
|
9
9
|
export { isBotAuthor, isAcknowledgmentComment } from './comment-utils.js';
|
|
10
10
|
export { getOctokit, checkRateLimit } from './github.js';
|
|
11
|
-
export { parseGitHubUrl, daysBetween, splitRepo, isOwnRepo, getCLIVersion, getDataDir, getStatePath, getBackupDir, getCacheDir, getDashboardPath, formatRelativeTime, byDateDescending, getGitHubToken, getGitHubTokenAsync, requireGitHubToken, resetGitHubTokenCache, } from './utils.js';
|
|
12
|
-
export { OssAutopilotError, ConfigurationError, ValidationError, errorMessage, getHttpStatusCode } from './errors.js';
|
|
11
|
+
export { parseGitHubUrl, daysBetween, splitRepo, isOwnRepo, getCLIVersion, getDataDir, getStatePath, getBackupDir, getCacheDir, getDashboardPath, formatRelativeTime, byDateDescending, getGitHubToken, getGitHubTokenAsync, requireGitHubToken, resetGitHubTokenCache, DEFAULT_CONCURRENCY, } from './utils.js';
|
|
12
|
+
export { OssAutopilotError, ConfigurationError, ValidationError, errorMessage, getHttpStatusCode, isRateLimitError, isRateLimitOrAuthError, } from './errors.js';
|
|
13
13
|
export { enableDebug, isDebugEnabled, debug, info, warn, timed } from './logger.js';
|
|
14
14
|
export { HttpCache, getHttpCache, cachedRequest } from './http-cache.js';
|
|
15
15
|
export { CRITICAL_STATUSES, computeRepoSignals, groupPRsByRepo, assessCapacity, collectActionableIssues, computeActionMenu, toShelvedPRRef, formatActionHint, formatBriefSummary, formatSummary, printDigest, } from './daily-logic.js';
|
|
@@ -9,12 +9,12 @@ import { getOctokit } from './github.js';
|
|
|
9
9
|
import { isBotAuthor, isAcknowledgmentComment } from './comment-utils.js';
|
|
10
10
|
import { paginateAll } from './pagination.js';
|
|
11
11
|
import { getStateManager } from './state.js';
|
|
12
|
-
import { daysBetween, splitRepo, extractOwnerRepo, isOwnRepo } from './utils.js';
|
|
12
|
+
import { daysBetween, splitRepo, extractOwnerRepo, isOwnRepo, DEFAULT_CONCURRENCY } from './utils.js';
|
|
13
13
|
import { runWorkerPool } from './concurrency.js';
|
|
14
14
|
import { ConfigurationError, errorMessage } from './errors.js';
|
|
15
15
|
import { debug, warn } from './logger.js';
|
|
16
16
|
const MODULE = 'issue-conversation';
|
|
17
|
-
const MAX_CONCURRENT_REQUESTS =
|
|
17
|
+
const MAX_CONCURRENT_REQUESTS = DEFAULT_CONCURRENCY;
|
|
18
18
|
/** Associations that indicate someone with repo-level permissions. */
|
|
19
19
|
const MAINTAINER_ASSOCIATIONS = new Set(['OWNER', 'MEMBER', 'COLLABORATOR']);
|
|
20
20
|
export class IssueConversationMonitor {
|
|
@@ -65,11 +65,6 @@ export declare class IssueDiscovery {
|
|
|
65
65
|
* Split repos into batches of the specified size.
|
|
66
66
|
*/
|
|
67
67
|
private batchRepos;
|
|
68
|
-
/**
|
|
69
|
-
* Check if an error is a GitHub rate limit error (429 or rate-limit 403).
|
|
70
|
-
* Static proxy kept for backward compatibility with tests.
|
|
71
|
-
*/
|
|
72
|
-
static isRateLimitError(error: unknown): boolean;
|
|
73
68
|
/**
|
|
74
69
|
* Vet a specific issue (delegates to IssueVetter).
|
|
75
70
|
*/
|
|
@@ -12,7 +12,7 @@ import { getOctokit, checkRateLimit } from './github.js';
|
|
|
12
12
|
import { getStateManager } from './state.js';
|
|
13
13
|
import { daysBetween, getDataDir } from './utils.js';
|
|
14
14
|
import { DEFAULT_CONFIG } from './types.js';
|
|
15
|
-
import { ValidationError, errorMessage, getHttpStatusCode } from './errors.js';
|
|
15
|
+
import { ValidationError, errorMessage, getHttpStatusCode, isRateLimitError } from './errors.js';
|
|
16
16
|
import { debug, info, warn } from './logger.js';
|
|
17
17
|
import { getHttpCache, cachedTimeBased } from './http-cache.js';
|
|
18
18
|
import { isDocOnlyIssue, detectLabelFarmingRepos, applyPerRepoCap } from './issue-filtering.js';
|
|
@@ -328,7 +328,7 @@ export class IssueDiscovery {
|
|
|
328
328
|
catch (error) {
|
|
329
329
|
const errMsg = errorMessage(error);
|
|
330
330
|
phase2Error = errMsg;
|
|
331
|
-
if (
|
|
331
|
+
if (isRateLimitError(error)) {
|
|
332
332
|
rateLimitHitDuringSearch = true;
|
|
333
333
|
}
|
|
334
334
|
warn(MODULE, `Error in general issue search: ${errMsg}`);
|
|
@@ -370,7 +370,7 @@ export class IssueDiscovery {
|
|
|
370
370
|
catch (error) {
|
|
371
371
|
const errMsg = errorMessage(error);
|
|
372
372
|
phase3Error = errMsg;
|
|
373
|
-
if (
|
|
373
|
+
if (isRateLimitError(error)) {
|
|
374
374
|
rateLimitHitDuringSearch = true;
|
|
375
375
|
}
|
|
376
376
|
warn(MODULE, `Error in maintained-repo search: ${errMsg}`);
|
|
@@ -464,7 +464,7 @@ export class IssueDiscovery {
|
|
|
464
464
|
}
|
|
465
465
|
catch (error) {
|
|
466
466
|
failedBatches++;
|
|
467
|
-
if (
|
|
467
|
+
if (isRateLimitError(error)) {
|
|
468
468
|
rateLimitFailures++;
|
|
469
469
|
}
|
|
470
470
|
const batchRepos = batch.join(', ');
|
|
@@ -489,13 +489,6 @@ export class IssueDiscovery {
|
|
|
489
489
|
}
|
|
490
490
|
return batches;
|
|
491
491
|
}
|
|
492
|
-
/**
|
|
493
|
-
* Check if an error is a GitHub rate limit error (429 or rate-limit 403).
|
|
494
|
-
* Static proxy kept for backward compatibility with tests.
|
|
495
|
-
*/
|
|
496
|
-
static isRateLimitError(error) {
|
|
497
|
-
return IssueVetter.isRateLimitError(error);
|
|
498
|
-
}
|
|
499
492
|
/**
|
|
500
493
|
* Vet a specific issue (delegates to IssueVetter).
|
|
501
494
|
*/
|
|
@@ -29,8 +29,6 @@ export declare class IssueVetter {
|
|
|
29
29
|
allFailed: boolean;
|
|
30
30
|
rateLimitHit: boolean;
|
|
31
31
|
}>;
|
|
32
|
-
/** Check if an error is a GitHub rate limit error (429 or rate-limit 403). */
|
|
33
|
-
static isRateLimitError(error: unknown): boolean;
|
|
34
32
|
checkNoExistingPR(owner: string, repo: string, issueNumber: number): Promise<CheckResult>;
|
|
35
33
|
/**
|
|
36
34
|
* Check how many merged PRs the authenticated user has in a repo.
|
|
@@ -5,14 +5,13 @@
|
|
|
5
5
|
* Extracted from issue-discovery.ts (#356) to isolate vetting logic.
|
|
6
6
|
*/
|
|
7
7
|
import { paginateAll } from './pagination.js';
|
|
8
|
-
import { parseGitHubUrl, daysBetween } from './utils.js';
|
|
9
|
-
import { ValidationError, errorMessage,
|
|
8
|
+
import { parseGitHubUrl, daysBetween, DEFAULT_CONCURRENCY } from './utils.js';
|
|
9
|
+
import { ValidationError, errorMessage, isRateLimitError } from './errors.js';
|
|
10
10
|
import { warn } from './logger.js';
|
|
11
11
|
import { getHttpCache, cachedRequest, cachedTimeBased } from './http-cache.js';
|
|
12
12
|
import { calculateRepoQualityBonus, calculateViabilityScore } from './issue-scoring.js';
|
|
13
13
|
const MODULE = 'issue-vetting';
|
|
14
|
-
|
|
15
|
-
const MAX_CONCURRENT_REQUESTS = 5;
|
|
14
|
+
const MAX_CONCURRENT_REQUESTS = DEFAULT_CONCURRENCY;
|
|
16
15
|
// Cache for contribution guidelines (expires after 1 hour, max 100 entries)
|
|
17
16
|
const guidelinesCache = new Map();
|
|
18
17
|
const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
|
|
@@ -233,7 +232,7 @@ export class IssueVetter {
|
|
|
233
232
|
*/
|
|
234
233
|
async vetIssuesParallel(urls, maxResults, priority) {
|
|
235
234
|
const candidates = [];
|
|
236
|
-
const pending =
|
|
235
|
+
const pending = new Map();
|
|
237
236
|
let failedVettingCount = 0;
|
|
238
237
|
let rateLimitFailures = 0;
|
|
239
238
|
let attemptedCount = 0;
|
|
@@ -253,21 +252,20 @@ export class IssueVetter {
|
|
|
253
252
|
})
|
|
254
253
|
.catch((error) => {
|
|
255
254
|
failedVettingCount++;
|
|
256
|
-
if (
|
|
255
|
+
if (isRateLimitError(error)) {
|
|
257
256
|
rateLimitFailures++;
|
|
258
257
|
}
|
|
259
258
|
warn(MODULE, `Error vetting issue ${url}:`, errorMessage(error));
|
|
260
|
-
})
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
pending.splice(completed, 1);
|
|
259
|
+
})
|
|
260
|
+
.finally(() => pending.delete(url));
|
|
261
|
+
pending.set(url, task);
|
|
262
|
+
// Limit concurrency — wait for at least one to complete before launching more
|
|
263
|
+
if (pending.size >= MAX_CONCURRENT_REQUESTS) {
|
|
264
|
+
await Promise.race(pending.values());
|
|
267
265
|
}
|
|
268
266
|
}
|
|
269
267
|
// Wait for remaining
|
|
270
|
-
await Promise.allSettled(pending);
|
|
268
|
+
await Promise.allSettled(pending.values());
|
|
271
269
|
const allFailed = failedVettingCount === attemptedCount && attemptedCount > 0;
|
|
272
270
|
if (allFailed) {
|
|
273
271
|
warn(MODULE, `All ${attemptedCount} issue(s) failed vetting. ` +
|
|
@@ -275,17 +273,6 @@ export class IssueVetter {
|
|
|
275
273
|
}
|
|
276
274
|
return { candidates: candidates.slice(0, maxResults), allFailed, rateLimitHit: rateLimitFailures > 0 };
|
|
277
275
|
}
|
|
278
|
-
/** Check if an error is a GitHub rate limit error (429 or rate-limit 403). */
|
|
279
|
-
static isRateLimitError(error) {
|
|
280
|
-
const status = getHttpStatusCode(error);
|
|
281
|
-
if (status === 429)
|
|
282
|
-
return true;
|
|
283
|
-
if (status === 403) {
|
|
284
|
-
const msg = errorMessage(error).toLowerCase();
|
|
285
|
-
return msg.includes('rate limit');
|
|
286
|
-
}
|
|
287
|
-
return false;
|
|
288
|
-
}
|
|
289
276
|
async checkNoExistingPR(owner, repo, issueNumber) {
|
|
290
277
|
try {
|
|
291
278
|
// Search for PRs that mention this issue
|
|
@@ -447,26 +434,25 @@ export class IssueVetter {
|
|
|
447
434
|
return cached.guidelines;
|
|
448
435
|
}
|
|
449
436
|
const filesToCheck = ['CONTRIBUTING.md', '.github/CONTRIBUTING.md', 'docs/CONTRIBUTING.md', 'contributing.md'];
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
}
|
|
437
|
+
// Probe all paths in parallel — take the first success in priority order
|
|
438
|
+
const results = await Promise.allSettled(filesToCheck.map((file) => this.octokit.repos.getContent({ owner, repo, path: file }).then(({ data }) => {
|
|
439
|
+
if ('content' in data) {
|
|
440
|
+
return Buffer.from(data.content, 'base64').toString('utf-8');
|
|
441
|
+
}
|
|
442
|
+
return null;
|
|
443
|
+
})));
|
|
444
|
+
for (let i = 0; i < results.length; i++) {
|
|
445
|
+
const result = results[i];
|
|
446
|
+
if (result.status === 'fulfilled' && result.value) {
|
|
447
|
+
const guidelines = this.parseContributionGuidelines(result.value);
|
|
448
|
+
guidelinesCache.set(cacheKey, { guidelines, fetchedAt: Date.now() });
|
|
449
|
+
pruneCache();
|
|
450
|
+
return guidelines;
|
|
465
451
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
if (
|
|
469
|
-
warn(MODULE, `Unexpected error fetching ${
|
|
452
|
+
if (result.status === 'rejected') {
|
|
453
|
+
const msg = result.reason instanceof Error ? result.reason.message : String(result.reason);
|
|
454
|
+
if (!msg.includes('404') && !msg.includes('Not Found')) {
|
|
455
|
+
warn(MODULE, `Unexpected error fetching ${filesToCheck[i]} from ${owner}/${repo}: ${msg}`);
|
|
470
456
|
}
|
|
471
457
|
}
|
|
472
458
|
}
|
|
@@ -523,7 +509,7 @@ export class IssueVetter {
|
|
|
523
509
|
if (!body || body.length < 50)
|
|
524
510
|
return false;
|
|
525
511
|
// Check for clear structure
|
|
526
|
-
const hasSteps = /\d
|
|
512
|
+
const hasSteps = /\d\.|[-*]\s/.test(body);
|
|
527
513
|
const hasCodeBlock = /```/.test(body);
|
|
528
514
|
const hasExpectedBehavior = /expect|should|must|want/i.test(body);
|
|
529
515
|
// Must have at least two indicators of clarity
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* - display-utils.ts: Display label computation
|
|
12
12
|
* - github-stats.ts: Merged/closed PR counts and star fetching
|
|
13
13
|
*/
|
|
14
|
-
import { FetchedPR, DailyDigest, ClosedPR, MergedPR } from './types.js';
|
|
14
|
+
import { FetchedPR, DailyDigest, ClosedPR, MergedPR, StarFilter } from './types.js';
|
|
15
15
|
import { type PRCountsResult } from './github-stats.js';
|
|
16
16
|
export { computeDisplayLabel } from './display-utils.js';
|
|
17
17
|
export { classifyCICheck, classifyFailingChecks } from './ci-analysis.js';
|
|
@@ -46,6 +46,29 @@ export declare class PRMonitor {
|
|
|
46
46
|
* Determine the overall status of a PR
|
|
47
47
|
*/
|
|
48
48
|
private determineStatus;
|
|
49
|
+
/**
|
|
50
|
+
* CI-fix bots that push commits as a direct result of the contributor's push (#568).
|
|
51
|
+
* Their commits represent contributor work and should count as addressing feedback.
|
|
52
|
+
* This is intentionally an allowlist — not all `[bot]` accounts are CI-fix bots
|
|
53
|
+
* (e.g. dependabot[bot] and renovate[bot] open their own PRs).
|
|
54
|
+
* Values must be lowercase — lookup uses .toLowerCase() for case-insensitive matching.
|
|
55
|
+
*/
|
|
56
|
+
private static readonly CI_FIX_BOTS;
|
|
57
|
+
/**
|
|
58
|
+
* Check whether the HEAD commit was authored by the contributor (#547).
|
|
59
|
+
* Returns true when the author matches, when the author is a known CI-fix
|
|
60
|
+
* bot (#568), or when author info is unavailable (graceful degradation).
|
|
61
|
+
*/
|
|
62
|
+
private isContributorCommit;
|
|
63
|
+
/** Minimum gap (ms) between maintainer comment and contributor commit for
|
|
64
|
+
* the commit to count as "addressing" the feedback (#547). Prevents false
|
|
65
|
+
* positives from race conditions, clock skew, and in-flight pushes. */
|
|
66
|
+
private static readonly MIN_RESPONSE_GAP_MS;
|
|
67
|
+
/**
|
|
68
|
+
* Check whether the contributor's commit is meaningfully after the maintainer's
|
|
69
|
+
* comment — i.e. the commit timestamp is at least MIN_RESPONSE_GAP_MS later (#547).
|
|
70
|
+
*/
|
|
71
|
+
private isCommitAfterComment;
|
|
49
72
|
/**
|
|
50
73
|
* Check if PR has merge conflict
|
|
51
74
|
*/
|
|
@@ -60,7 +83,7 @@ export declare class PRMonitor {
|
|
|
60
83
|
* Fetch merged PR counts and latest merge dates per repository for the configured user.
|
|
61
84
|
* Delegates to github-stats module.
|
|
62
85
|
*/
|
|
63
|
-
fetchUserMergedPRCounts(): Promise<PRCountsResult<{
|
|
86
|
+
fetchUserMergedPRCounts(starFilter?: StarFilter): Promise<PRCountsResult<{
|
|
64
87
|
count: number;
|
|
65
88
|
lastMergedAt: string;
|
|
66
89
|
}>>;
|
|
@@ -68,7 +91,7 @@ export declare class PRMonitor {
|
|
|
68
91
|
* Fetch closed-without-merge PR counts per repository for the configured user.
|
|
69
92
|
* Delegates to github-stats module.
|
|
70
93
|
*/
|
|
71
|
-
fetchUserClosedPRCounts(): Promise<PRCountsResult<number>>;
|
|
94
|
+
fetchUserClosedPRCounts(starFilter?: StarFilter): Promise<PRCountsResult<number>>;
|
|
72
95
|
/**
|
|
73
96
|
* Fetch GitHub star counts for a list of repositories.
|
|
74
97
|
* Delegates to github-stats module.
|