@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.
Files changed (98) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +85 -0
  3. package/dist/cli.bundle.cjs +17657 -0
  4. package/dist/cli.d.ts +12 -0
  5. package/dist/cli.js +325 -0
  6. package/dist/commands/check-integration.d.ts +10 -0
  7. package/dist/commands/check-integration.js +192 -0
  8. package/dist/commands/comments.d.ts +24 -0
  9. package/dist/commands/comments.js +311 -0
  10. package/dist/commands/config.d.ts +11 -0
  11. package/dist/commands/config.js +82 -0
  12. package/dist/commands/daily.d.ts +29 -0
  13. package/dist/commands/daily.js +433 -0
  14. package/dist/commands/dashboard-data.d.ts +45 -0
  15. package/dist/commands/dashboard-data.js +132 -0
  16. package/dist/commands/dashboard-templates.d.ts +23 -0
  17. package/dist/commands/dashboard-templates.js +1627 -0
  18. package/dist/commands/dashboard.d.ts +18 -0
  19. package/dist/commands/dashboard.js +134 -0
  20. package/dist/commands/dismiss.d.ts +13 -0
  21. package/dist/commands/dismiss.js +49 -0
  22. package/dist/commands/init.d.ts +10 -0
  23. package/dist/commands/init.js +27 -0
  24. package/dist/commands/local-repos.d.ts +14 -0
  25. package/dist/commands/local-repos.js +155 -0
  26. package/dist/commands/parse-list.d.ts +13 -0
  27. package/dist/commands/parse-list.js +139 -0
  28. package/dist/commands/read.d.ts +12 -0
  29. package/dist/commands/read.js +33 -0
  30. package/dist/commands/search.d.ts +10 -0
  31. package/dist/commands/search.js +74 -0
  32. package/dist/commands/setup.d.ts +15 -0
  33. package/dist/commands/setup.js +276 -0
  34. package/dist/commands/shelve.d.ts +13 -0
  35. package/dist/commands/shelve.js +49 -0
  36. package/dist/commands/snooze.d.ts +18 -0
  37. package/dist/commands/snooze.js +83 -0
  38. package/dist/commands/startup.d.ts +33 -0
  39. package/dist/commands/startup.js +197 -0
  40. package/dist/commands/status.d.ts +10 -0
  41. package/dist/commands/status.js +43 -0
  42. package/dist/commands/track.d.ts +16 -0
  43. package/dist/commands/track.js +59 -0
  44. package/dist/commands/validation.d.ts +43 -0
  45. package/dist/commands/validation.js +112 -0
  46. package/dist/commands/vet.d.ts +10 -0
  47. package/dist/commands/vet.js +36 -0
  48. package/dist/core/checklist-analysis.d.ts +17 -0
  49. package/dist/core/checklist-analysis.js +39 -0
  50. package/dist/core/ci-analysis.d.ts +78 -0
  51. package/dist/core/ci-analysis.js +163 -0
  52. package/dist/core/comment-utils.d.ts +15 -0
  53. package/dist/core/comment-utils.js +52 -0
  54. package/dist/core/concurrency.d.ts +5 -0
  55. package/dist/core/concurrency.js +15 -0
  56. package/dist/core/daily-logic.d.ts +77 -0
  57. package/dist/core/daily-logic.js +512 -0
  58. package/dist/core/display-utils.d.ts +10 -0
  59. package/dist/core/display-utils.js +100 -0
  60. package/dist/core/errors.d.ts +24 -0
  61. package/dist/core/errors.js +34 -0
  62. package/dist/core/github-stats.d.ts +73 -0
  63. package/dist/core/github-stats.js +272 -0
  64. package/dist/core/github.d.ts +19 -0
  65. package/dist/core/github.js +60 -0
  66. package/dist/core/http-cache.d.ts +97 -0
  67. package/dist/core/http-cache.js +269 -0
  68. package/dist/core/index.d.ts +15 -0
  69. package/dist/core/index.js +15 -0
  70. package/dist/core/issue-conversation.d.ts +29 -0
  71. package/dist/core/issue-conversation.js +231 -0
  72. package/dist/core/issue-discovery.d.ts +85 -0
  73. package/dist/core/issue-discovery.js +589 -0
  74. package/dist/core/issue-filtering.d.ts +51 -0
  75. package/dist/core/issue-filtering.js +103 -0
  76. package/dist/core/issue-scoring.d.ts +40 -0
  77. package/dist/core/issue-scoring.js +92 -0
  78. package/dist/core/issue-vetting.d.ts +49 -0
  79. package/dist/core/issue-vetting.js +536 -0
  80. package/dist/core/logger.d.ts +21 -0
  81. package/dist/core/logger.js +49 -0
  82. package/dist/core/maintainer-analysis.d.ts +10 -0
  83. package/dist/core/maintainer-analysis.js +59 -0
  84. package/dist/core/pagination.d.ts +11 -0
  85. package/dist/core/pagination.js +20 -0
  86. package/dist/core/pr-monitor.d.ts +109 -0
  87. package/dist/core/pr-monitor.js +594 -0
  88. package/dist/core/review-analysis.d.ts +72 -0
  89. package/dist/core/review-analysis.js +163 -0
  90. package/dist/core/state.d.ts +371 -0
  91. package/dist/core/state.js +1089 -0
  92. package/dist/core/types.d.ts +507 -0
  93. package/dist/core/types.js +34 -0
  94. package/dist/core/utils.d.ts +249 -0
  95. package/dist/core/utils.js +422 -0
  96. package/dist/formatters/json.d.ts +269 -0
  97. package/dist/formatters/json.js +88 -0
  98. 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
+ }