@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,311 @@
1
+ /**
2
+ * Comments, Post, and Claim commands
3
+ * Handles GitHub comment interactions
4
+ */
5
+ import { getStateManager, getOctokit, parseGitHubUrl, formatRelativeTime, getGitHubToken } from '../core/index.js';
6
+ import { paginateAll } from '../core/pagination.js';
7
+ import { outputJson, outputJsonError } from '../formatters/json.js';
8
+ import { validateUrl, validateMessage } from './validation.js';
9
+ export async function runComments(options) {
10
+ validateUrl(options.prUrl);
11
+ // Token is guaranteed by the preAction hook in cli.ts for non-LOCAL_ONLY_COMMANDS.
12
+ const token = getGitHubToken();
13
+ const stateManager = getStateManager();
14
+ const octokit = getOctokit(token);
15
+ // Parse PR URL
16
+ const parsed = parseGitHubUrl(options.prUrl);
17
+ if (!parsed || parsed.type !== 'pull') {
18
+ if (options.json) {
19
+ outputJsonError('Invalid PR URL format');
20
+ }
21
+ else {
22
+ console.error('Invalid PR URL format');
23
+ }
24
+ process.exit(1);
25
+ }
26
+ const { owner, repo, number: pull_number } = parsed;
27
+ // Get PR details
28
+ const { data: pr } = await octokit.pulls.get({ owner, repo, pull_number });
29
+ // Get review comments (inline code comments)
30
+ const reviewComments = await paginateAll((page) => octokit.pulls.listReviewComments({
31
+ owner,
32
+ repo,
33
+ pull_number,
34
+ per_page: 100,
35
+ page,
36
+ }));
37
+ // Get issue comments (general PR discussion)
38
+ const issueComments = await paginateAll((page) => octokit.issues.listComments({
39
+ owner,
40
+ repo,
41
+ issue_number: pull_number,
42
+ per_page: 100,
43
+ page,
44
+ }));
45
+ // Get reviews
46
+ const reviews = await paginateAll((page) => octokit.pulls.listReviews({
47
+ owner,
48
+ repo,
49
+ pull_number,
50
+ per_page: 100,
51
+ page,
52
+ }));
53
+ // Filter out own comments, optionally show bots
54
+ const username = stateManager.getState().config.githubUsername;
55
+ const filterComment = (c) => {
56
+ if (!c.user)
57
+ return false;
58
+ if (c.user.login === username)
59
+ return false;
60
+ if (c.user.type === 'Bot' && !options.showBots)
61
+ return false;
62
+ return true;
63
+ };
64
+ const relevantReviewComments = reviewComments
65
+ .filter(filterComment)
66
+ .sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
67
+ const relevantIssueComments = issueComments
68
+ .filter(filterComment)
69
+ .sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
70
+ const relevantReviews = reviews
71
+ .filter((r) => filterComment(r) && r.body && r.body.trim())
72
+ .sort((a, b) => new Date(b.submitted_at || 0).getTime() - new Date(a.submitted_at || 0).getTime());
73
+ if (options.json) {
74
+ outputJson({
75
+ pr: {
76
+ title: pr.title,
77
+ state: pr.state,
78
+ mergeable: pr.mergeable,
79
+ head: pr.head.ref,
80
+ base: pr.base.ref,
81
+ url: pr.html_url,
82
+ },
83
+ reviews: relevantReviews.map((r) => ({
84
+ user: r.user?.login,
85
+ state: r.state,
86
+ body: r.body,
87
+ submittedAt: r.submitted_at,
88
+ })),
89
+ reviewComments: relevantReviewComments.map((c) => ({
90
+ user: c.user?.login,
91
+ body: c.body,
92
+ path: c.path,
93
+ createdAt: c.created_at,
94
+ })),
95
+ issueComments: relevantIssueComments.map((c) => ({
96
+ user: c.user?.login,
97
+ body: c.body,
98
+ createdAt: c.created_at,
99
+ })),
100
+ summary: {
101
+ reviewCount: relevantReviews.length,
102
+ inlineCommentCount: relevantReviewComments.length,
103
+ discussionCommentCount: relevantIssueComments.length,
104
+ },
105
+ });
106
+ return;
107
+ }
108
+ // Text output
109
+ console.log(`\n💬 Fetching comments for: ${options.prUrl}\n`);
110
+ console.log(`## ${pr.title}\n`);
111
+ console.log(`**Status:** ${pr.state} | **Mergeable:** ${pr.mergeable ?? 'checking...'}`);
112
+ console.log(`**Branch:** ${pr.head.ref} → ${pr.base.ref}`);
113
+ console.log(`**URL:** ${pr.html_url}\n`);
114
+ if (relevantReviews.length > 0) {
115
+ console.log('### Reviews (newest first)\n');
116
+ for (const review of relevantReviews) {
117
+ const state = review.state === 'APPROVED' ? '✅' : review.state === 'CHANGES_REQUESTED' ? '❌' : '💬';
118
+ const time = review.submitted_at ? formatRelativeTime(review.submitted_at) : '';
119
+ console.log(`${state} **@${review.user?.login}** (${review.state}) - ${time}`);
120
+ if (review.body) {
121
+ console.log(`> ${review.body.split('\n').join('\n> ')}\n`);
122
+ }
123
+ }
124
+ }
125
+ if (relevantReviewComments.length > 0) {
126
+ console.log('### Inline Comments (newest first)\n');
127
+ for (const comment of relevantReviewComments) {
128
+ const time = formatRelativeTime(comment.created_at);
129
+ console.log(`**@${comment.user?.login}** on \`${comment.path}\` - ${time}`);
130
+ console.log(`> ${comment.body.split('\n').join('\n> ')}`);
131
+ if (comment.diff_hunk) {
132
+ console.log(`\`\`\`diff\n${comment.diff_hunk.slice(-500)}\n\`\`\``);
133
+ }
134
+ console.log('');
135
+ }
136
+ }
137
+ if (relevantIssueComments.length > 0) {
138
+ console.log('### Discussion (newest first)\n');
139
+ for (const comment of relevantIssueComments) {
140
+ const time = formatRelativeTime(comment.created_at);
141
+ console.log(`**@${comment.user?.login}** - ${time}`);
142
+ console.log(`> ${comment.body?.split('\n').join('\n> ')}\n`);
143
+ }
144
+ }
145
+ if (relevantReviewComments.length === 0 && relevantIssueComments.length === 0 && relevantReviews.length === 0) {
146
+ console.log('No comments from other users.\n');
147
+ }
148
+ console.log('---');
149
+ console.log(`**Summary:** ${relevantReviews.length} reviews, ${relevantReviewComments.length} inline comments, ${relevantIssueComments.length} discussion comments`);
150
+ }
151
+ export async function runPost(options) {
152
+ validateUrl(options.url);
153
+ // Token is guaranteed by the preAction hook in cli.ts for non-LOCAL_ONLY_COMMANDS.
154
+ const token = getGitHubToken();
155
+ let message = options.message;
156
+ // Read from stdin if specified
157
+ if (options.stdin) {
158
+ const chunks = [];
159
+ for await (const chunk of process.stdin) {
160
+ chunks.push(chunk);
161
+ }
162
+ message = Buffer.concat(chunks).toString('utf-8').trim();
163
+ }
164
+ if (!message) {
165
+ if (options.json) {
166
+ outputJsonError('No message provided');
167
+ }
168
+ else {
169
+ console.error('Error: No message provided');
170
+ }
171
+ process.exit(1);
172
+ }
173
+ try {
174
+ validateMessage(message);
175
+ }
176
+ catch (error) {
177
+ if (options.json) {
178
+ outputJsonError(error instanceof Error ? error.message : 'Invalid message');
179
+ }
180
+ else {
181
+ console.error(`Error: ${error instanceof Error ? error.message : 'Invalid message'}`);
182
+ }
183
+ process.exit(1);
184
+ }
185
+ // Parse URL
186
+ const parsed = parseGitHubUrl(options.url);
187
+ if (!parsed) {
188
+ if (options.json) {
189
+ outputJsonError('Invalid GitHub URL format');
190
+ }
191
+ else {
192
+ console.error('Invalid GitHub URL format');
193
+ }
194
+ process.exit(1);
195
+ }
196
+ const { owner, repo, number } = parsed;
197
+ const octokit = getOctokit(token);
198
+ if (!options.json) {
199
+ console.log('\n📝 Posting comment to:', options.url);
200
+ console.log('---');
201
+ console.log(message);
202
+ console.log('---\n');
203
+ }
204
+ try {
205
+ const { data: comment } = await octokit.issues.createComment({
206
+ owner,
207
+ repo,
208
+ issue_number: number,
209
+ body: message,
210
+ });
211
+ if (options.json) {
212
+ outputJson({
213
+ commentUrl: comment.html_url,
214
+ url: options.url,
215
+ });
216
+ }
217
+ else {
218
+ console.log('✅ Comment posted successfully!');
219
+ console.log(` ${comment.html_url}`);
220
+ }
221
+ }
222
+ catch (error) {
223
+ if (options.json) {
224
+ outputJsonError(error instanceof Error ? error.message : 'Unknown error');
225
+ }
226
+ else {
227
+ console.error('❌ Failed to post comment:', error instanceof Error ? error.message : error);
228
+ }
229
+ process.exit(1);
230
+ }
231
+ }
232
+ export async function runClaim(options) {
233
+ validateUrl(options.issueUrl);
234
+ // Token is guaranteed by the preAction hook in cli.ts for non-LOCAL_ONLY_COMMANDS.
235
+ const token = getGitHubToken();
236
+ // Default claim message or custom
237
+ const message = options.message || "Hi! I'd like to work on this issue. Could you assign it to me?";
238
+ try {
239
+ validateMessage(message);
240
+ }
241
+ catch (error) {
242
+ if (options.json) {
243
+ outputJsonError(error instanceof Error ? error.message : 'Invalid message');
244
+ }
245
+ else {
246
+ console.error(`Error: ${error instanceof Error ? error.message : 'Invalid message'}`);
247
+ }
248
+ process.exit(1);
249
+ }
250
+ // Parse URL
251
+ const parsed = parseGitHubUrl(options.issueUrl);
252
+ if (!parsed || parsed.type !== 'issues') {
253
+ if (options.json) {
254
+ outputJsonError('Invalid issue URL format (must be an issue, not a PR)');
255
+ }
256
+ else {
257
+ console.error('Invalid issue URL format (must be an issue, not a PR)');
258
+ }
259
+ process.exit(1);
260
+ }
261
+ const { owner, repo, number } = parsed;
262
+ if (!options.json) {
263
+ console.log('\n🙋 Claiming issue:', options.issueUrl);
264
+ console.log('---');
265
+ console.log(message);
266
+ console.log('---\n');
267
+ }
268
+ const octokit = getOctokit(token);
269
+ try {
270
+ const { data: comment } = await octokit.issues.createComment({
271
+ owner,
272
+ repo,
273
+ issue_number: number,
274
+ body: message,
275
+ });
276
+ // Add to tracked issues
277
+ const stateManager = getStateManager();
278
+ stateManager.addIssue({
279
+ id: number,
280
+ url: options.issueUrl,
281
+ repo: `${owner}/${repo}`,
282
+ number,
283
+ title: '(claimed)',
284
+ status: 'claimed',
285
+ labels: [],
286
+ createdAt: new Date().toISOString(),
287
+ updatedAt: new Date().toISOString(),
288
+ vetted: false,
289
+ });
290
+ stateManager.save();
291
+ if (options.json) {
292
+ outputJson({
293
+ commentUrl: comment.html_url,
294
+ issueUrl: options.issueUrl,
295
+ });
296
+ }
297
+ else {
298
+ console.log('✅ Issue claimed!');
299
+ console.log(` ${comment.html_url}`);
300
+ }
301
+ }
302
+ catch (error) {
303
+ if (options.json) {
304
+ outputJsonError(error instanceof Error ? error.message : 'Unknown error');
305
+ }
306
+ else {
307
+ console.error('❌ Failed to claim issue:', error instanceof Error ? error.message : error);
308
+ }
309
+ process.exit(1);
310
+ }
311
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Config command
3
+ * Shows or updates configuration
4
+ */
5
+ interface ConfigOptions {
6
+ key?: string;
7
+ value?: string;
8
+ json?: boolean;
9
+ }
10
+ export declare function runConfig(options: ConfigOptions): Promise<void>;
11
+ export {};
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Config command
3
+ * Shows or updates configuration
4
+ */
5
+ import { getStateManager } from '../core/index.js';
6
+ import { outputJson, outputJsonError } from '../formatters/json.js';
7
+ function exitWithError(msg, json) {
8
+ if (json) {
9
+ outputJsonError(msg);
10
+ }
11
+ else {
12
+ console.error(msg);
13
+ }
14
+ process.exit(1);
15
+ }
16
+ export async function runConfig(options) {
17
+ const stateManager = getStateManager();
18
+ const currentConfig = stateManager.getState().config;
19
+ if (!options.key) {
20
+ // Show current config
21
+ if (options.json) {
22
+ outputJson({ config: currentConfig });
23
+ }
24
+ else {
25
+ console.log('\n⚙️ Current Configuration:\n');
26
+ console.log(JSON.stringify(currentConfig, null, 2));
27
+ }
28
+ return;
29
+ }
30
+ if (!options.value) {
31
+ exitWithError('Value required', options.json);
32
+ }
33
+ const value = options.value;
34
+ // Handle specific config keys
35
+ switch (options.key) {
36
+ case 'username':
37
+ stateManager.updateConfig({ githubUsername: value });
38
+ break;
39
+ case 'add-language':
40
+ if (!currentConfig.languages.includes(value)) {
41
+ stateManager.updateConfig({ languages: [...currentConfig.languages, value] });
42
+ }
43
+ break;
44
+ case 'add-label':
45
+ if (!currentConfig.labels.includes(value)) {
46
+ stateManager.updateConfig({ labels: [...currentConfig.labels, value] });
47
+ }
48
+ break;
49
+ case 'exclude-repo': {
50
+ const parts = value.split('/');
51
+ if (parts.length !== 2 || !parts[0] || !parts[1]) {
52
+ exitWithError(`Invalid repo format "${value}". Use "owner/repo" format. To exclude an entire org, use: config exclude-org ${value}`, options.json);
53
+ }
54
+ const valueLower = value.toLowerCase();
55
+ if (!currentConfig.excludeRepos.some((r) => r.toLowerCase() === valueLower)) {
56
+ stateManager.updateConfig({ excludeRepos: [...currentConfig.excludeRepos, value] });
57
+ stateManager.cleanupExcludedData([value], []);
58
+ }
59
+ break;
60
+ }
61
+ case 'exclude-org': {
62
+ if (value.includes('/')) {
63
+ exitWithError(`Invalid org name "${value}". Use just the org name (e.g., "facebook"), not "owner/repo" format. To exclude a specific repo, use: config exclude-repo ${value}`, options.json);
64
+ }
65
+ const currentOrgs = currentConfig.excludeOrgs ?? [];
66
+ if (!currentOrgs.some((o) => o.toLowerCase() === value.toLowerCase())) {
67
+ stateManager.updateConfig({ excludeOrgs: [...currentOrgs, value] });
68
+ stateManager.cleanupExcludedData([], [value]);
69
+ }
70
+ break;
71
+ }
72
+ default:
73
+ exitWithError(`Unknown config key: ${options.key}`, options.json);
74
+ }
75
+ stateManager.save();
76
+ if (options.json) {
77
+ outputJson({ success: true, key: options.key, value });
78
+ }
79
+ else {
80
+ console.log(`Set ${options.key} to: ${value}`);
81
+ }
82
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Daily check command
3
+ * Fetches all open PRs fresh from GitHub (v2: no PR-level state tracking),
4
+ * generates a digest, and updates repo scores and analytics in local state.
5
+ *
6
+ * Domain logic lives in src/core/daily-logic.ts; this file is a thin
7
+ * orchestration layer that wires up the phases and handles I/O.
8
+ */
9
+ import { type DailyOutput } from '../formatters/json.js';
10
+ export { computeRepoSignals, groupPRsByRepo, assessCapacity, collectActionableIssues, computeActionMenu, toShelvedPRRef, formatBriefSummary, formatSummary, printDigest, CRITICAL_STATUSES, } from '../core/index.js';
11
+ interface DailyOptions {
12
+ json?: boolean;
13
+ }
14
+ export declare function runDaily(options: DailyOptions): Promise<void>;
15
+ /**
16
+ * Core daily check logic, extracted for reuse by the startup command.
17
+ * Fetches all open PRs, updates state, and returns structured output.
18
+ *
19
+ * Returns a deduplicated DailyOutput where category arrays contain PR URLs
20
+ * instead of full objects (#287). Full PR objects are in digest.openPRs only.
21
+ *
22
+ * Orchestrates five named phases:
23
+ * 1. fetchPRData — fetch open PRs, merged/closed counts, issues
24
+ * 2. updateRepoScores — update signals, star counts, trust in state
25
+ * 3. updateAnalytics — store monthly chart data
26
+ * 4. partitionPRs — expire snoozes, shelve/unshelve, generate digest
27
+ * 5. generateDigestOutput — capacity, dismiss filter, action menu assembly
28
+ */
29
+ export declare function executeDailyCheck(token: string): Promise<DailyOutput>;