@oss-autopilot/core 0.41.0 → 0.42.1
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.bundle.cjs +1552 -1318
- package/dist/cli.js +593 -69
- package/dist/commands/check-integration.d.ts +3 -3
- package/dist/commands/check-integration.js +10 -43
- package/dist/commands/comments.d.ts +6 -9
- package/dist/commands/comments.js +102 -252
- package/dist/commands/config.d.ts +8 -2
- package/dist/commands/config.js +6 -28
- package/dist/commands/daily.d.ts +28 -4
- package/dist/commands/daily.js +33 -45
- package/dist/commands/dashboard-data.js +7 -6
- package/dist/commands/dashboard-server.d.ts +14 -0
- package/dist/commands/dashboard-server.js +362 -0
- package/dist/commands/dashboard.d.ts +5 -0
- package/dist/commands/dashboard.js +51 -1
- package/dist/commands/dismiss.d.ts +13 -5
- package/dist/commands/dismiss.js +4 -24
- package/dist/commands/index.d.ts +33 -0
- package/dist/commands/index.js +22 -0
- package/dist/commands/init.d.ts +5 -4
- package/dist/commands/init.js +4 -14
- package/dist/commands/local-repos.d.ts +4 -5
- package/dist/commands/local-repos.js +6 -33
- package/dist/commands/parse-list.d.ts +3 -4
- package/dist/commands/parse-list.js +8 -39
- package/dist/commands/read.d.ts +11 -5
- package/dist/commands/read.js +4 -18
- package/dist/commands/search.d.ts +3 -3
- package/dist/commands/search.js +39 -65
- package/dist/commands/setup.d.ts +34 -5
- package/dist/commands/setup.js +75 -166
- package/dist/commands/shelve.d.ts +13 -5
- package/dist/commands/shelve.js +4 -24
- package/dist/commands/snooze.d.ts +15 -9
- package/dist/commands/snooze.js +16 -59
- package/dist/commands/startup.d.ts +11 -6
- package/dist/commands/startup.js +44 -82
- package/dist/commands/status.d.ts +3 -3
- package/dist/commands/status.js +10 -29
- package/dist/commands/track.d.ts +10 -9
- package/dist/commands/track.js +17 -39
- package/dist/commands/validation.d.ts +2 -2
- package/dist/commands/validation.js +7 -15
- package/dist/commands/vet.d.ts +3 -3
- package/dist/commands/vet.js +16 -26
- package/dist/core/errors.d.ts +9 -0
- package/dist/core/errors.js +17 -0
- package/dist/core/github-stats.d.ts +14 -21
- package/dist/core/github-stats.js +84 -138
- package/dist/core/http-cache.d.ts +6 -0
- package/dist/core/http-cache.js +16 -4
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.js +2 -1
- package/dist/core/issue-conversation.js +4 -4
- package/dist/core/issue-discovery.js +14 -14
- package/dist/core/issue-vetting.js +17 -17
- package/dist/core/pr-monitor.d.ts +6 -20
- package/dist/core/pr-monitor.js +11 -52
- package/dist/core/state.js +4 -5
- package/dist/core/utils.d.ts +11 -0
- package/dist/core/utils.js +21 -0
- package/dist/formatters/json.d.ts +58 -0
- package/package.json +5 -1
package/dist/cli.js
CHANGED
|
@@ -10,21 +10,30 @@
|
|
|
10
10
|
* async token fetch to avoid blocking the event loop on `gh auth token`.
|
|
11
11
|
*/
|
|
12
12
|
import { Command } from 'commander';
|
|
13
|
-
import { getGitHubTokenAsync, enableDebug, debug } from './core/index.js';
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
13
|
+
import { getGitHubTokenAsync, enableDebug, debug, formatRelativeTime, getCLIVersion } from './core/index.js';
|
|
14
|
+
import { errorMessage } from './core/errors.js';
|
|
15
|
+
import { outputJson, outputJsonError } from './formatters/json.js';
|
|
16
|
+
/** Print local repos in human-readable format */
|
|
17
|
+
function printRepos(repos) {
|
|
18
|
+
const entries = Object.entries(repos).sort(([a], [b]) => a.localeCompare(b));
|
|
19
|
+
for (const [remote, info] of entries) {
|
|
20
|
+
const branch = info.currentBranch ? ` (${info.currentBranch})` : '';
|
|
21
|
+
console.log(` ${remote}${branch}`);
|
|
22
|
+
console.log(` ${info.path}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** Shared error handler for CLI action catch blocks. */
|
|
26
|
+
function handleCommandError(err, json) {
|
|
27
|
+
const msg = errorMessage(err);
|
|
28
|
+
if (json) {
|
|
29
|
+
outputJsonError(msg);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
console.error(`Error: ${msg}`);
|
|
33
|
+
}
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
const VERSION = getCLIVersion();
|
|
28
37
|
// Commands that skip the preAction GitHub token check.
|
|
29
38
|
// startup handles auth internally (returns authError in JSON instead of process.exit).
|
|
30
39
|
const LOCAL_ONLY_COMMANDS = [
|
|
@@ -37,6 +46,7 @@ const LOCAL_ONLY_COMMANDS = [
|
|
|
37
46
|
'setup',
|
|
38
47
|
'checkSetup',
|
|
39
48
|
'dashboard',
|
|
49
|
+
'serve',
|
|
40
50
|
'parse-issue-list',
|
|
41
51
|
'check-integration',
|
|
42
52
|
'local-repos',
|
|
@@ -60,8 +70,21 @@ program
|
|
|
60
70
|
.description('Run daily check on all tracked PRs')
|
|
61
71
|
.option('--json', 'Output as JSON')
|
|
62
72
|
.action(async (options) => {
|
|
63
|
-
|
|
64
|
-
|
|
73
|
+
try {
|
|
74
|
+
if (options.json) {
|
|
75
|
+
const { runDaily } = await import('./commands/daily.js');
|
|
76
|
+
const data = await runDaily();
|
|
77
|
+
outputJson(data);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
const { runDailyForDisplay, printDigest } = await import('./commands/daily.js');
|
|
81
|
+
const result = await runDailyForDisplay();
|
|
82
|
+
printDigest(result.digest, result.capacity, result.commentedIssues);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
handleCommandError(err, options.json);
|
|
87
|
+
}
|
|
65
88
|
});
|
|
66
89
|
// Status command
|
|
67
90
|
program
|
|
@@ -70,8 +93,31 @@ program
|
|
|
70
93
|
.option('--json', 'Output as JSON')
|
|
71
94
|
.option('--offline', 'Use cached data only (no GitHub API calls)')
|
|
72
95
|
.action(async (options) => {
|
|
73
|
-
|
|
74
|
-
|
|
96
|
+
try {
|
|
97
|
+
const { runStatus } = await import('./commands/status.js');
|
|
98
|
+
const data = await runStatus({ offline: options.offline });
|
|
99
|
+
if (options.json) {
|
|
100
|
+
outputJson(data);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
console.log('\n\ud83d\udcca OSS Status\n');
|
|
104
|
+
console.log(`Merged PRs: ${data.stats.mergedPRs}`);
|
|
105
|
+
console.log(`Closed PRs: ${data.stats.closedPRs}`);
|
|
106
|
+
console.log(`Merge Rate: ${data.stats.mergeRate}`);
|
|
107
|
+
console.log(`Needs Response: ${data.stats.needsResponse}`);
|
|
108
|
+
if (data.offline) {
|
|
109
|
+
console.log(`\nLast Updated: ${data.lastUpdated || 'Never'}`);
|
|
110
|
+
console.log('(Offline mode: showing cached data)');
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
console.log(`\nLast Run: ${data.lastRunAt || 'Never'}`);
|
|
114
|
+
}
|
|
115
|
+
console.log('\nRun with --json for structured output');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
handleCommandError(err, options.json);
|
|
120
|
+
}
|
|
75
121
|
});
|
|
76
122
|
// Search command
|
|
77
123
|
program
|
|
@@ -79,8 +125,46 @@ program
|
|
|
79
125
|
.description('Search for new issues to work on')
|
|
80
126
|
.option('--json', 'Output as JSON')
|
|
81
127
|
.action(async (count, options) => {
|
|
82
|
-
|
|
83
|
-
|
|
128
|
+
try {
|
|
129
|
+
const { runSearch } = await import('./commands/search.js');
|
|
130
|
+
if (!options.json) {
|
|
131
|
+
console.log(`\nSearching for issues (max ${parseInt(count) || 5})...\n`);
|
|
132
|
+
}
|
|
133
|
+
const data = await runSearch({ maxResults: parseInt(count) || 5 });
|
|
134
|
+
if (options.json) {
|
|
135
|
+
outputJson(data);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
if (data.candidates.length === 0) {
|
|
139
|
+
if (data.rateLimitWarning) {
|
|
140
|
+
console.warn(`\n${data.rateLimitWarning}\n`);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
console.log('No matching issues found.');
|
|
144
|
+
}
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (data.rateLimitWarning) {
|
|
148
|
+
console.warn(`\n${data.rateLimitWarning}\n`);
|
|
149
|
+
}
|
|
150
|
+
console.log(`Found ${data.candidates.length} candidates:\n`);
|
|
151
|
+
for (const candidate of data.candidates) {
|
|
152
|
+
// Simple text format for candidates
|
|
153
|
+
const { issue, recommendation, reasonsToApprove, reasonsToSkip, viabilityScore } = candidate;
|
|
154
|
+
console.log(`[${recommendation.toUpperCase()}] ${issue.repo}#${issue.number}: ${issue.title}`);
|
|
155
|
+
console.log(` URL: ${issue.url}`);
|
|
156
|
+
console.log(` Viability: ${viabilityScore}/100`);
|
|
157
|
+
if (reasonsToApprove.length > 0)
|
|
158
|
+
console.log(` Approve: ${reasonsToApprove.join(', ')}`);
|
|
159
|
+
if (reasonsToSkip.length > 0)
|
|
160
|
+
console.log(` Skip: ${reasonsToSkip.join(', ')}`);
|
|
161
|
+
console.log('---');
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
handleCommandError(err, options.json);
|
|
167
|
+
}
|
|
84
168
|
});
|
|
85
169
|
// Vet command
|
|
86
170
|
program
|
|
@@ -88,8 +172,26 @@ program
|
|
|
88
172
|
.description('Vet a specific issue before working on it')
|
|
89
173
|
.option('--json', 'Output as JSON')
|
|
90
174
|
.action(async (issueUrl, options) => {
|
|
91
|
-
|
|
92
|
-
|
|
175
|
+
try {
|
|
176
|
+
const { runVet } = await import('./commands/vet.js');
|
|
177
|
+
const data = await runVet({ issueUrl });
|
|
178
|
+
if (options.json) {
|
|
179
|
+
outputJson(data);
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
const { issue, recommendation, reasonsToApprove, reasonsToSkip } = data;
|
|
183
|
+
console.log(`\nVetting issue: ${issueUrl}\n`);
|
|
184
|
+
console.log(`[${recommendation.toUpperCase()}] ${issue.repo}#${issue.number}: ${issue.title}`);
|
|
185
|
+
console.log(` URL: ${issue.url}`);
|
|
186
|
+
if (reasonsToApprove.length > 0)
|
|
187
|
+
console.log(` Approve: ${reasonsToApprove.join(', ')}`);
|
|
188
|
+
if (reasonsToSkip.length > 0)
|
|
189
|
+
console.log(` Skip: ${reasonsToSkip.join(', ')}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
handleCommandError(err, options.json);
|
|
194
|
+
}
|
|
93
195
|
});
|
|
94
196
|
// Track command
|
|
95
197
|
program
|
|
@@ -97,8 +199,20 @@ program
|
|
|
97
199
|
.description('Add a PR to track')
|
|
98
200
|
.option('--json', 'Output as JSON')
|
|
99
201
|
.action(async (prUrl, options) => {
|
|
100
|
-
|
|
101
|
-
|
|
202
|
+
try {
|
|
203
|
+
const { runTrack } = await import('./commands/track.js');
|
|
204
|
+
const data = await runTrack({ prUrl });
|
|
205
|
+
if (options.json) {
|
|
206
|
+
outputJson(data);
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
console.log(`\nPR: ${data.pr.repo}#${data.pr.number} - ${data.pr.title}`);
|
|
210
|
+
console.log('Note: In v2, PRs are tracked automatically via the daily run.');
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
catch (err) {
|
|
214
|
+
handleCommandError(err, options.json);
|
|
215
|
+
}
|
|
102
216
|
});
|
|
103
217
|
// Untrack command
|
|
104
218
|
program
|
|
@@ -106,8 +220,20 @@ program
|
|
|
106
220
|
.description('Stop tracking a PR')
|
|
107
221
|
.option('--json', 'Output as JSON')
|
|
108
222
|
.action(async (prUrl, options) => {
|
|
109
|
-
|
|
110
|
-
|
|
223
|
+
try {
|
|
224
|
+
const { runUntrack } = await import('./commands/track.js');
|
|
225
|
+
const data = await runUntrack({ prUrl });
|
|
226
|
+
if (options.json) {
|
|
227
|
+
outputJson(data);
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
console.log('Note: In v2, PRs are fetched fresh on each daily run \u2014 there is no local tracking list to remove from.');
|
|
231
|
+
console.log('Use `shelve` to temporarily hide a PR from the daily summary.');
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
handleCommandError(err, options.json);
|
|
236
|
+
}
|
|
111
237
|
});
|
|
112
238
|
// Read command (mark as read)
|
|
113
239
|
program
|
|
@@ -116,8 +242,19 @@ program
|
|
|
116
242
|
.option('--all', 'Mark all PRs as read')
|
|
117
243
|
.option('--json', 'Output as JSON')
|
|
118
244
|
.action(async (prUrl, options) => {
|
|
119
|
-
|
|
120
|
-
|
|
245
|
+
try {
|
|
246
|
+
const { runRead } = await import('./commands/read.js');
|
|
247
|
+
const data = await runRead({ prUrl, all: options.all });
|
|
248
|
+
if (options.json) {
|
|
249
|
+
outputJson(data);
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
console.log('Note: In v2, PR read state is not tracked locally. PRs are fetched fresh on each daily run.');
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch (err) {
|
|
256
|
+
handleCommandError(err, options.json);
|
|
257
|
+
}
|
|
121
258
|
});
|
|
122
259
|
// Comments command
|
|
123
260
|
program
|
|
@@ -126,8 +263,61 @@ program
|
|
|
126
263
|
.option('--bots', 'Include bot comments')
|
|
127
264
|
.option('--json', 'Output as JSON')
|
|
128
265
|
.action(async (prUrl, options) => {
|
|
129
|
-
|
|
130
|
-
|
|
266
|
+
try {
|
|
267
|
+
const { runComments } = await import('./commands/comments.js');
|
|
268
|
+
const data = await runComments({ prUrl, showBots: options.bots });
|
|
269
|
+
if (options.json) {
|
|
270
|
+
outputJson(data);
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
// Text output
|
|
274
|
+
console.log(`\nFetching comments for: ${prUrl}\n`);
|
|
275
|
+
console.log(`## ${data.pr.title}\n`);
|
|
276
|
+
console.log(`**Status:** ${data.pr.state} | **Mergeable:** ${data.pr.mergeable ?? 'checking...'}`);
|
|
277
|
+
console.log(`**Branch:** ${data.pr.head} -> ${data.pr.base}`);
|
|
278
|
+
console.log(`**URL:** ${data.pr.url}\n`);
|
|
279
|
+
const REVIEW_STATE_LABELS = {
|
|
280
|
+
APPROVED: '[Approved]',
|
|
281
|
+
CHANGES_REQUESTED: '[Changes]',
|
|
282
|
+
};
|
|
283
|
+
if (data.reviews.length > 0) {
|
|
284
|
+
console.log('### Reviews (newest first)\n');
|
|
285
|
+
for (const review of data.reviews) {
|
|
286
|
+
const state = REVIEW_STATE_LABELS[review.state] ?? '[Comment]';
|
|
287
|
+
const time = review.submittedAt ? formatRelativeTime(review.submittedAt) : '';
|
|
288
|
+
console.log(`${state} **@${review.user}** (${review.state}) - ${time}`);
|
|
289
|
+
if (review.body) {
|
|
290
|
+
console.log(`> ${review.body.split('\n').join('\n> ')}\n`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (data.reviewComments.length > 0) {
|
|
295
|
+
console.log('### Inline Comments (newest first)\n');
|
|
296
|
+
for (const comment of data.reviewComments) {
|
|
297
|
+
const time = formatRelativeTime(comment.createdAt);
|
|
298
|
+
console.log(`**@${comment.user}** on \`${comment.path}\` - ${time}`);
|
|
299
|
+
console.log(`> ${comment.body.split('\n').join('\n> ')}`);
|
|
300
|
+
console.log('');
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (data.issueComments.length > 0) {
|
|
304
|
+
console.log('### Discussion (newest first)\n');
|
|
305
|
+
for (const comment of data.issueComments) {
|
|
306
|
+
const time = formatRelativeTime(comment.createdAt);
|
|
307
|
+
console.log(`**@${comment.user}** - ${time}`);
|
|
308
|
+
console.log(`> ${comment.body?.split('\n').join('\n> ')}\n`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if (data.reviewComments.length === 0 && data.issueComments.length === 0 && data.reviews.length === 0) {
|
|
312
|
+
console.log('No comments from other users.\n');
|
|
313
|
+
}
|
|
314
|
+
console.log('---');
|
|
315
|
+
console.log(`**Summary:** ${data.summary.reviewCount} reviews, ${data.summary.inlineCommentCount} inline comments, ${data.summary.discussionCommentCount} discussion comments`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
catch (err) {
|
|
319
|
+
handleCommandError(err, options.json);
|
|
320
|
+
}
|
|
131
321
|
});
|
|
132
322
|
// Post command
|
|
133
323
|
program
|
|
@@ -136,9 +326,30 @@ program
|
|
|
136
326
|
.option('--stdin', 'Read message from stdin')
|
|
137
327
|
.option('--json', 'Output as JSON')
|
|
138
328
|
.action(async (url, messageParts, options) => {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
329
|
+
try {
|
|
330
|
+
let message;
|
|
331
|
+
if (options.stdin) {
|
|
332
|
+
const chunks = [];
|
|
333
|
+
for await (const chunk of process.stdin) {
|
|
334
|
+
chunks.push(chunk);
|
|
335
|
+
}
|
|
336
|
+
message = Buffer.concat(chunks).toString('utf-8').trim();
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
message = messageParts.join(' ');
|
|
340
|
+
}
|
|
341
|
+
const { runPost } = await import('./commands/comments.js');
|
|
342
|
+
const data = await runPost({ url, message });
|
|
343
|
+
if (options.json) {
|
|
344
|
+
outputJson(data);
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
console.log(`Comment posted: ${data.commentUrl}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
catch (err) {
|
|
351
|
+
handleCommandError(err, options.json);
|
|
352
|
+
}
|
|
142
353
|
});
|
|
143
354
|
// Claim command
|
|
144
355
|
program
|
|
@@ -146,9 +357,20 @@ program
|
|
|
146
357
|
.description('Claim an issue by posting a comment')
|
|
147
358
|
.option('--json', 'Output as JSON')
|
|
148
359
|
.action(async (issueUrl, messageParts, options) => {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
360
|
+
try {
|
|
361
|
+
const { runClaim } = await import('./commands/comments.js');
|
|
362
|
+
const message = messageParts.length > 0 ? messageParts.join(' ') : undefined;
|
|
363
|
+
const data = await runClaim({ issueUrl, message });
|
|
364
|
+
if (options.json) {
|
|
365
|
+
outputJson(data);
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
console.log(`Issue claimed: ${data.commentUrl}`);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
catch (err) {
|
|
372
|
+
handleCommandError(err, options.json);
|
|
373
|
+
}
|
|
152
374
|
});
|
|
153
375
|
// Config command
|
|
154
376
|
program
|
|
@@ -156,8 +378,23 @@ program
|
|
|
156
378
|
.description('Show or update configuration')
|
|
157
379
|
.option('--json', 'Output as JSON')
|
|
158
380
|
.action(async (key, value, options) => {
|
|
159
|
-
|
|
160
|
-
|
|
381
|
+
try {
|
|
382
|
+
const { runConfig } = await import('./commands/config.js');
|
|
383
|
+
const data = await runConfig({ key, value });
|
|
384
|
+
if (options.json) {
|
|
385
|
+
outputJson(data);
|
|
386
|
+
}
|
|
387
|
+
else if ('config' in data) {
|
|
388
|
+
console.log('\n\u2699\ufe0f Current Configuration:\n');
|
|
389
|
+
console.log(JSON.stringify(data.config, null, 2));
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
console.log(`Set ${data.key} to: ${data.value}`);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
catch (err) {
|
|
396
|
+
handleCommandError(err, options.json);
|
|
397
|
+
}
|
|
161
398
|
});
|
|
162
399
|
// Init command
|
|
163
400
|
program
|
|
@@ -165,8 +402,20 @@ program
|
|
|
165
402
|
.description('Initialize with your GitHub username and import open PRs')
|
|
166
403
|
.option('--json', 'Output as JSON')
|
|
167
404
|
.action(async (username, options) => {
|
|
168
|
-
|
|
169
|
-
|
|
405
|
+
try {
|
|
406
|
+
const { runInit } = await import('./commands/init.js');
|
|
407
|
+
const data = await runInit({ username });
|
|
408
|
+
if (options.json) {
|
|
409
|
+
outputJson(data);
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
console.log(`\nUsername set to @${data.username}.`);
|
|
413
|
+
console.log('Run `oss-autopilot daily` to fetch your open PRs from GitHub.');
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
catch (err) {
|
|
417
|
+
handleCommandError(err, options.json);
|
|
418
|
+
}
|
|
170
419
|
});
|
|
171
420
|
// Setup command
|
|
172
421
|
program
|
|
@@ -176,8 +425,64 @@ program
|
|
|
176
425
|
.option('--set <settings...>', 'Set specific values (key=value)')
|
|
177
426
|
.option('--json', 'Output as JSON')
|
|
178
427
|
.action(async (options) => {
|
|
179
|
-
|
|
180
|
-
|
|
428
|
+
try {
|
|
429
|
+
const { runSetup } = await import('./commands/setup.js');
|
|
430
|
+
const data = await runSetup({ reset: options.reset, set: options.set });
|
|
431
|
+
if (options.json) {
|
|
432
|
+
outputJson(data);
|
|
433
|
+
}
|
|
434
|
+
else if ('success' in data) {
|
|
435
|
+
// --set mode
|
|
436
|
+
for (const [key, value] of Object.entries(data.settings)) {
|
|
437
|
+
console.log(`\u2713 ${key}: ${value}`);
|
|
438
|
+
}
|
|
439
|
+
if (data.warnings) {
|
|
440
|
+
for (const w of data.warnings) {
|
|
441
|
+
console.warn(w);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
else if ('setupComplete' in data && data.setupComplete) {
|
|
446
|
+
// Already complete
|
|
447
|
+
console.log('\n\u2699\ufe0f OSS Autopilot Setup\n');
|
|
448
|
+
console.log('\u2713 Setup already complete!\n');
|
|
449
|
+
console.log('Current settings:');
|
|
450
|
+
console.log(` GitHub username: ${data.config.githubUsername || '(not set)'}`);
|
|
451
|
+
console.log(` Max active PRs: ${data.config.maxActivePRs}`);
|
|
452
|
+
console.log(` Dormant threshold: ${data.config.dormantThresholdDays} days`);
|
|
453
|
+
console.log(` Approaching dormant: ${data.config.approachingDormantDays} days`);
|
|
454
|
+
console.log(` Languages: ${data.config.languages.join(', ')}`);
|
|
455
|
+
console.log(` Labels: ${data.config.labels.join(', ')}`);
|
|
456
|
+
console.log(`\nRun 'setup --reset' to reconfigure.`);
|
|
457
|
+
}
|
|
458
|
+
else if ('setupRequired' in data) {
|
|
459
|
+
// Needs setup
|
|
460
|
+
console.log('\n\u2699\ufe0f OSS Autopilot Setup\n');
|
|
461
|
+
console.log('SETUP_REQUIRED');
|
|
462
|
+
console.log('---');
|
|
463
|
+
console.log('Please configure the following settings:\n');
|
|
464
|
+
for (const prompt of data.prompts) {
|
|
465
|
+
console.log(`SETTING: ${prompt.setting}`);
|
|
466
|
+
console.log(`PROMPT: ${prompt.prompt}`);
|
|
467
|
+
const currentVal = Array.isArray(prompt.current) ? prompt.current.join(', ') : prompt.current;
|
|
468
|
+
console.log(`CURRENT: ${currentVal ?? '(not set)'}`);
|
|
469
|
+
if (prompt.required)
|
|
470
|
+
console.log('REQUIRED: true');
|
|
471
|
+
if (prompt.default !== undefined) {
|
|
472
|
+
const defaultVal = Array.isArray(prompt.default) ? prompt.default.join(', ') : prompt.default;
|
|
473
|
+
console.log(`DEFAULT: ${defaultVal}`);
|
|
474
|
+
}
|
|
475
|
+
if (prompt.type)
|
|
476
|
+
console.log(`TYPE: ${prompt.type}`);
|
|
477
|
+
console.log('');
|
|
478
|
+
}
|
|
479
|
+
console.log('---');
|
|
480
|
+
console.log('END_SETUP_PROMPTS');
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
catch (err) {
|
|
484
|
+
handleCommandError(err, options.json);
|
|
485
|
+
}
|
|
181
486
|
});
|
|
182
487
|
// Check setup command
|
|
183
488
|
program
|
|
@@ -185,13 +490,42 @@ program
|
|
|
185
490
|
.description('Check if setup is complete')
|
|
186
491
|
.option('--json', 'Output as JSON')
|
|
187
492
|
.action(async (options) => {
|
|
188
|
-
|
|
189
|
-
|
|
493
|
+
try {
|
|
494
|
+
const { runCheckSetup } = await import('./commands/setup.js');
|
|
495
|
+
const data = await runCheckSetup();
|
|
496
|
+
if (options.json) {
|
|
497
|
+
outputJson(data);
|
|
498
|
+
}
|
|
499
|
+
else if (data.setupComplete) {
|
|
500
|
+
console.log('SETUP_COMPLETE');
|
|
501
|
+
console.log(`username=${data.username}`);
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
console.log('SETUP_INCOMPLETE');
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
catch (err) {
|
|
508
|
+
handleCommandError(err, options.json);
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
// Dashboard commands
|
|
512
|
+
const dashboardCmd = program.command('dashboard').description('Dashboard commands');
|
|
513
|
+
dashboardCmd
|
|
514
|
+
.command('serve')
|
|
515
|
+
.description('Start interactive dashboard server')
|
|
516
|
+
.option('--port <port>', 'Port to listen on', '3000')
|
|
517
|
+
.option('--no-open', 'Do not open browser automatically')
|
|
518
|
+
.action(async (options) => {
|
|
519
|
+
const port = parseInt(options.port, 10);
|
|
520
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
521
|
+
console.error(`Invalid port number: "${options.port}". Must be an integer between 1 and 65535.`);
|
|
522
|
+
process.exit(1);
|
|
523
|
+
}
|
|
524
|
+
const { serveDashboard } = await import('./commands/dashboard.js');
|
|
525
|
+
await serveDashboard({ port, open: options.open });
|
|
190
526
|
});
|
|
191
|
-
//
|
|
192
|
-
|
|
193
|
-
.command('dashboard')
|
|
194
|
-
.description('Generate HTML stats dashboard')
|
|
527
|
+
// Keep bare `dashboard` (no subcommand) for backward compat — generates static HTML
|
|
528
|
+
dashboardCmd
|
|
195
529
|
.option('--open', 'Open in browser')
|
|
196
530
|
.option('--json', 'Output as JSON')
|
|
197
531
|
.option('--offline', 'Use cached data only (no GitHub API calls)')
|
|
@@ -205,8 +539,34 @@ program
|
|
|
205
539
|
.description('Parse a markdown issue list into structured JSON')
|
|
206
540
|
.option('--json', 'Output as JSON')
|
|
207
541
|
.action(async (filePath, options) => {
|
|
208
|
-
|
|
209
|
-
|
|
542
|
+
try {
|
|
543
|
+
const { runParseList } = await import('./commands/parse-list.js');
|
|
544
|
+
const data = await runParseList({ filePath });
|
|
545
|
+
if (options.json) {
|
|
546
|
+
outputJson(data);
|
|
547
|
+
}
|
|
548
|
+
else {
|
|
549
|
+
const path = await import('path');
|
|
550
|
+
const resolvedPath = path.resolve(filePath);
|
|
551
|
+
console.log(`\n\ud83d\udccb Issue List: ${resolvedPath}\n`);
|
|
552
|
+
console.log(`Available: ${data.availableCount} | Completed: ${data.completedCount}\n`);
|
|
553
|
+
if (data.available.length > 0) {
|
|
554
|
+
console.log('--- Available ---');
|
|
555
|
+
for (const item of data.available) {
|
|
556
|
+
console.log(` [${item.tier}] ${item.repo}#${item.number}: ${item.title}`);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
if (data.completed.length > 0) {
|
|
560
|
+
console.log('\n--- Completed ---');
|
|
561
|
+
for (const item of data.completed) {
|
|
562
|
+
console.log(` [${item.tier}] ${item.repo}#${item.number}: ${item.title}`);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
catch (err) {
|
|
568
|
+
handleCommandError(err, options.json);
|
|
569
|
+
}
|
|
210
570
|
});
|
|
211
571
|
// Check integration command (#83)
|
|
212
572
|
program
|
|
@@ -215,8 +575,36 @@ program
|
|
|
215
575
|
.option('--base <branch>', 'Base branch to compare against', 'main')
|
|
216
576
|
.option('--json', 'Output as JSON')
|
|
217
577
|
.action(async (options) => {
|
|
218
|
-
|
|
219
|
-
|
|
578
|
+
try {
|
|
579
|
+
const { runCheckIntegration } = await import('./commands/check-integration.js');
|
|
580
|
+
const data = await runCheckIntegration({ base: options.base });
|
|
581
|
+
if (options.json) {
|
|
582
|
+
outputJson(data);
|
|
583
|
+
}
|
|
584
|
+
else if (data.newFiles.length === 0) {
|
|
585
|
+
console.log('\nNo new code files to check.');
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
console.log(`\n\ud83d\udd0d Integration Check (base: ${options.base})\n`);
|
|
589
|
+
console.log(`New files: ${data.newFiles.length} | Unreferenced: ${data.unreferencedCount}\n`);
|
|
590
|
+
for (const file of data.newFiles) {
|
|
591
|
+
const status = file.isIntegrated ? '\u2705' : '\u26a0\ufe0f';
|
|
592
|
+
console.log(`${status} ${file.path}`);
|
|
593
|
+
if (file.isIntegrated) {
|
|
594
|
+
console.log(` Referenced by: ${file.referencedBy.join(', ')}`);
|
|
595
|
+
}
|
|
596
|
+
else {
|
|
597
|
+
console.log(' Not referenced by any file');
|
|
598
|
+
if (file.suggestedEntryPoints && file.suggestedEntryPoints.length > 0) {
|
|
599
|
+
console.log(` Suggested entry points: ${file.suggestedEntryPoints.join(', ')}`);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
catch (err) {
|
|
606
|
+
handleCommandError(err, options.json);
|
|
607
|
+
}
|
|
220
608
|
});
|
|
221
609
|
// Local repos command (#84)
|
|
222
610
|
program
|
|
@@ -226,8 +614,24 @@ program
|
|
|
226
614
|
.option('--paths <dirs...>', 'Directories to scan')
|
|
227
615
|
.option('--json', 'Output as JSON')
|
|
228
616
|
.action(async (options) => {
|
|
229
|
-
|
|
230
|
-
|
|
617
|
+
try {
|
|
618
|
+
const { runLocalRepos } = await import('./commands/local-repos.js');
|
|
619
|
+
const data = await runLocalRepos({ scan: options.scan, paths: options.paths });
|
|
620
|
+
if (options.json) {
|
|
621
|
+
outputJson(data);
|
|
622
|
+
}
|
|
623
|
+
else if (data.fromCache) {
|
|
624
|
+
console.log(`\n\ud83d\udcc1 Local Repos (cached ${data.cachedAt})\n`);
|
|
625
|
+
printRepos(data.repos);
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
console.log(`Found ${Object.keys(data.repos).length} repos:\n`);
|
|
629
|
+
printRepos(data.repos);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
catch (err) {
|
|
633
|
+
handleCommandError(err, options.json);
|
|
634
|
+
}
|
|
231
635
|
});
|
|
232
636
|
// Startup command (combines auth, setup, daily, dashboard, issue list)
|
|
233
637
|
program
|
|
@@ -235,8 +639,30 @@ program
|
|
|
235
639
|
.description('Run all pre-flight checks and daily fetch in one call')
|
|
236
640
|
.option('--json', 'Output as JSON')
|
|
237
641
|
.action(async (options) => {
|
|
238
|
-
|
|
239
|
-
|
|
642
|
+
try {
|
|
643
|
+
const { runStartup } = await import('./commands/startup.js');
|
|
644
|
+
const data = await runStartup();
|
|
645
|
+
if (options.json) {
|
|
646
|
+
outputJson(data);
|
|
647
|
+
}
|
|
648
|
+
else {
|
|
649
|
+
if (!data.setupComplete) {
|
|
650
|
+
console.log('Setup incomplete. Run /setup-oss first.');
|
|
651
|
+
}
|
|
652
|
+
else if (data.authError) {
|
|
653
|
+
console.error(`Error: ${data.authError}`);
|
|
654
|
+
}
|
|
655
|
+
else {
|
|
656
|
+
console.log(`OSS Autopilot v${data.version}`);
|
|
657
|
+
console.log(data.daily?.briefSummary ?? '');
|
|
658
|
+
if (data.dashboardPath)
|
|
659
|
+
console.log(`Dashboard: ${data.dashboardPath}`);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
catch (err) {
|
|
664
|
+
handleCommandError(err, options.json);
|
|
665
|
+
}
|
|
240
666
|
});
|
|
241
667
|
// Shelve command
|
|
242
668
|
program
|
|
@@ -244,8 +670,24 @@ program
|
|
|
244
670
|
.description('Shelve a PR (exclude from capacity and actionable issues)')
|
|
245
671
|
.option('--json', 'Output as JSON')
|
|
246
672
|
.action(async (prUrl, options) => {
|
|
247
|
-
|
|
248
|
-
|
|
673
|
+
try {
|
|
674
|
+
const { runShelve } = await import('./commands/shelve.js');
|
|
675
|
+
const data = await runShelve({ prUrl });
|
|
676
|
+
if (options.json) {
|
|
677
|
+
outputJson(data);
|
|
678
|
+
}
|
|
679
|
+
else if (data.shelved) {
|
|
680
|
+
console.log(`Shelved: ${prUrl}`);
|
|
681
|
+
console.log('This PR is now excluded from capacity and actionable issues.');
|
|
682
|
+
console.log('It will auto-unshelve if a maintainer engages.');
|
|
683
|
+
}
|
|
684
|
+
else {
|
|
685
|
+
console.log('PR is already shelved.');
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
catch (err) {
|
|
689
|
+
handleCommandError(err, options.json);
|
|
690
|
+
}
|
|
249
691
|
});
|
|
250
692
|
// Unshelve command
|
|
251
693
|
program
|
|
@@ -253,8 +695,23 @@ program
|
|
|
253
695
|
.description('Unshelve a PR (include in capacity and actionable issues again)')
|
|
254
696
|
.option('--json', 'Output as JSON')
|
|
255
697
|
.action(async (prUrl, options) => {
|
|
256
|
-
|
|
257
|
-
|
|
698
|
+
try {
|
|
699
|
+
const { runUnshelve } = await import('./commands/shelve.js');
|
|
700
|
+
const data = await runUnshelve({ prUrl });
|
|
701
|
+
if (options.json) {
|
|
702
|
+
outputJson(data);
|
|
703
|
+
}
|
|
704
|
+
else if (data.unshelved) {
|
|
705
|
+
console.log(`Unshelved: ${prUrl}`);
|
|
706
|
+
console.log('This PR is now active again.');
|
|
707
|
+
}
|
|
708
|
+
else {
|
|
709
|
+
console.log('PR was not shelved.');
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
catch (err) {
|
|
713
|
+
handleCommandError(err, options.json);
|
|
714
|
+
}
|
|
258
715
|
});
|
|
259
716
|
// Dismiss command
|
|
260
717
|
program
|
|
@@ -262,8 +719,24 @@ program
|
|
|
262
719
|
.description('Dismiss issue reply notifications (resurfaces on new activity)')
|
|
263
720
|
.option('--json', 'Output as JSON')
|
|
264
721
|
.action(async (issueUrl, options) => {
|
|
265
|
-
|
|
266
|
-
|
|
722
|
+
try {
|
|
723
|
+
const { runDismiss } = await import('./commands/dismiss.js');
|
|
724
|
+
const data = await runDismiss({ issueUrl });
|
|
725
|
+
if (options.json) {
|
|
726
|
+
outputJson(data);
|
|
727
|
+
}
|
|
728
|
+
else if (data.dismissed) {
|
|
729
|
+
console.log(`Dismissed: ${issueUrl}`);
|
|
730
|
+
console.log('Issue reply notifications are now muted.');
|
|
731
|
+
console.log('New responses after this point will resurface automatically.');
|
|
732
|
+
}
|
|
733
|
+
else {
|
|
734
|
+
console.log('Issue is already dismissed.');
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
catch (err) {
|
|
738
|
+
handleCommandError(err, options.json);
|
|
739
|
+
}
|
|
267
740
|
});
|
|
268
741
|
// Undismiss command
|
|
269
742
|
program
|
|
@@ -271,8 +744,23 @@ program
|
|
|
271
744
|
.description('Undismiss an issue (re-enable reply notifications)')
|
|
272
745
|
.option('--json', 'Output as JSON')
|
|
273
746
|
.action(async (issueUrl, options) => {
|
|
274
|
-
|
|
275
|
-
|
|
747
|
+
try {
|
|
748
|
+
const { runUndismiss } = await import('./commands/dismiss.js');
|
|
749
|
+
const data = await runUndismiss({ issueUrl });
|
|
750
|
+
if (options.json) {
|
|
751
|
+
outputJson(data);
|
|
752
|
+
}
|
|
753
|
+
else if (data.undismissed) {
|
|
754
|
+
console.log(`Undismissed: ${issueUrl}`);
|
|
755
|
+
console.log('Issue reply notifications are active again.');
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
console.log('Issue was not dismissed.');
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
catch (err) {
|
|
762
|
+
handleCommandError(err, options.json);
|
|
763
|
+
}
|
|
276
764
|
});
|
|
277
765
|
// Snooze command
|
|
278
766
|
program
|
|
@@ -282,8 +770,29 @@ program
|
|
|
282
770
|
.option('--days <days>', 'Number of days to snooze (default: 7)', '7')
|
|
283
771
|
.option('--json', 'Output as JSON')
|
|
284
772
|
.action(async (prUrl, options) => {
|
|
285
|
-
|
|
286
|
-
|
|
773
|
+
try {
|
|
774
|
+
const { runSnooze } = await import('./commands/snooze.js');
|
|
775
|
+
const data = await runSnooze({ prUrl, reason: options.reason, days: parseInt(options.days, 10) });
|
|
776
|
+
if (options.json) {
|
|
777
|
+
outputJson(data);
|
|
778
|
+
}
|
|
779
|
+
else if (data.snoozed) {
|
|
780
|
+
console.log(`Snoozed: ${prUrl}`);
|
|
781
|
+
console.log(`Reason: ${data.reason}`);
|
|
782
|
+
console.log(`Duration: ${data.days} day${data.days === 1 ? '' : 's'}`);
|
|
783
|
+
console.log(`Expires: ${data.expiresAt ? new Date(data.expiresAt).toLocaleString() : 'unknown'}`);
|
|
784
|
+
console.log('CI failure notifications are now muted for this PR.');
|
|
785
|
+
}
|
|
786
|
+
else {
|
|
787
|
+
console.log('PR is already snoozed.');
|
|
788
|
+
if (data.expiresAt) {
|
|
789
|
+
console.log(`Expires: ${new Date(data.expiresAt).toLocaleString()}`);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
catch (err) {
|
|
794
|
+
handleCommandError(err, options.json);
|
|
795
|
+
}
|
|
287
796
|
});
|
|
288
797
|
// Unsnooze command
|
|
289
798
|
program
|
|
@@ -291,8 +800,23 @@ program
|
|
|
291
800
|
.description('Unsnooze a PR (re-enable CI failure notifications)')
|
|
292
801
|
.option('--json', 'Output as JSON')
|
|
293
802
|
.action(async (prUrl, options) => {
|
|
294
|
-
|
|
295
|
-
|
|
803
|
+
try {
|
|
804
|
+
const { runUnsnooze } = await import('./commands/snooze.js');
|
|
805
|
+
const data = await runUnsnooze({ prUrl });
|
|
806
|
+
if (options.json) {
|
|
807
|
+
outputJson(data);
|
|
808
|
+
}
|
|
809
|
+
else if (data.unsnoozed) {
|
|
810
|
+
console.log(`Unsnoozed: ${prUrl}`);
|
|
811
|
+
console.log('CI failure notifications are active again for this PR.');
|
|
812
|
+
}
|
|
813
|
+
else {
|
|
814
|
+
console.log('PR was not snoozed.');
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
catch (err) {
|
|
818
|
+
handleCommandError(err, options.json);
|
|
819
|
+
}
|
|
296
820
|
});
|
|
297
821
|
// Validate GitHub token before running commands that need it
|
|
298
822
|
program.hook('preAction', async (thisCommand, actionCommand) => {
|