@oss-autopilot/core 0.41.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/LICENSE +21 -0
- package/README.md +85 -0
- package/dist/cli.bundle.cjs +17657 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.js +325 -0
- package/dist/commands/check-integration.d.ts +10 -0
- package/dist/commands/check-integration.js +192 -0
- package/dist/commands/comments.d.ts +24 -0
- package/dist/commands/comments.js +311 -0
- package/dist/commands/config.d.ts +11 -0
- package/dist/commands/config.js +82 -0
- package/dist/commands/daily.d.ts +29 -0
- package/dist/commands/daily.js +433 -0
- package/dist/commands/dashboard-data.d.ts +45 -0
- package/dist/commands/dashboard-data.js +132 -0
- package/dist/commands/dashboard-templates.d.ts +23 -0
- package/dist/commands/dashboard-templates.js +1627 -0
- package/dist/commands/dashboard.d.ts +18 -0
- package/dist/commands/dashboard.js +134 -0
- package/dist/commands/dismiss.d.ts +13 -0
- package/dist/commands/dismiss.js +49 -0
- package/dist/commands/init.d.ts +10 -0
- package/dist/commands/init.js +27 -0
- package/dist/commands/local-repos.d.ts +14 -0
- package/dist/commands/local-repos.js +155 -0
- package/dist/commands/parse-list.d.ts +13 -0
- package/dist/commands/parse-list.js +139 -0
- package/dist/commands/read.d.ts +12 -0
- package/dist/commands/read.js +33 -0
- package/dist/commands/search.d.ts +10 -0
- package/dist/commands/search.js +74 -0
- package/dist/commands/setup.d.ts +15 -0
- package/dist/commands/setup.js +276 -0
- package/dist/commands/shelve.d.ts +13 -0
- package/dist/commands/shelve.js +49 -0
- package/dist/commands/snooze.d.ts +18 -0
- package/dist/commands/snooze.js +83 -0
- package/dist/commands/startup.d.ts +33 -0
- package/dist/commands/startup.js +197 -0
- package/dist/commands/status.d.ts +10 -0
- package/dist/commands/status.js +43 -0
- package/dist/commands/track.d.ts +16 -0
- package/dist/commands/track.js +59 -0
- package/dist/commands/validation.d.ts +43 -0
- package/dist/commands/validation.js +112 -0
- package/dist/commands/vet.d.ts +10 -0
- package/dist/commands/vet.js +36 -0
- package/dist/core/checklist-analysis.d.ts +17 -0
- package/dist/core/checklist-analysis.js +39 -0
- package/dist/core/ci-analysis.d.ts +78 -0
- package/dist/core/ci-analysis.js +163 -0
- package/dist/core/comment-utils.d.ts +15 -0
- package/dist/core/comment-utils.js +52 -0
- package/dist/core/concurrency.d.ts +5 -0
- package/dist/core/concurrency.js +15 -0
- package/dist/core/daily-logic.d.ts +77 -0
- package/dist/core/daily-logic.js +512 -0
- package/dist/core/display-utils.d.ts +10 -0
- package/dist/core/display-utils.js +100 -0
- package/dist/core/errors.d.ts +24 -0
- package/dist/core/errors.js +34 -0
- package/dist/core/github-stats.d.ts +73 -0
- package/dist/core/github-stats.js +272 -0
- package/dist/core/github.d.ts +19 -0
- package/dist/core/github.js +60 -0
- package/dist/core/http-cache.d.ts +97 -0
- package/dist/core/http-cache.js +269 -0
- package/dist/core/index.d.ts +15 -0
- package/dist/core/index.js +15 -0
- package/dist/core/issue-conversation.d.ts +29 -0
- package/dist/core/issue-conversation.js +231 -0
- package/dist/core/issue-discovery.d.ts +85 -0
- package/dist/core/issue-discovery.js +589 -0
- package/dist/core/issue-filtering.d.ts +51 -0
- package/dist/core/issue-filtering.js +103 -0
- package/dist/core/issue-scoring.d.ts +40 -0
- package/dist/core/issue-scoring.js +92 -0
- package/dist/core/issue-vetting.d.ts +49 -0
- package/dist/core/issue-vetting.js +536 -0
- package/dist/core/logger.d.ts +21 -0
- package/dist/core/logger.js +49 -0
- package/dist/core/maintainer-analysis.d.ts +10 -0
- package/dist/core/maintainer-analysis.js +59 -0
- package/dist/core/pagination.d.ts +11 -0
- package/dist/core/pagination.js +20 -0
- package/dist/core/pr-monitor.d.ts +109 -0
- package/dist/core/pr-monitor.js +594 -0
- package/dist/core/review-analysis.d.ts +72 -0
- package/dist/core/review-analysis.js +163 -0
- package/dist/core/state.d.ts +371 -0
- package/dist/core/state.js +1089 -0
- package/dist/core/types.d.ts +507 -0
- package/dist/core/types.js +34 -0
- package/dist/core/utils.d.ts +249 -0
- package/dist/core/utils.js +422 -0
- package/dist/formatters/json.d.ts +269 -0
- package/dist/formatters/json.js +88 -0
- package/package.json +67 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Issue Filtering — pure functions for filtering and spam detection on search results.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from issue-discovery.ts (#356) to isolate filtering logic:
|
|
5
|
+
* label farming detection, doc-only filtering, per-repo caps, templated title detection.
|
|
6
|
+
*/
|
|
7
|
+
/** Labels that indicate documentation-only issues (#105). */
|
|
8
|
+
export const DOC_ONLY_LABELS = new Set(['documentation', 'docs', 'typo', 'spelling']);
|
|
9
|
+
/**
|
|
10
|
+
* Check if an issue's labels are ALL documentation-related (#105).
|
|
11
|
+
* Issues with mixed labels (e.g., "good first issue" + "documentation") pass through.
|
|
12
|
+
* Issues with no labels are not considered doc-only.
|
|
13
|
+
*/
|
|
14
|
+
export function isDocOnlyIssue(item) {
|
|
15
|
+
if (!item.labels || !Array.isArray(item.labels) || item.labels.length === 0)
|
|
16
|
+
return false;
|
|
17
|
+
const labelNames = item.labels.map((l) => (typeof l === 'string' ? l : l.name || '').toLowerCase());
|
|
18
|
+
// Filter out empty label names before checking
|
|
19
|
+
const nonEmptyLabels = labelNames.filter((n) => n.length > 0);
|
|
20
|
+
if (nonEmptyLabels.length === 0)
|
|
21
|
+
return false;
|
|
22
|
+
return nonEmptyLabels.every((n) => DOC_ONLY_LABELS.has(n));
|
|
23
|
+
}
|
|
24
|
+
/** Known beginner-type label names used to detect label-farming repos (#97). */
|
|
25
|
+
export const BEGINNER_LABELS = new Set([
|
|
26
|
+
'good first issue',
|
|
27
|
+
'hacktoberfest',
|
|
28
|
+
'easy',
|
|
29
|
+
'up-for-grabs',
|
|
30
|
+
'first-timers-only',
|
|
31
|
+
'beginner-friendly',
|
|
32
|
+
'beginner',
|
|
33
|
+
'starter',
|
|
34
|
+
'newbie',
|
|
35
|
+
'low-hanging-fruit',
|
|
36
|
+
'community',
|
|
37
|
+
]);
|
|
38
|
+
/** Check if a single issue has an excessive number of beginner labels (>= 5). */
|
|
39
|
+
export function isLabelFarming(item) {
|
|
40
|
+
if (!item.labels || !Array.isArray(item.labels))
|
|
41
|
+
return false;
|
|
42
|
+
const labelNames = item.labels.map((l) => (typeof l === 'string' ? l : l.name || '').toLowerCase());
|
|
43
|
+
const beginnerCount = labelNames.filter((n) => BEGINNER_LABELS.has(n)).length;
|
|
44
|
+
return beginnerCount >= 5;
|
|
45
|
+
}
|
|
46
|
+
/** Detect mass-created issue titles like "Add Trivia Question 61" or "Create Entry #5". */
|
|
47
|
+
export function hasTemplatedTitle(title) {
|
|
48
|
+
if (!title)
|
|
49
|
+
return false;
|
|
50
|
+
// Matches "<anything> <category-noun> <number>" where category nouns are typical
|
|
51
|
+
// of mass-created templated issues. This avoids false positives on legitimate titles
|
|
52
|
+
// like "Add support for Python 3" or "Implement RFC 7231" which lack category nouns.
|
|
53
|
+
return /^.+\s+(question|fact|point|item|task|entry|post|challenge|exercise|example|problem|tip|recipe|snippet)\s+#?\d+$/i.test(title);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Batch-analyze search items to detect label-farming repositories (#97).
|
|
57
|
+
* Returns a Set of repo full names (owner/repo) that appear to be spam.
|
|
58
|
+
*
|
|
59
|
+
* A repo is flagged if:
|
|
60
|
+
* - ANY single issue has >= 5 beginner labels (strong individual signal), OR
|
|
61
|
+
* - It has >= 3 issues with templated titles (batch signal)
|
|
62
|
+
*/
|
|
63
|
+
export function detectLabelFarmingRepos(items) {
|
|
64
|
+
const spamRepos = new Set();
|
|
65
|
+
const repoSpamCounts = new Map();
|
|
66
|
+
for (const item of items) {
|
|
67
|
+
const repoFullName = item.repository_url.split('/').slice(-2).join('/');
|
|
68
|
+
// Strong signal: single issue with 5+ beginner labels
|
|
69
|
+
if (isLabelFarming(item)) {
|
|
70
|
+
spamRepos.add(repoFullName);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
// Weaker signal: templated title
|
|
74
|
+
if (item.title && hasTemplatedTitle(item.title)) {
|
|
75
|
+
repoSpamCounts.set(repoFullName, (repoSpamCounts.get(repoFullName) || 0) + 1);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Flag repos with 3+ templated-title issues
|
|
79
|
+
for (const [repo, count] of repoSpamCounts) {
|
|
80
|
+
if (count >= 3) {
|
|
81
|
+
spamRepos.add(repo);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return spamRepos;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Apply per-repo cap to candidates (#105).
|
|
88
|
+
* Keeps at most `maxPerRepo` issues from any single repo.
|
|
89
|
+
* Maintains the existing sort order — first N from each repo are kept,
|
|
90
|
+
* excess issues from over-represented repos are dropped.
|
|
91
|
+
*/
|
|
92
|
+
export function applyPerRepoCap(candidates, maxPerRepo) {
|
|
93
|
+
const repoCounts = new Map();
|
|
94
|
+
const kept = [];
|
|
95
|
+
for (const c of candidates) {
|
|
96
|
+
const count = repoCounts.get(c.issue.repo) || 0;
|
|
97
|
+
if (count < maxPerRepo) {
|
|
98
|
+
kept.push(c);
|
|
99
|
+
repoCounts.set(c.issue.repo, count + 1);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return kept;
|
|
103
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Issue Scoring — pure functions for computing viability scores and quality bonuses.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from issue-discovery.ts (#356) to isolate scoring logic.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Calculate a quality bonus based on repo star and fork counts (#98).
|
|
8
|
+
* Stars: <50 → 0, 50-499 → +3, 500-4999 → +5, 5000+ → +8
|
|
9
|
+
* Forks: 50+ → +2, 500+ → +4
|
|
10
|
+
* Natural max is 12 (8 stars + 4 forks).
|
|
11
|
+
*/
|
|
12
|
+
export declare function calculateRepoQualityBonus(stargazersCount: number, forksCount: number): number;
|
|
13
|
+
export interface ViabilityScoreParams {
|
|
14
|
+
repoScore: number | null;
|
|
15
|
+
hasExistingPR: boolean;
|
|
16
|
+
isClaimed: boolean;
|
|
17
|
+
clearRequirements: boolean;
|
|
18
|
+
hasContributionGuidelines: boolean;
|
|
19
|
+
issueUpdatedAt: string;
|
|
20
|
+
closedWithoutMergeCount: number;
|
|
21
|
+
mergedPRCount: number;
|
|
22
|
+
orgHasMergedPRs: boolean;
|
|
23
|
+
repoQualityBonus?: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Calculate viability score for an issue (0-100 scale)
|
|
27
|
+
* Scoring:
|
|
28
|
+
* - Base: 50 points
|
|
29
|
+
* - +repoScore*2 (up to +20 for score of 10)
|
|
30
|
+
* - +repoQualityBonus (up to +12 for established repos, from star/fork counts) (#98)
|
|
31
|
+
* - +15 for merged PR in this repo (direct proven relationship) (#99)
|
|
32
|
+
* - +15 for clear requirements (clarity)
|
|
33
|
+
* - +15 for freshness (recently updated)
|
|
34
|
+
* - +10 for contribution guidelines
|
|
35
|
+
* - +5 for org affinity (merged PRs in same org)
|
|
36
|
+
* - -30 if existing PR
|
|
37
|
+
* - -20 if claimed
|
|
38
|
+
* - -15 if closed-without-merge history with no merges
|
|
39
|
+
*/
|
|
40
|
+
export declare function calculateViabilityScore(params: ViabilityScoreParams): number;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Issue Scoring — pure functions for computing viability scores and quality bonuses.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from issue-discovery.ts (#356) to isolate scoring logic.
|
|
5
|
+
*/
|
|
6
|
+
import { daysBetween } from './utils.js';
|
|
7
|
+
/**
|
|
8
|
+
* Calculate a quality bonus based on repo star and fork counts (#98).
|
|
9
|
+
* Stars: <50 → 0, 50-499 → +3, 500-4999 → +5, 5000+ → +8
|
|
10
|
+
* Forks: 50+ → +2, 500+ → +4
|
|
11
|
+
* Natural max is 12 (8 stars + 4 forks).
|
|
12
|
+
*/
|
|
13
|
+
export function calculateRepoQualityBonus(stargazersCount, forksCount) {
|
|
14
|
+
let bonus = 0;
|
|
15
|
+
// Star tiers
|
|
16
|
+
if (stargazersCount >= 5000)
|
|
17
|
+
bonus += 8;
|
|
18
|
+
else if (stargazersCount >= 500)
|
|
19
|
+
bonus += 5;
|
|
20
|
+
else if (stargazersCount >= 50)
|
|
21
|
+
bonus += 3;
|
|
22
|
+
// Fork tiers
|
|
23
|
+
if (forksCount >= 500)
|
|
24
|
+
bonus += 4;
|
|
25
|
+
else if (forksCount >= 50)
|
|
26
|
+
bonus += 2;
|
|
27
|
+
return bonus;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Calculate viability score for an issue (0-100 scale)
|
|
31
|
+
* Scoring:
|
|
32
|
+
* - Base: 50 points
|
|
33
|
+
* - +repoScore*2 (up to +20 for score of 10)
|
|
34
|
+
* - +repoQualityBonus (up to +12 for established repos, from star/fork counts) (#98)
|
|
35
|
+
* - +15 for merged PR in this repo (direct proven relationship) (#99)
|
|
36
|
+
* - +15 for clear requirements (clarity)
|
|
37
|
+
* - +15 for freshness (recently updated)
|
|
38
|
+
* - +10 for contribution guidelines
|
|
39
|
+
* - +5 for org affinity (merged PRs in same org)
|
|
40
|
+
* - -30 if existing PR
|
|
41
|
+
* - -20 if claimed
|
|
42
|
+
* - -15 if closed-without-merge history with no merges
|
|
43
|
+
*/
|
|
44
|
+
export function calculateViabilityScore(params) {
|
|
45
|
+
let score = 50; // Base score
|
|
46
|
+
// Add repo score contribution (up to +20)
|
|
47
|
+
if (params.repoScore !== null) {
|
|
48
|
+
score += params.repoScore * 2;
|
|
49
|
+
}
|
|
50
|
+
// Repo quality bonus from star/fork counts (#98, up to +12)
|
|
51
|
+
score += params.repoQualityBonus ?? 0;
|
|
52
|
+
// Merged PR bonus (+15) — direct proven relationship with this repo (#99)
|
|
53
|
+
if (params.mergedPRCount > 0) {
|
|
54
|
+
score += 15;
|
|
55
|
+
}
|
|
56
|
+
// Clarity bonus (+15)
|
|
57
|
+
if (params.clearRequirements) {
|
|
58
|
+
score += 15;
|
|
59
|
+
}
|
|
60
|
+
// Freshness bonus (+15 for issues updated within last 14 days)
|
|
61
|
+
const updatedAt = new Date(params.issueUpdatedAt);
|
|
62
|
+
const daysSinceUpdate = daysBetween(updatedAt);
|
|
63
|
+
if (daysSinceUpdate <= 14) {
|
|
64
|
+
score += 15;
|
|
65
|
+
}
|
|
66
|
+
else if (daysSinceUpdate <= 30) {
|
|
67
|
+
// Partial bonus for 15-30 days
|
|
68
|
+
score += Math.round(15 * (1 - (daysSinceUpdate - 14) / 16));
|
|
69
|
+
}
|
|
70
|
+
// Contribution guidelines bonus (+10)
|
|
71
|
+
if (params.hasContributionGuidelines) {
|
|
72
|
+
score += 10;
|
|
73
|
+
}
|
|
74
|
+
// Org affinity bonus (+5) — user has merged PRs in another repo under same org
|
|
75
|
+
if (params.orgHasMergedPRs) {
|
|
76
|
+
score += 5;
|
|
77
|
+
}
|
|
78
|
+
// Penalty for existing PR (-30)
|
|
79
|
+
if (params.hasExistingPR) {
|
|
80
|
+
score -= 30;
|
|
81
|
+
}
|
|
82
|
+
// Penalty for claimed issue (-20)
|
|
83
|
+
if (params.isClaimed) {
|
|
84
|
+
score -= 20;
|
|
85
|
+
}
|
|
86
|
+
// Penalty for closed-without-merge history with no successful merges (-15)
|
|
87
|
+
if (params.closedWithoutMergeCount > 0 && params.mergedPRCount === 0) {
|
|
88
|
+
score -= 15;
|
|
89
|
+
}
|
|
90
|
+
// Clamp to 0-100
|
|
91
|
+
return Math.max(0, Math.min(100, score));
|
|
92
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Issue Vetting — checks individual issues for claimability, existing PRs,
|
|
3
|
+
* project health, contribution guidelines, and requirement clarity.
|
|
4
|
+
*
|
|
5
|
+
* Extracted from issue-discovery.ts (#356) to isolate vetting logic.
|
|
6
|
+
*/
|
|
7
|
+
import { Octokit } from '@octokit/rest';
|
|
8
|
+
import { ContributionGuidelines, ProjectHealth, type SearchPriority, type IssueCandidate } from './types.js';
|
|
9
|
+
import { getStateManager } from './state.js';
|
|
10
|
+
/** Result of a vetting check that may be inconclusive due to API errors. */
|
|
11
|
+
export interface CheckResult {
|
|
12
|
+
passed: boolean;
|
|
13
|
+
inconclusive?: boolean;
|
|
14
|
+
reason?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare class IssueVetter {
|
|
17
|
+
private octokit;
|
|
18
|
+
private stateManager;
|
|
19
|
+
constructor(octokit: Octokit, stateManager: ReturnType<typeof getStateManager>);
|
|
20
|
+
/**
|
|
21
|
+
* Vet a specific issue — runs all checks and computes recommendation + viability score.
|
|
22
|
+
*/
|
|
23
|
+
vetIssue(issueUrl: string): Promise<IssueCandidate>;
|
|
24
|
+
/**
|
|
25
|
+
* Vet multiple issues in parallel with concurrency limit
|
|
26
|
+
*/
|
|
27
|
+
vetIssuesParallel(urls: string[], maxResults: number, priority?: SearchPriority): Promise<{
|
|
28
|
+
candidates: IssueCandidate[];
|
|
29
|
+
allFailed: boolean;
|
|
30
|
+
rateLimitHit: boolean;
|
|
31
|
+
}>;
|
|
32
|
+
/** Check if an error is a GitHub rate limit error (429 or rate-limit 403). */
|
|
33
|
+
static isRateLimitError(error: unknown): boolean;
|
|
34
|
+
checkNoExistingPR(owner: string, repo: string, issueNumber: number): Promise<CheckResult>;
|
|
35
|
+
/**
|
|
36
|
+
* Check how many merged PRs the authenticated user has in a repo.
|
|
37
|
+
* Uses GitHub Search API. Returns 0 on error (non-fatal).
|
|
38
|
+
*/
|
|
39
|
+
checkUserMergedPRsInRepo(owner: string, repo: string): Promise<number>;
|
|
40
|
+
checkNotClaimed(owner: string, repo: string, issueNumber: number, commentCount: number): Promise<CheckResult>;
|
|
41
|
+
checkProjectHealth(owner: string, repo: string): Promise<ProjectHealth>;
|
|
42
|
+
fetchContributionGuidelines(owner: string, repo: string): Promise<ContributionGuidelines | undefined>;
|
|
43
|
+
parseContributionGuidelines(content: string): ContributionGuidelines;
|
|
44
|
+
analyzeRequirements(body: string): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Get the repo score from state, or return null if not evaluated
|
|
47
|
+
*/
|
|
48
|
+
private getRepoScore;
|
|
49
|
+
}
|