@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.bundle.cjs
CHANGED
|
@@ -1196,8 +1196,8 @@ var require_command = __commonJS({
|
|
|
1196
1196
|
"../../node_modules/.pnpm/commander@14.0.3/node_modules/commander/lib/command.js"(exports2) {
|
|
1197
1197
|
var EventEmitter = require("node:events").EventEmitter;
|
|
1198
1198
|
var childProcess = require("node:child_process");
|
|
1199
|
-
var
|
|
1200
|
-
var
|
|
1199
|
+
var path10 = require("node:path");
|
|
1200
|
+
var fs10 = require("node:fs");
|
|
1201
1201
|
var process2 = require("node:process");
|
|
1202
1202
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
1203
1203
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -2191,7 +2191,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2191
2191
|
* @param {string} subcommandName
|
|
2192
2192
|
*/
|
|
2193
2193
|
_checkForMissingExecutable(executableFile, executableDir, subcommandName) {
|
|
2194
|
-
if (
|
|
2194
|
+
if (fs10.existsSync(executableFile)) return;
|
|
2195
2195
|
const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
|
|
2196
2196
|
const executableMissing = `'${executableFile}' does not exist
|
|
2197
2197
|
- if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
|
|
@@ -2209,11 +2209,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2209
2209
|
let launchWithNode = false;
|
|
2210
2210
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
2211
2211
|
function findFile(baseDir, baseName) {
|
|
2212
|
-
const localBin =
|
|
2213
|
-
if (
|
|
2214
|
-
if (sourceExt.includes(
|
|
2212
|
+
const localBin = path10.resolve(baseDir, baseName);
|
|
2213
|
+
if (fs10.existsSync(localBin)) return localBin;
|
|
2214
|
+
if (sourceExt.includes(path10.extname(baseName))) return void 0;
|
|
2215
2215
|
const foundExt = sourceExt.find(
|
|
2216
|
-
(ext) =>
|
|
2216
|
+
(ext) => fs10.existsSync(`${localBin}${ext}`)
|
|
2217
2217
|
);
|
|
2218
2218
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
2219
2219
|
return void 0;
|
|
@@ -2225,21 +2225,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2225
2225
|
if (this._scriptPath) {
|
|
2226
2226
|
let resolvedScriptPath;
|
|
2227
2227
|
try {
|
|
2228
|
-
resolvedScriptPath =
|
|
2228
|
+
resolvedScriptPath = fs10.realpathSync(this._scriptPath);
|
|
2229
2229
|
} catch {
|
|
2230
2230
|
resolvedScriptPath = this._scriptPath;
|
|
2231
2231
|
}
|
|
2232
|
-
executableDir =
|
|
2233
|
-
|
|
2232
|
+
executableDir = path10.resolve(
|
|
2233
|
+
path10.dirname(resolvedScriptPath),
|
|
2234
2234
|
executableDir
|
|
2235
2235
|
);
|
|
2236
2236
|
}
|
|
2237
2237
|
if (executableDir) {
|
|
2238
2238
|
let localFile = findFile(executableDir, executableFile);
|
|
2239
2239
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
2240
|
-
const legacyName =
|
|
2240
|
+
const legacyName = path10.basename(
|
|
2241
2241
|
this._scriptPath,
|
|
2242
|
-
|
|
2242
|
+
path10.extname(this._scriptPath)
|
|
2243
2243
|
);
|
|
2244
2244
|
if (legacyName !== this._name) {
|
|
2245
2245
|
localFile = findFile(
|
|
@@ -2250,7 +2250,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2250
2250
|
}
|
|
2251
2251
|
executableFile = localFile || executableFile;
|
|
2252
2252
|
}
|
|
2253
|
-
launchWithNode = sourceExt.includes(
|
|
2253
|
+
launchWithNode = sourceExt.includes(path10.extname(executableFile));
|
|
2254
2254
|
let proc;
|
|
2255
2255
|
if (process2.platform !== "win32") {
|
|
2256
2256
|
if (launchWithNode) {
|
|
@@ -3165,7 +3165,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3165
3165
|
* @return {Command}
|
|
3166
3166
|
*/
|
|
3167
3167
|
nameFromFilename(filename) {
|
|
3168
|
-
this._name =
|
|
3168
|
+
this._name = path10.basename(filename, path10.extname(filename));
|
|
3169
3169
|
return this;
|
|
3170
3170
|
}
|
|
3171
3171
|
/**
|
|
@@ -3179,9 +3179,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3179
3179
|
* @param {string} [path]
|
|
3180
3180
|
* @return {(string|null|Command)}
|
|
3181
3181
|
*/
|
|
3182
|
-
executableDir(
|
|
3183
|
-
if (
|
|
3184
|
-
this._executableDir =
|
|
3182
|
+
executableDir(path11) {
|
|
3183
|
+
if (path11 === void 0) return this._executableDir;
|
|
3184
|
+
this._executableDir = path11;
|
|
3185
3185
|
return this;
|
|
3186
3186
|
}
|
|
3187
3187
|
/**
|
|
@@ -3498,6 +3498,16 @@ var init_types = __esm({
|
|
|
3498
3498
|
});
|
|
3499
3499
|
|
|
3500
3500
|
// src/core/errors.ts
|
|
3501
|
+
function errorMessage(e) {
|
|
3502
|
+
return e instanceof Error ? e.message : String(e);
|
|
3503
|
+
}
|
|
3504
|
+
function getHttpStatusCode(error) {
|
|
3505
|
+
if (error && typeof error === "object" && "status" in error) {
|
|
3506
|
+
const status = error.status;
|
|
3507
|
+
return typeof status === "number" && Number.isFinite(status) ? status : void 0;
|
|
3508
|
+
}
|
|
3509
|
+
return void 0;
|
|
3510
|
+
}
|
|
3501
3511
|
var OssAutopilotError, ConfigurationError, ValidationError;
|
|
3502
3512
|
var init_errors = __esm({
|
|
3503
3513
|
"src/core/errors.ts"() {
|
|
@@ -3640,6 +3650,17 @@ function splitRepo(repoFullName) {
|
|
|
3640
3650
|
const [owner, repo] = repoFullName.split("/");
|
|
3641
3651
|
return { owner, repo };
|
|
3642
3652
|
}
|
|
3653
|
+
function isOwnRepo(owner, username) {
|
|
3654
|
+
return owner.toLowerCase() === username.toLowerCase();
|
|
3655
|
+
}
|
|
3656
|
+
function getCLIVersion() {
|
|
3657
|
+
try {
|
|
3658
|
+
const pkgPath = path.join(path.dirname(process.argv[1]), "..", "package.json");
|
|
3659
|
+
return JSON.parse(fs.readFileSync(pkgPath, "utf-8")).version;
|
|
3660
|
+
} catch {
|
|
3661
|
+
return "0.0.0";
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3643
3664
|
function formatRelativeTime(dateStr) {
|
|
3644
3665
|
const date = new Date(dateStr);
|
|
3645
3666
|
const diffMs = Date.now() - date.getTime();
|
|
@@ -3681,6 +3702,15 @@ function getGitHubToken() {
|
|
|
3681
3702
|
}
|
|
3682
3703
|
return null;
|
|
3683
3704
|
}
|
|
3705
|
+
function requireGitHubToken() {
|
|
3706
|
+
const token = getGitHubToken();
|
|
3707
|
+
if (!token) {
|
|
3708
|
+
throw new ConfigurationError(
|
|
3709
|
+
"GitHub authentication required.\n\nOptions:\n 1. Use gh CLI: gh auth login\n 2. Set GITHUB_TOKEN environment variable\n\nThe gh CLI is recommended - install from https://cli.github.com"
|
|
3710
|
+
);
|
|
3711
|
+
}
|
|
3712
|
+
return token;
|
|
3713
|
+
}
|
|
3684
3714
|
async function getGitHubTokenAsync() {
|
|
3685
3715
|
if (cachedGitHubToken) {
|
|
3686
3716
|
return cachedGitHubToken;
|
|
@@ -3694,12 +3724,12 @@ async function getGitHubTokenAsync() {
|
|
|
3694
3724
|
return cachedGitHubToken;
|
|
3695
3725
|
}
|
|
3696
3726
|
try {
|
|
3697
|
-
const token = await new Promise((
|
|
3727
|
+
const token = await new Promise((resolve5, reject) => {
|
|
3698
3728
|
(0, import_child_process.execFile)("gh", ["auth", "token"], { encoding: "utf-8", timeout: 2e3 }, (error, stdout) => {
|
|
3699
3729
|
if (error) {
|
|
3700
3730
|
reject(error);
|
|
3701
3731
|
} else {
|
|
3702
|
-
|
|
3732
|
+
resolve5(stdout.trim());
|
|
3703
3733
|
}
|
|
3704
3734
|
});
|
|
3705
3735
|
});
|
|
@@ -3951,8 +3981,7 @@ var init_state = __esm({
|
|
|
3951
3981
|
debug(MODULE2, "Migration complete!");
|
|
3952
3982
|
return true;
|
|
3953
3983
|
} catch (error) {
|
|
3954
|
-
|
|
3955
|
-
warn(MODULE2, `Failed to migrate state: ${errorMessage}`);
|
|
3984
|
+
warn(MODULE2, `Failed to migrate state: ${errorMessage(error)}`);
|
|
3956
3985
|
const newStatePath2 = getStatePath();
|
|
3957
3986
|
if (fs2.existsSync(newStatePath2) && fs2.existsSync(LEGACY_STATE_FILE)) {
|
|
3958
3987
|
try {
|
|
@@ -4104,11 +4133,11 @@ var init_state = __esm({
|
|
|
4104
4133
|
try {
|
|
4105
4134
|
fs2.unlinkSync(path2.join(backupDir, file));
|
|
4106
4135
|
} catch (error) {
|
|
4107
|
-
warn(MODULE2, `Could not delete old backup ${file}:`, error
|
|
4136
|
+
warn(MODULE2, `Could not delete old backup ${file}:`, errorMessage(error));
|
|
4108
4137
|
}
|
|
4109
4138
|
}
|
|
4110
4139
|
} catch (error) {
|
|
4111
|
-
warn(MODULE2, "Could not clean up backups:", error
|
|
4140
|
+
warn(MODULE2, "Could not clean up backups:", errorMessage(error));
|
|
4112
4141
|
}
|
|
4113
4142
|
}
|
|
4114
4143
|
/**
|
|
@@ -5891,17 +5920,17 @@ function requestLog(octokit) {
|
|
|
5891
5920
|
octokit.log.debug("request", options);
|
|
5892
5921
|
const start = Date.now();
|
|
5893
5922
|
const requestOptions = octokit.request.endpoint.parse(options);
|
|
5894
|
-
const
|
|
5923
|
+
const path10 = requestOptions.url.replace(options.baseUrl, "");
|
|
5895
5924
|
return request2(options).then((response) => {
|
|
5896
5925
|
const requestId = response.headers["x-github-request-id"];
|
|
5897
5926
|
octokit.log.info(
|
|
5898
|
-
`${requestOptions.method} ${
|
|
5927
|
+
`${requestOptions.method} ${path10} - ${response.status} with id ${requestId} in ${Date.now() - start}ms`
|
|
5899
5928
|
);
|
|
5900
5929
|
return response;
|
|
5901
5930
|
}).catch((error) => {
|
|
5902
5931
|
const requestId = error.response?.headers["x-github-request-id"] || "UNKNOWN";
|
|
5903
5932
|
octokit.log.error(
|
|
5904
|
-
`${requestOptions.method} ${
|
|
5933
|
+
`${requestOptions.method} ${path10} - ${error.status} with id ${requestId} in ${Date.now() - start}ms`
|
|
5905
5934
|
);
|
|
5906
5935
|
throw error;
|
|
5907
5936
|
});
|
|
@@ -8970,8 +8999,8 @@ var require_light = __commonJS({
|
|
|
8970
8999
|
return this.Promise.resolve();
|
|
8971
9000
|
}
|
|
8972
9001
|
yieldLoop(t = 0) {
|
|
8973
|
-
return new this.Promise(function(
|
|
8974
|
-
return setTimeout(
|
|
9002
|
+
return new this.Promise(function(resolve5, reject) {
|
|
9003
|
+
return setTimeout(resolve5, t);
|
|
8975
9004
|
});
|
|
8976
9005
|
}
|
|
8977
9006
|
computePenalty() {
|
|
@@ -9182,15 +9211,15 @@ var require_light = __commonJS({
|
|
|
9182
9211
|
return this._queue.length === 0;
|
|
9183
9212
|
}
|
|
9184
9213
|
async _tryToRun() {
|
|
9185
|
-
var args, cb, error, reject,
|
|
9214
|
+
var args, cb, error, reject, resolve5, returned, task;
|
|
9186
9215
|
if (this._running < 1 && this._queue.length > 0) {
|
|
9187
9216
|
this._running++;
|
|
9188
|
-
({ task, args, resolve:
|
|
9217
|
+
({ task, args, resolve: resolve5, reject } = this._queue.shift());
|
|
9189
9218
|
cb = await (async function() {
|
|
9190
9219
|
try {
|
|
9191
9220
|
returned = await task(...args);
|
|
9192
9221
|
return function() {
|
|
9193
|
-
return
|
|
9222
|
+
return resolve5(returned);
|
|
9194
9223
|
};
|
|
9195
9224
|
} catch (error1) {
|
|
9196
9225
|
error = error1;
|
|
@@ -9205,13 +9234,13 @@ var require_light = __commonJS({
|
|
|
9205
9234
|
}
|
|
9206
9235
|
}
|
|
9207
9236
|
schedule(task, ...args) {
|
|
9208
|
-
var promise, reject,
|
|
9209
|
-
|
|
9237
|
+
var promise, reject, resolve5;
|
|
9238
|
+
resolve5 = reject = null;
|
|
9210
9239
|
promise = new this.Promise(function(_resolve, _reject) {
|
|
9211
|
-
|
|
9240
|
+
resolve5 = _resolve;
|
|
9212
9241
|
return reject = _reject;
|
|
9213
9242
|
});
|
|
9214
|
-
this._queue.push({ task, args, resolve:
|
|
9243
|
+
this._queue.push({ task, args, resolve: resolve5, reject });
|
|
9215
9244
|
this._tryToRun();
|
|
9216
9245
|
return promise;
|
|
9217
9246
|
}
|
|
@@ -9612,14 +9641,14 @@ var require_light = __commonJS({
|
|
|
9612
9641
|
counts = this._states.counts;
|
|
9613
9642
|
return counts[0] + counts[1] + counts[2] + counts[3] === at;
|
|
9614
9643
|
};
|
|
9615
|
-
return new this.Promise((
|
|
9644
|
+
return new this.Promise((resolve5, reject) => {
|
|
9616
9645
|
if (finished()) {
|
|
9617
|
-
return
|
|
9646
|
+
return resolve5();
|
|
9618
9647
|
} else {
|
|
9619
9648
|
return this.on("done", () => {
|
|
9620
9649
|
if (finished()) {
|
|
9621
9650
|
this.removeAllListeners("done");
|
|
9622
|
-
return
|
|
9651
|
+
return resolve5();
|
|
9623
9652
|
}
|
|
9624
9653
|
});
|
|
9625
9654
|
}
|
|
@@ -9712,9 +9741,9 @@ var require_light = __commonJS({
|
|
|
9712
9741
|
options = parser$5.load(options, this.jobDefaults);
|
|
9713
9742
|
}
|
|
9714
9743
|
task = (...args2) => {
|
|
9715
|
-
return new this.Promise(function(
|
|
9744
|
+
return new this.Promise(function(resolve5, reject) {
|
|
9716
9745
|
return fn(...args2, function(...args3) {
|
|
9717
|
-
return (args3[0] != null ? reject :
|
|
9746
|
+
return (args3[0] != null ? reject : resolve5)(args3);
|
|
9718
9747
|
});
|
|
9719
9748
|
});
|
|
9720
9749
|
};
|
|
@@ -9882,7 +9911,7 @@ function isAuthRequest(method, pathname) {
|
|
|
9882
9911
|
}
|
|
9883
9912
|
function routeMatcher(paths) {
|
|
9884
9913
|
const regexes = paths.map(
|
|
9885
|
-
(
|
|
9914
|
+
(path10) => path10.split("/").map((c) => c.startsWith("{") ? "(?:.+?)" : c).join("/")
|
|
9886
9915
|
);
|
|
9887
9916
|
const regex2 = `^(?:${regexes.map((r) => `(?:${r})`).join("|")})[^/]*$`;
|
|
9888
9917
|
return new RegExp(regex2, "i");
|
|
@@ -10210,10 +10239,7 @@ async function cachedRequest(cache, url, fetcher) {
|
|
|
10210
10239
|
}
|
|
10211
10240
|
}
|
|
10212
10241
|
function isNotModifiedError(err) {
|
|
10213
|
-
|
|
10214
|
-
return err.status === 304;
|
|
10215
|
-
}
|
|
10216
|
-
return false;
|
|
10242
|
+
return getHttpStatusCode(err) === 304;
|
|
10217
10243
|
}
|
|
10218
10244
|
var fs3, path3, crypto, MODULE4, DEFAULT_MAX_AGE_MS, HttpCache, _httpCache;
|
|
10219
10245
|
var init_http_cache = __esm({
|
|
@@ -10224,6 +10250,7 @@ var init_http_cache = __esm({
|
|
|
10224
10250
|
crypto = __toESM(require("crypto"), 1);
|
|
10225
10251
|
init_utils();
|
|
10226
10252
|
init_logger();
|
|
10253
|
+
init_errors();
|
|
10227
10254
|
MODULE4 = "http-cache";
|
|
10228
10255
|
DEFAULT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
10229
10256
|
HttpCache = class {
|
|
@@ -10241,6 +10268,18 @@ var init_http_cache = __esm({
|
|
|
10241
10268
|
pathFor(url) {
|
|
10242
10269
|
return path3.join(this.cacheDir, `${this.keyFor(url)}.json`);
|
|
10243
10270
|
}
|
|
10271
|
+
/**
|
|
10272
|
+
* Return the cached body if the entry exists and is younger than `maxAgeMs`.
|
|
10273
|
+
* Useful for time-based caching where ETag validation isn't applicable
|
|
10274
|
+
* (e.g., caching aggregated results from paginated API calls).
|
|
10275
|
+
*/
|
|
10276
|
+
getIfFresh(key, maxAgeMs) {
|
|
10277
|
+
const entry = this.get(key);
|
|
10278
|
+
if (!entry) return null;
|
|
10279
|
+
const age = Date.now() - new Date(entry.cachedAt).getTime();
|
|
10280
|
+
if (!Number.isFinite(age) || age < 0 || age > maxAgeMs) return null;
|
|
10281
|
+
return entry.body;
|
|
10282
|
+
}
|
|
10244
10283
|
/**
|
|
10245
10284
|
* Look up a cached response. Returns `null` if no cache entry exists.
|
|
10246
10285
|
*/
|
|
@@ -10804,11 +10843,28 @@ var init_display_utils = __esm({
|
|
|
10804
10843
|
});
|
|
10805
10844
|
|
|
10806
10845
|
// src/core/github-stats.ts
|
|
10807
|
-
|
|
10846
|
+
function isCachedPRCounts(v) {
|
|
10847
|
+
if (typeof v !== "object" || v === null) return false;
|
|
10848
|
+
const obj = v;
|
|
10849
|
+
return Array.isArray(obj.reposEntries) && typeof obj.monthlyCounts === "object" && obj.monthlyCounts !== null && typeof obj.monthlyOpenedCounts === "object" && obj.monthlyOpenedCounts !== null && typeof obj.dailyActivityCounts === "object" && obj.dailyActivityCounts !== null;
|
|
10850
|
+
}
|
|
10851
|
+
async function fetchUserPRCounts(octokit, githubUsername, query, label, accumulateRepo) {
|
|
10808
10852
|
if (!githubUsername) {
|
|
10809
10853
|
return { repos: /* @__PURE__ */ new Map(), monthlyCounts: {}, monthlyOpenedCounts: {}, dailyActivityCounts: {} };
|
|
10810
10854
|
}
|
|
10811
|
-
|
|
10855
|
+
const cache = getHttpCache();
|
|
10856
|
+
const cacheKey = `pr-counts:${label}:${githubUsername}`;
|
|
10857
|
+
const cached = cache.getIfFresh(cacheKey, PR_COUNTS_CACHE_TTL_MS);
|
|
10858
|
+
if (cached && isCachedPRCounts(cached)) {
|
|
10859
|
+
debug(MODULE6, `Using cached ${label} PR counts for @${githubUsername}`);
|
|
10860
|
+
return {
|
|
10861
|
+
repos: new Map(cached.reposEntries),
|
|
10862
|
+
monthlyCounts: cached.monthlyCounts,
|
|
10863
|
+
monthlyOpenedCounts: cached.monthlyOpenedCounts,
|
|
10864
|
+
dailyActivityCounts: cached.dailyActivityCounts
|
|
10865
|
+
};
|
|
10866
|
+
}
|
|
10867
|
+
debug(MODULE6, `Fetching ${label} PR counts for @${githubUsername}...`);
|
|
10812
10868
|
const repos = /* @__PURE__ */ new Map();
|
|
10813
10869
|
const monthlyCounts = {};
|
|
10814
10870
|
const monthlyOpenedCounts = {};
|
|
@@ -10817,7 +10873,7 @@ async function fetchUserMergedPRCounts(octokit, githubUsername) {
|
|
|
10817
10873
|
let fetched = 0;
|
|
10818
10874
|
while (true) {
|
|
10819
10875
|
const { data } = await octokit.search.issuesAndPullRequests({
|
|
10820
|
-
q: `is:pr
|
|
10876
|
+
q: `is:pr ${query} author:${githubUsername}`,
|
|
10821
10877
|
sort: "updated",
|
|
10822
10878
|
order: "desc",
|
|
10823
10879
|
per_page: 100,
|
|
@@ -10826,25 +10882,18 @@ async function fetchUserMergedPRCounts(octokit, githubUsername) {
|
|
|
10826
10882
|
for (const item of data.items) {
|
|
10827
10883
|
const parsed = extractOwnerRepo(item.html_url);
|
|
10828
10884
|
if (!parsed) {
|
|
10829
|
-
warn(MODULE6, `Skipping
|
|
10885
|
+
warn(MODULE6, `Skipping ${label} PR with unparseable URL: ${item.html_url}`);
|
|
10830
10886
|
continue;
|
|
10831
10887
|
}
|
|
10832
10888
|
const { owner } = parsed;
|
|
10833
10889
|
const repo = `${owner}/${parsed.repo}`;
|
|
10834
|
-
if (owner
|
|
10835
|
-
const
|
|
10836
|
-
|
|
10837
|
-
|
|
10838
|
-
existing.count += 1;
|
|
10839
|
-
if (mergedAt && mergedAt > existing.lastMergedAt) {
|
|
10840
|
-
existing.lastMergedAt = mergedAt;
|
|
10841
|
-
}
|
|
10842
|
-
} else {
|
|
10843
|
-
repos.set(repo, { count: 1, lastMergedAt: mergedAt });
|
|
10844
|
-
}
|
|
10845
|
-
if (mergedAt) {
|
|
10846
|
-
const month = mergedAt.slice(0, 7);
|
|
10890
|
+
if (isOwnRepo(owner, githubUsername)) continue;
|
|
10891
|
+
const primaryDate = accumulateRepo(repos, repo, item);
|
|
10892
|
+
if (primaryDate) {
|
|
10893
|
+
const month = primaryDate.slice(0, 7);
|
|
10847
10894
|
monthlyCounts[month] = (monthlyCounts[month] || 0) + 1;
|
|
10895
|
+
const day = primaryDate.slice(0, 10);
|
|
10896
|
+
if (day.length === 10) dailyActivityCounts[day] = (dailyActivityCounts[day] || 0) + 1;
|
|
10848
10897
|
}
|
|
10849
10898
|
if (item.created_at) {
|
|
10850
10899
|
const openedMonth = item.created_at.slice(0, 7);
|
|
@@ -10852,10 +10901,6 @@ async function fetchUserMergedPRCounts(octokit, githubUsername) {
|
|
|
10852
10901
|
const openedDay = item.created_at.slice(0, 10);
|
|
10853
10902
|
if (openedDay.length === 10) dailyActivityCounts[openedDay] = (dailyActivityCounts[openedDay] || 0) + 1;
|
|
10854
10903
|
}
|
|
10855
|
-
if (mergedAt) {
|
|
10856
|
-
const mergedDay = mergedAt.slice(0, 10);
|
|
10857
|
-
if (mergedDay.length === 10) dailyActivityCounts[mergedDay] = (dailyActivityCounts[mergedDay] || 0) + 1;
|
|
10858
|
-
}
|
|
10859
10904
|
}
|
|
10860
10905
|
fetched += data.items.length;
|
|
10861
10906
|
if (fetched >= data.total_count || fetched >= 1e3 || data.items.length === 0) {
|
|
@@ -10863,59 +10908,41 @@ async function fetchUserMergedPRCounts(octokit, githubUsername) {
|
|
|
10863
10908
|
}
|
|
10864
10909
|
page++;
|
|
10865
10910
|
}
|
|
10866
|
-
debug(MODULE6, `Found ${fetched}
|
|
10911
|
+
debug(MODULE6, `Found ${fetched} ${label} PRs across ${repos.size} repos`);
|
|
10912
|
+
cache.set(cacheKey, "", {
|
|
10913
|
+
reposEntries: Array.from(repos.entries()),
|
|
10914
|
+
monthlyCounts,
|
|
10915
|
+
monthlyOpenedCounts,
|
|
10916
|
+
dailyActivityCounts
|
|
10917
|
+
});
|
|
10867
10918
|
return { repos, monthlyCounts, monthlyOpenedCounts, dailyActivityCounts };
|
|
10868
10919
|
}
|
|
10869
|
-
|
|
10870
|
-
|
|
10871
|
-
|
|
10872
|
-
|
|
10873
|
-
|
|
10874
|
-
|
|
10875
|
-
|
|
10876
|
-
const monthlyOpenedCounts = {};
|
|
10877
|
-
const dailyActivityCounts = {};
|
|
10878
|
-
let page = 1;
|
|
10879
|
-
let fetched = 0;
|
|
10880
|
-
while (true) {
|
|
10881
|
-
const { data } = await octokit.search.issuesAndPullRequests({
|
|
10882
|
-
q: `is:pr is:closed is:unmerged author:${githubUsername}`,
|
|
10883
|
-
sort: "updated",
|
|
10884
|
-
order: "desc",
|
|
10885
|
-
per_page: 100,
|
|
10886
|
-
page
|
|
10887
|
-
});
|
|
10888
|
-
for (const item of data.items) {
|
|
10889
|
-
const parsed = extractOwnerRepo(item.html_url);
|
|
10890
|
-
if (!parsed) {
|
|
10891
|
-
warn(MODULE6, `Skipping closed PR with unparseable URL: ${item.html_url}`);
|
|
10892
|
-
continue;
|
|
10893
|
-
}
|
|
10894
|
-
const { owner } = parsed;
|
|
10895
|
-
const repo = `${owner}/${parsed.repo}`;
|
|
10896
|
-
if (owner.toLowerCase() === githubUsername.toLowerCase()) continue;
|
|
10897
|
-
repos.set(repo, (repos.get(repo) || 0) + 1);
|
|
10898
|
-
if (item.closed_at) {
|
|
10899
|
-
const closedMonth = item.closed_at.slice(0, 7);
|
|
10900
|
-
monthlyCounts[closedMonth] = (monthlyCounts[closedMonth] || 0) + 1;
|
|
10901
|
-
const closedDay = item.closed_at.slice(0, 10);
|
|
10902
|
-
if (closedDay.length === 10) dailyActivityCounts[closedDay] = (dailyActivityCounts[closedDay] || 0) + 1;
|
|
10903
|
-
}
|
|
10904
|
-
if (item.created_at) {
|
|
10905
|
-
const openedMonth = item.created_at.slice(0, 7);
|
|
10906
|
-
monthlyOpenedCounts[openedMonth] = (monthlyOpenedCounts[openedMonth] || 0) + 1;
|
|
10907
|
-
const openedDay = item.created_at.slice(0, 10);
|
|
10908
|
-
if (openedDay.length === 10) dailyActivityCounts[openedDay] = (dailyActivityCounts[openedDay] || 0) + 1;
|
|
10909
|
-
}
|
|
10920
|
+
function fetchUserMergedPRCounts(octokit, githubUsername) {
|
|
10921
|
+
return fetchUserPRCounts(octokit, githubUsername, "is:merged", "merged", (repos, repo, item) => {
|
|
10922
|
+
if (!item.pull_request?.merged_at) {
|
|
10923
|
+
warn(
|
|
10924
|
+
MODULE6,
|
|
10925
|
+
`merged_at missing for merged PR ${item.html_url}${item.closed_at ? ", falling back to closed_at" : ", no date available"}`
|
|
10926
|
+
);
|
|
10910
10927
|
}
|
|
10911
|
-
|
|
10912
|
-
|
|
10913
|
-
|
|
10928
|
+
const mergedAt = item.pull_request?.merged_at || item.closed_at || "";
|
|
10929
|
+
const existing = repos.get(repo);
|
|
10930
|
+
if (existing) {
|
|
10931
|
+
existing.count += 1;
|
|
10932
|
+
if (mergedAt && mergedAt > existing.lastMergedAt) {
|
|
10933
|
+
existing.lastMergedAt = mergedAt;
|
|
10934
|
+
}
|
|
10935
|
+
} else {
|
|
10936
|
+
repos.set(repo, { count: 1, lastMergedAt: mergedAt });
|
|
10914
10937
|
}
|
|
10915
|
-
|
|
10916
|
-
}
|
|
10917
|
-
|
|
10918
|
-
|
|
10938
|
+
return mergedAt;
|
|
10939
|
+
});
|
|
10940
|
+
}
|
|
10941
|
+
function fetchUserClosedPRCounts(octokit, githubUsername) {
|
|
10942
|
+
return fetchUserPRCounts(octokit, githubUsername, "is:closed is:unmerged", "closed", (repos, repo, item) => {
|
|
10943
|
+
repos.set(repo, (repos.get(repo) || 0) + 1);
|
|
10944
|
+
return item.closed_at || "";
|
|
10945
|
+
});
|
|
10919
10946
|
}
|
|
10920
10947
|
async function fetchRecentPRs(octokit, config, query, label, days, mapItem) {
|
|
10921
10948
|
if (!config.githubUsername) {
|
|
@@ -10940,7 +10967,7 @@ async function fetchRecentPRs(octokit, config, query, label, days, mapItem) {
|
|
|
10940
10967
|
continue;
|
|
10941
10968
|
}
|
|
10942
10969
|
const repo = `${parsed.owner}/${parsed.repo}`;
|
|
10943
|
-
if (parsed.owner
|
|
10970
|
+
if (isOwnRepo(parsed.owner, config.githubUsername)) continue;
|
|
10944
10971
|
if (config.excludeRepos.includes(repo)) continue;
|
|
10945
10972
|
if (config.excludeOrgs?.some((org) => parsed.owner.toLowerCase() === org.toLowerCase())) continue;
|
|
10946
10973
|
results.push(mapItem(item, { owner: parsed.owner, repo, number: parsed.number }));
|
|
@@ -10989,14 +11016,15 @@ async function fetchRecentlyMergedPRs(octokit, config, days = 7) {
|
|
|
10989
11016
|
}
|
|
10990
11017
|
);
|
|
10991
11018
|
}
|
|
10992
|
-
var MODULE6;
|
|
11019
|
+
var MODULE6, PR_COUNTS_CACHE_TTL_MS;
|
|
10993
11020
|
var init_github_stats = __esm({
|
|
10994
11021
|
"src/core/github-stats.ts"() {
|
|
10995
11022
|
"use strict";
|
|
10996
11023
|
init_utils();
|
|
10997
|
-
init_errors();
|
|
10998
11024
|
init_logger();
|
|
11025
|
+
init_http_cache();
|
|
10999
11026
|
MODULE6 = "github-stats";
|
|
11027
|
+
PR_COUNTS_CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
11000
11028
|
}
|
|
11001
11029
|
});
|
|
11002
11030
|
|
|
@@ -11097,9 +11125,9 @@ var init_pr_monitor = __esm({
|
|
|
11097
11125
|
const pr = await this.fetchPRDetails(item.html_url);
|
|
11098
11126
|
if (pr) prs.push(pr);
|
|
11099
11127
|
} catch (error) {
|
|
11100
|
-
const
|
|
11101
|
-
warn("pr-monitor", `Error fetching ${item.html_url}: ${
|
|
11102
|
-
failures.push({ prUrl: item.html_url, error:
|
|
11128
|
+
const errMsg = errorMessage(error);
|
|
11129
|
+
warn("pr-monitor", `Error fetching ${item.html_url}: ${errMsg}`);
|
|
11130
|
+
failures.push({ prUrl: item.html_url, error: errMsg });
|
|
11103
11131
|
}
|
|
11104
11132
|
},
|
|
11105
11133
|
MAX_CONCURRENT_REQUESTS
|
|
@@ -11146,12 +11174,12 @@ var init_pr_monitor = __esm({
|
|
|
11146
11174
|
paginateAll(
|
|
11147
11175
|
(page) => this.octokit.pulls.listReviewComments({ owner, repo, pull_number: number, per_page: 100, page })
|
|
11148
11176
|
).catch((err) => {
|
|
11149
|
-
const status2 = err
|
|
11177
|
+
const status2 = getHttpStatusCode(err);
|
|
11150
11178
|
if (status2 === 429) {
|
|
11151
11179
|
throw err;
|
|
11152
11180
|
}
|
|
11153
11181
|
if (status2 === 403) {
|
|
11154
|
-
const msg = (err
|
|
11182
|
+
const msg = errorMessage(err).toLowerCase();
|
|
11155
11183
|
if (msg.includes("rate limit") || msg.includes("abuse detection")) {
|
|
11156
11184
|
throw err;
|
|
11157
11185
|
}
|
|
@@ -11318,7 +11346,7 @@ var init_pr_monitor = __esm({
|
|
|
11318
11346
|
this.octokit.repos.getCombinedStatusForRef({ owner, repo, ref: sha }),
|
|
11319
11347
|
// 404 is expected for repos without check runs configured; log other errors for debugging
|
|
11320
11348
|
this.octokit.checks.listForRef({ owner, repo, ref: sha }).catch((err) => {
|
|
11321
|
-
const status = err
|
|
11349
|
+
const status = getHttpStatusCode(err);
|
|
11322
11350
|
if (status === 404) {
|
|
11323
11351
|
debug("pr-monitor", `Check runs 404 for ${owner}/${repo}@${sha.slice(0, 7)} (no checks configured)`);
|
|
11324
11352
|
} else {
|
|
@@ -11344,8 +11372,8 @@ var init_pr_monitor = __esm({
|
|
|
11344
11372
|
const combinedAnalysis = analyzeCombinedStatus(combinedStatus);
|
|
11345
11373
|
return mergeStatuses(checkRunAnalysis, combinedAnalysis, checkRuns.length);
|
|
11346
11374
|
} catch (error) {
|
|
11347
|
-
const statusCode = error
|
|
11348
|
-
const
|
|
11375
|
+
const statusCode = getHttpStatusCode(error);
|
|
11376
|
+
const errMsg = errorMessage(error);
|
|
11349
11377
|
if (statusCode === 401) {
|
|
11350
11378
|
warn("pr-monitor", `CI check failed for ${owner}/${repo}: Invalid token`);
|
|
11351
11379
|
} else if (statusCode === 403) {
|
|
@@ -11354,7 +11382,7 @@ var init_pr_monitor = __esm({
|
|
|
11354
11382
|
debug("pr-monitor", `CI check 404 for ${owner}/${repo} (no CI configured)`);
|
|
11355
11383
|
return { status: "unknown", failingCheckNames: [], failingCheckConclusions: /* @__PURE__ */ new Map() };
|
|
11356
11384
|
} else {
|
|
11357
|
-
warn("pr-monitor", `Failed to check CI for ${owner}/${repo}@${sha.slice(0, 7)}: ${
|
|
11385
|
+
warn("pr-monitor", `Failed to check CI for ${owner}/${repo}@${sha.slice(0, 7)}: ${errMsg}`);
|
|
11358
11386
|
}
|
|
11359
11387
|
return { status: "unknown", failingCheckNames: [], failingCheckConclusions: /* @__PURE__ */ new Map() };
|
|
11360
11388
|
}
|
|
@@ -11415,10 +11443,7 @@ var init_pr_monitor = __esm({
|
|
|
11415
11443
|
results.set(result.value.repo, result.value.stars);
|
|
11416
11444
|
} else {
|
|
11417
11445
|
chunkFailures++;
|
|
11418
|
-
warn(
|
|
11419
|
-
MODULE7,
|
|
11420
|
-
`Failed to fetch stars for ${chunk[j]}: ${result.reason instanceof Error ? result.reason.message : result.reason}`
|
|
11421
|
-
);
|
|
11446
|
+
warn(MODULE7, `Failed to fetch stars for ${chunk[j]}: ${errorMessage(result.reason)}`);
|
|
11422
11447
|
}
|
|
11423
11448
|
}
|
|
11424
11449
|
if (chunkFailures === chunk.length && chunk.length > 0) {
|
|
@@ -11432,42 +11457,6 @@ var init_pr_monitor = __esm({
|
|
|
11432
11457
|
debug(MODULE7, `Fetched star counts for ${results.size}/${repos.length} repos`);
|
|
11433
11458
|
return results;
|
|
11434
11459
|
}
|
|
11435
|
-
/**
|
|
11436
|
-
* Shared helper: search for recent PRs and filter out own repos, excluded repos/orgs.
|
|
11437
|
-
* Returns parsed search results that pass all filters.
|
|
11438
|
-
*/
|
|
11439
|
-
async fetchRecentPRs(query, label, days, mapItem) {
|
|
11440
|
-
const config = this.stateManager.getState().config;
|
|
11441
|
-
if (!config.githubUsername) {
|
|
11442
|
-
warn(MODULE7, `Skipping recently ${label} PRs fetch: no githubUsername configured. Run /setup-oss to configure.`);
|
|
11443
|
-
return [];
|
|
11444
|
-
}
|
|
11445
|
-
const sinceDate = /* @__PURE__ */ new Date();
|
|
11446
|
-
sinceDate.setDate(sinceDate.getDate() - days);
|
|
11447
|
-
const since = sinceDate.toISOString().split("T")[0];
|
|
11448
|
-
debug(MODULE7, `Fetching recently ${label} PRs for @${config.githubUsername} (since ${since})...`);
|
|
11449
|
-
const { data } = await this.octokit.search.issuesAndPullRequests({
|
|
11450
|
-
q: query.replace("{username}", config.githubUsername).replace("{since}", since),
|
|
11451
|
-
sort: "updated",
|
|
11452
|
-
order: "desc",
|
|
11453
|
-
per_page: 100
|
|
11454
|
-
});
|
|
11455
|
-
const results = [];
|
|
11456
|
-
for (const item of data.items) {
|
|
11457
|
-
const parsed = parseGitHubUrl(item.html_url);
|
|
11458
|
-
if (!parsed) {
|
|
11459
|
-
warn(MODULE7, `Could not parse GitHub URL from API response: ${item.html_url}`);
|
|
11460
|
-
continue;
|
|
11461
|
-
}
|
|
11462
|
-
const repo = `${parsed.owner}/${parsed.repo}`;
|
|
11463
|
-
if (parsed.owner.toLowerCase() === config.githubUsername.toLowerCase()) continue;
|
|
11464
|
-
if (config.excludeRepos.includes(repo)) continue;
|
|
11465
|
-
if (config.excludeOrgs?.some((org) => parsed.owner.toLowerCase() === org.toLowerCase())) continue;
|
|
11466
|
-
results.push(mapItem(item, { owner: parsed.owner, repo, number: parsed.number }));
|
|
11467
|
-
}
|
|
11468
|
-
debug(MODULE7, `Found ${results.length} recently ${label} PRs`);
|
|
11469
|
-
return results;
|
|
11470
|
-
}
|
|
11471
11460
|
/**
|
|
11472
11461
|
* Fetch PRs closed without merge in the last N days.
|
|
11473
11462
|
* Delegates to github-stats module.
|
|
@@ -11892,7 +11881,7 @@ var init_issue_vetting = __esm({
|
|
|
11892
11881
|
if (_IssueVetter.isRateLimitError(error)) {
|
|
11893
11882
|
rateLimitFailures++;
|
|
11894
11883
|
}
|
|
11895
|
-
warn(MODULE8, `Error vetting issue ${url}:`, error
|
|
11884
|
+
warn(MODULE8, `Error vetting issue ${url}:`, errorMessage(error));
|
|
11896
11885
|
});
|
|
11897
11886
|
pending.push(task);
|
|
11898
11887
|
if (pending.length >= MAX_CONCURRENT_REQUESTS2) {
|
|
@@ -11912,10 +11901,10 @@ var init_issue_vetting = __esm({
|
|
|
11912
11901
|
}
|
|
11913
11902
|
/** Check if an error is a GitHub rate limit error (429 or rate-limit 403). */
|
|
11914
11903
|
static isRateLimitError(error) {
|
|
11915
|
-
const status = error
|
|
11904
|
+
const status = getHttpStatusCode(error);
|
|
11916
11905
|
if (status === 429) return true;
|
|
11917
11906
|
if (status === 403) {
|
|
11918
|
-
const msg =
|
|
11907
|
+
const msg = errorMessage(error).toLowerCase();
|
|
11919
11908
|
return msg.includes("rate limit");
|
|
11920
11909
|
}
|
|
11921
11910
|
return false;
|
|
@@ -11941,12 +11930,12 @@ var init_issue_vetting = __esm({
|
|
|
11941
11930
|
});
|
|
11942
11931
|
return { passed: data.total_count === 0 && linkedPRs.length === 0 };
|
|
11943
11932
|
} catch (error) {
|
|
11944
|
-
const
|
|
11933
|
+
const errMsg = errorMessage(error);
|
|
11945
11934
|
warn(
|
|
11946
11935
|
MODULE8,
|
|
11947
|
-
`Failed to check for existing PRs on ${owner}/${repo}#${issueNumber}: ${
|
|
11936
|
+
`Failed to check for existing PRs on ${owner}/${repo}#${issueNumber}: ${errMsg}. Assuming no existing PR.`
|
|
11948
11937
|
);
|
|
11949
|
-
return { passed: true, inconclusive: true, reason:
|
|
11938
|
+
return { passed: true, inconclusive: true, reason: errMsg };
|
|
11950
11939
|
}
|
|
11951
11940
|
}
|
|
11952
11941
|
/**
|
|
@@ -11962,8 +11951,8 @@ var init_issue_vetting = __esm({
|
|
|
11962
11951
|
});
|
|
11963
11952
|
return data.total_count;
|
|
11964
11953
|
} catch (error) {
|
|
11965
|
-
const
|
|
11966
|
-
warn(MODULE8, `Could not check merged PRs in ${owner}/${repo}: ${
|
|
11954
|
+
const errMsg = errorMessage(error);
|
|
11955
|
+
warn(MODULE8, `Could not check merged PRs in ${owner}/${repo}: ${errMsg}. Defaulting to 0.`);
|
|
11967
11956
|
return 0;
|
|
11968
11957
|
}
|
|
11969
11958
|
}
|
|
@@ -12006,12 +11995,9 @@ var init_issue_vetting = __esm({
|
|
|
12006
11995
|
}
|
|
12007
11996
|
return { passed: true };
|
|
12008
11997
|
} catch (error) {
|
|
12009
|
-
const
|
|
12010
|
-
warn(
|
|
12011
|
-
|
|
12012
|
-
`Failed to check claim status on ${owner}/${repo}#${issueNumber}: ${errorMessage}. Assuming not claimed.`
|
|
12013
|
-
);
|
|
12014
|
-
return { passed: true, inconclusive: true, reason: errorMessage };
|
|
11998
|
+
const errMsg = errorMessage(error);
|
|
11999
|
+
warn(MODULE8, `Failed to check claim status on ${owner}/${repo}#${issueNumber}: ${errMsg}. Assuming not claimed.`);
|
|
12000
|
+
return { passed: true, inconclusive: true, reason: errMsg };
|
|
12015
12001
|
}
|
|
12016
12002
|
}
|
|
12017
12003
|
async checkProjectHealth(owner, repo) {
|
|
@@ -12042,8 +12028,8 @@ var init_issue_vetting = __esm({
|
|
|
12042
12028
|
ciStatus = "passing";
|
|
12043
12029
|
}
|
|
12044
12030
|
} catch (error) {
|
|
12045
|
-
const
|
|
12046
|
-
warn(MODULE8, `Failed to check CI status for ${owner}/${repo}: ${
|
|
12031
|
+
const errMsg = errorMessage(error);
|
|
12032
|
+
warn(MODULE8, `Failed to check CI status for ${owner}/${repo}: ${errMsg}. Defaulting to unknown.`);
|
|
12047
12033
|
}
|
|
12048
12034
|
return {
|
|
12049
12035
|
repo: `${owner}/${repo}`,
|
|
@@ -12058,8 +12044,8 @@ var init_issue_vetting = __esm({
|
|
|
12058
12044
|
forksCount: repoData.forks_count
|
|
12059
12045
|
};
|
|
12060
12046
|
} catch (error) {
|
|
12061
|
-
const
|
|
12062
|
-
warn(MODULE8, `Error checking project health for ${owner}/${repo}: ${
|
|
12047
|
+
const errMsg = errorMessage(error);
|
|
12048
|
+
warn(MODULE8, `Error checking project health for ${owner}/${repo}: ${errMsg}`);
|
|
12063
12049
|
return {
|
|
12064
12050
|
repo: `${owner}/${repo}`,
|
|
12065
12051
|
lastCommitAt: "",
|
|
@@ -12069,7 +12055,7 @@ var init_issue_vetting = __esm({
|
|
|
12069
12055
|
ciStatus: "unknown",
|
|
12070
12056
|
isActive: false,
|
|
12071
12057
|
checkFailed: true,
|
|
12072
|
-
failureReason:
|
|
12058
|
+
failureReason: errMsg
|
|
12073
12059
|
};
|
|
12074
12060
|
}
|
|
12075
12061
|
}
|
|
@@ -12222,18 +12208,18 @@ var init_issue_discovery = __esm({
|
|
|
12222
12208
|
return starredRepos;
|
|
12223
12209
|
} catch (error) {
|
|
12224
12210
|
const cachedRepos = this.stateManager.getStarredRepos();
|
|
12225
|
-
const
|
|
12226
|
-
warn(MODULE9, "Error fetching starred repos:",
|
|
12211
|
+
const errMsg = errorMessage(error);
|
|
12212
|
+
warn(MODULE9, "Error fetching starred repos:", errMsg);
|
|
12227
12213
|
if (cachedRepos.length === 0) {
|
|
12228
12214
|
warn(
|
|
12229
12215
|
MODULE9,
|
|
12230
|
-
`Failed to fetch starred repositories from GitHub API. No cached repos available. Error: ${
|
|
12216
|
+
`Failed to fetch starred repositories from GitHub API. No cached repos available. Error: ${errMsg}
|
|
12231
12217
|
Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`
|
|
12232
12218
|
);
|
|
12233
12219
|
} else {
|
|
12234
12220
|
warn(
|
|
12235
12221
|
MODULE9,
|
|
12236
|
-
`Failed to fetch starred repositories from GitHub API. Using ${cachedRepos.length} cached repos instead. Error: ${
|
|
12222
|
+
`Failed to fetch starred repositories from GitHub API. Using ${cachedRepos.length} cached repos instead. Error: ${errMsg}`
|
|
12237
12223
|
);
|
|
12238
12224
|
}
|
|
12239
12225
|
return cachedRepos;
|
|
@@ -12272,10 +12258,10 @@ Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`
|
|
|
12272
12258
|
warn(MODULE9, this.rateLimitWarning);
|
|
12273
12259
|
}
|
|
12274
12260
|
} catch (error) {
|
|
12275
|
-
if (error
|
|
12261
|
+
if (getHttpStatusCode(error) === 401) {
|
|
12276
12262
|
throw error;
|
|
12277
12263
|
}
|
|
12278
|
-
warn(MODULE9, "Could not check rate limit:", error
|
|
12264
|
+
warn(MODULE9, "Could not check rate limit:", errorMessage(error));
|
|
12279
12265
|
}
|
|
12280
12266
|
const mergedPRRepos = this.stateManager.getReposWithMergedPRs();
|
|
12281
12267
|
const mergedPRRepoSet = new Set(mergedPRRepos);
|
|
@@ -12441,12 +12427,12 @@ Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`
|
|
|
12441
12427
|
}
|
|
12442
12428
|
console.log(`Found ${starFiltered.length} candidates from general search`);
|
|
12443
12429
|
} catch (error) {
|
|
12444
|
-
const
|
|
12445
|
-
phase2Error =
|
|
12430
|
+
const errMsg = errorMessage(error);
|
|
12431
|
+
phase2Error = errMsg;
|
|
12446
12432
|
if (IssueVetter.isRateLimitError(error)) {
|
|
12447
12433
|
rateLimitHitDuringSearch = true;
|
|
12448
12434
|
}
|
|
12449
|
-
warn(MODULE9, `Error in general issue search: ${
|
|
12435
|
+
warn(MODULE9, `Error in general issue search: ${errMsg}`);
|
|
12450
12436
|
}
|
|
12451
12437
|
}
|
|
12452
12438
|
let phase3Error = null;
|
|
@@ -12510,12 +12496,12 @@ Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`
|
|
|
12510
12496
|
}
|
|
12511
12497
|
console.log(`Found ${starFiltered.length} candidates from maintained-repo search`);
|
|
12512
12498
|
} catch (error) {
|
|
12513
|
-
const
|
|
12514
|
-
phase3Error =
|
|
12499
|
+
const errMsg = errorMessage(error);
|
|
12500
|
+
phase3Error = errMsg;
|
|
12515
12501
|
if (IssueVetter.isRateLimitError(error)) {
|
|
12516
12502
|
rateLimitHitDuringSearch = true;
|
|
12517
12503
|
}
|
|
12518
|
-
warn(MODULE9, `Error in maintained-repo search: ${
|
|
12504
|
+
warn(MODULE9, `Error in maintained-repo search: ${errMsg}`);
|
|
12519
12505
|
}
|
|
12520
12506
|
}
|
|
12521
12507
|
if (allCandidates.length === 0) {
|
|
@@ -12591,11 +12577,7 @@ Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`
|
|
|
12591
12577
|
rateLimitFailures++;
|
|
12592
12578
|
}
|
|
12593
12579
|
const batchRepos = batch.join(", ");
|
|
12594
|
-
warn(
|
|
12595
|
-
MODULE9,
|
|
12596
|
-
`Error searching issues in batch [${batchRepos}]:`,
|
|
12597
|
-
error instanceof Error ? error.message : error
|
|
12598
|
-
);
|
|
12580
|
+
warn(MODULE9, `Error searching issues in batch [${batchRepos}]:`, errorMessage(error));
|
|
12599
12581
|
}
|
|
12600
12582
|
}
|
|
12601
12583
|
const allBatchesFailed = failedBatches === batches.length && batches.length > 0;
|
|
@@ -12789,7 +12771,7 @@ var init_issue_conversation = __esm({
|
|
|
12789
12771
|
}
|
|
12790
12772
|
const { owner, repo } = parsed;
|
|
12791
12773
|
const repoFullName = `${owner}/${repo}`;
|
|
12792
|
-
if (owner
|
|
12774
|
+
if (isOwnRepo(owner, username)) continue;
|
|
12793
12775
|
if (item.user?.login?.toLowerCase() === username.toLowerCase()) continue;
|
|
12794
12776
|
if (config.excludeRepos.includes(repoFullName)) continue;
|
|
12795
12777
|
if (config.excludeOrgs?.some((org) => owner.toLowerCase() === org.toLowerCase())) continue;
|
|
@@ -12814,7 +12796,7 @@ var init_issue_conversation = __esm({
|
|
|
12814
12796
|
});
|
|
12815
12797
|
}
|
|
12816
12798
|
} catch (error) {
|
|
12817
|
-
const msg =
|
|
12799
|
+
const msg = errorMessage(error);
|
|
12818
12800
|
failures.push({ issueUrl: item.html_url, error: msg });
|
|
12819
12801
|
warn(MODULE10, `Error analyzing issue ${item.html_url}: ${msg}`);
|
|
12820
12802
|
}
|
|
@@ -13368,6 +13350,7 @@ var init_core = __esm({
|
|
|
13368
13350
|
init_comment_utils();
|
|
13369
13351
|
init_github();
|
|
13370
13352
|
init_utils();
|
|
13353
|
+
init_errors();
|
|
13371
13354
|
init_logger();
|
|
13372
13355
|
init_http_cache();
|
|
13373
13356
|
init_daily_logic();
|
|
@@ -13455,25 +13438,9 @@ __export(daily_exports, {
|
|
|
13455
13438
|
groupPRsByRepo: () => groupPRsByRepo,
|
|
13456
13439
|
printDigest: () => printDigest,
|
|
13457
13440
|
runDaily: () => runDaily,
|
|
13441
|
+
runDailyForDisplay: () => runDailyForDisplay,
|
|
13458
13442
|
toShelvedPRRef: () => toShelvedPRRef
|
|
13459
13443
|
});
|
|
13460
|
-
async function runDaily(options) {
|
|
13461
|
-
const token = getGitHubToken();
|
|
13462
|
-
try {
|
|
13463
|
-
await runDailyInner(token, options);
|
|
13464
|
-
} catch (error) {
|
|
13465
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
13466
|
-
if (options.json) {
|
|
13467
|
-
outputJsonError(`Daily check failed: ${msg}`);
|
|
13468
|
-
} else {
|
|
13469
|
-
console.error(`[FATAL] Daily check failed: ${msg}`);
|
|
13470
|
-
if (error instanceof Error && error.stack) {
|
|
13471
|
-
console.error(error.stack);
|
|
13472
|
-
}
|
|
13473
|
-
}
|
|
13474
|
-
process.exit(1);
|
|
13475
|
-
}
|
|
13476
|
-
}
|
|
13477
13444
|
async function fetchPRData(prMonitor, token) {
|
|
13478
13445
|
const { prs, failures } = await prMonitor.fetchUserOpenPRs();
|
|
13479
13446
|
if (failures.length > 0) {
|
|
@@ -13485,15 +13452,15 @@ async function fetchPRData(prMonitor, token) {
|
|
|
13485
13452
|
prMonitor.fetchUserMergedPRCounts(),
|
|
13486
13453
|
prMonitor.fetchUserClosedPRCounts(),
|
|
13487
13454
|
prMonitor.fetchRecentlyClosedPRs().catch((err) => {
|
|
13488
|
-
console.error(`Warning: Failed to fetch recently closed PRs: ${err
|
|
13455
|
+
console.error(`Warning: Failed to fetch recently closed PRs: ${errorMessage(err)}`);
|
|
13489
13456
|
return [];
|
|
13490
13457
|
}),
|
|
13491
13458
|
prMonitor.fetchRecentlyMergedPRs().catch((err) => {
|
|
13492
|
-
console.error(`Warning: Failed to fetch recently merged PRs: ${err
|
|
13459
|
+
console.error(`Warning: Failed to fetch recently merged PRs: ${errorMessage(err)}`);
|
|
13493
13460
|
return [];
|
|
13494
13461
|
}),
|
|
13495
13462
|
issueMonitor.fetchCommentedIssues().catch((error) => {
|
|
13496
|
-
const msg =
|
|
13463
|
+
const msg = errorMessage(error);
|
|
13497
13464
|
if (msg.includes("No GitHub username configured")) {
|
|
13498
13465
|
console.error(`[DAILY] Issue conversation tracking requires setup: ${msg}`);
|
|
13499
13466
|
} else {
|
|
@@ -13550,10 +13517,7 @@ async function updateRepoScores(prMonitor, prs, mergedCounts, closedCounts) {
|
|
|
13550
13517
|
stateManager2.updateRepoScore(repo, { mergedPRCount: count, lastMergedAt: lastMergedAt || void 0 });
|
|
13551
13518
|
} catch (error) {
|
|
13552
13519
|
mergedCountFailures++;
|
|
13553
|
-
console.error(
|
|
13554
|
-
`[DAILY] Failed to update merged count for ${repo}:`,
|
|
13555
|
-
error instanceof Error ? error.message : error
|
|
13556
|
-
);
|
|
13520
|
+
console.error(`[DAILY] Failed to update merged count for ${repo}:`, errorMessage(error));
|
|
13557
13521
|
}
|
|
13558
13522
|
}
|
|
13559
13523
|
if (mergedCountFailures === mergedCounts.size && mergedCounts.size > 0) {
|
|
@@ -13573,10 +13537,7 @@ async function updateRepoScores(prMonitor, prs, mergedCounts, closedCounts) {
|
|
|
13573
13537
|
stateManager2.updateRepoScore(repo, { closedWithoutMergeCount: count });
|
|
13574
13538
|
} catch (error) {
|
|
13575
13539
|
closedCountFailures++;
|
|
13576
|
-
console.error(
|
|
13577
|
-
`[DAILY] Failed to update closed count for ${repo}:`,
|
|
13578
|
-
error instanceof Error ? error.message : error
|
|
13579
|
-
);
|
|
13540
|
+
console.error(`[DAILY] Failed to update closed count for ${repo}:`, errorMessage(error));
|
|
13580
13541
|
}
|
|
13581
13542
|
}
|
|
13582
13543
|
if (closedCountFailures === closedCounts.size && closedCounts.size > 0) {
|
|
@@ -13589,7 +13550,7 @@ async function updateRepoScores(prMonitor, prs, mergedCounts, closedCounts) {
|
|
|
13589
13550
|
stateManager2.updateRepoScore(repo, { signals });
|
|
13590
13551
|
} catch (error) {
|
|
13591
13552
|
signalUpdateFailures++;
|
|
13592
|
-
console.error(`[DAILY] Failed to update signals for ${repo}:`, error
|
|
13553
|
+
console.error(`[DAILY] Failed to update signals for ${repo}:`, errorMessage(error));
|
|
13593
13554
|
}
|
|
13594
13555
|
}
|
|
13595
13556
|
if (signalUpdateFailures === repoSignals.size && repoSignals.size > 0) {
|
|
@@ -13602,7 +13563,7 @@ async function updateRepoScores(prMonitor, prs, mergedCounts, closedCounts) {
|
|
|
13602
13563
|
try {
|
|
13603
13564
|
starCounts = await prMonitor.fetchRepoStarCounts(allRepos);
|
|
13604
13565
|
} catch (error) {
|
|
13605
|
-
console.error("[DAILY] Failed to fetch repo star counts:", error
|
|
13566
|
+
console.error("[DAILY] Failed to fetch repo star counts:", errorMessage(error));
|
|
13606
13567
|
console.error(
|
|
13607
13568
|
"[DAILY] Dashboard minStars filter will use cached star counts (or be skipped for repos without cached data)."
|
|
13608
13569
|
);
|
|
@@ -13614,7 +13575,7 @@ async function updateRepoScores(prMonitor, prs, mergedCounts, closedCounts) {
|
|
|
13614
13575
|
stateManager2.updateRepoScore(repo, { stargazersCount: stars });
|
|
13615
13576
|
} catch (error) {
|
|
13616
13577
|
starUpdateFailures++;
|
|
13617
|
-
console.error(`[DAILY] Failed to update star count for ${repo}:`, error
|
|
13578
|
+
console.error(`[DAILY] Failed to update star count for ${repo}:`, errorMessage(error));
|
|
13618
13579
|
}
|
|
13619
13580
|
}
|
|
13620
13581
|
if (starUpdateFailures === starCounts.size && starCounts.size > 0) {
|
|
@@ -13626,7 +13587,7 @@ async function updateRepoScores(prMonitor, prs, mergedCounts, closedCounts) {
|
|
|
13626
13587
|
stateManager2.addTrustedProject(repo);
|
|
13627
13588
|
} catch (error) {
|
|
13628
13589
|
trustSyncFailures++;
|
|
13629
|
-
console.error(`[DAILY] Failed to sync trusted project ${repo}:`, error
|
|
13590
|
+
console.error(`[DAILY] Failed to sync trusted project ${repo}:`, errorMessage(error));
|
|
13630
13591
|
}
|
|
13631
13592
|
}
|
|
13632
13593
|
if (trustSyncFailures === mergedCounts.size && mergedCounts.size > 0) {
|
|
@@ -13640,12 +13601,12 @@ function updateAnalytics(prs, monthlyCounts, monthlyClosedCounts, openedFromMerg
|
|
|
13640
13601
|
try {
|
|
13641
13602
|
stateManager2.setMonthlyMergedCounts(monthlyCounts);
|
|
13642
13603
|
} catch (error) {
|
|
13643
|
-
console.error("[DAILY] Failed to store monthly merged counts:", error
|
|
13604
|
+
console.error("[DAILY] Failed to store monthly merged counts:", errorMessage(error));
|
|
13644
13605
|
}
|
|
13645
13606
|
try {
|
|
13646
13607
|
stateManager2.setMonthlyClosedCounts(monthlyClosedCounts);
|
|
13647
13608
|
} catch (error) {
|
|
13648
|
-
console.error("[DAILY] Failed to store monthly closed counts:", error
|
|
13609
|
+
console.error("[DAILY] Failed to store monthly closed counts:", errorMessage(error));
|
|
13649
13610
|
}
|
|
13650
13611
|
try {
|
|
13651
13612
|
const combinedOpenedCounts = { ...openedFromMerged };
|
|
@@ -13660,10 +13621,7 @@ function updateAnalytics(prs, monthlyCounts, monthlyClosedCounts, openedFromMerg
|
|
|
13660
13621
|
}
|
|
13661
13622
|
stateManager2.setMonthlyOpenedCounts(combinedOpenedCounts);
|
|
13662
13623
|
} catch (error) {
|
|
13663
|
-
console.error(
|
|
13664
|
-
"[DAILY] Failed to compute/store monthly opened counts:",
|
|
13665
|
-
error instanceof Error ? error.message : error
|
|
13666
|
-
);
|
|
13624
|
+
console.error("[DAILY] Failed to compute/store monthly opened counts:", errorMessage(error));
|
|
13667
13625
|
}
|
|
13668
13626
|
}
|
|
13669
13627
|
function partitionPRs(prMonitor, prs, recentlyClosedPRs, recentlyMergedPRs) {
|
|
@@ -13678,7 +13636,7 @@ function partitionPRs(prMonitor, prs, recentlyClosedPRs, recentlyMergedPRs) {
|
|
|
13678
13636
|
stateManager2.save();
|
|
13679
13637
|
}
|
|
13680
13638
|
} catch (error) {
|
|
13681
|
-
console.error("[DAILY] Failed to expire/persist snoozes:", error
|
|
13639
|
+
console.error("[DAILY] Failed to expire/persist snoozes:", errorMessage(error));
|
|
13682
13640
|
}
|
|
13683
13641
|
const shelvedPRs = [];
|
|
13684
13642
|
const autoUnshelvedPRs = [];
|
|
@@ -13796,19 +13754,19 @@ async function executeDailyCheckInternal(token) {
|
|
|
13796
13754
|
const { activePRs, shelvedPRs, digest } = partitionPRs(prMonitor, prs, recentlyClosedPRs, recentlyMergedPRs);
|
|
13797
13755
|
return generateDigestOutput(digest, activePRs, shelvedPRs, commentedIssues, failures);
|
|
13798
13756
|
}
|
|
13799
|
-
async function
|
|
13800
|
-
|
|
13801
|
-
|
|
13802
|
-
|
|
13803
|
-
|
|
13804
|
-
|
|
13805
|
-
|
|
13806
|
-
}
|
|
13757
|
+
async function runDaily() {
|
|
13758
|
+
const token = requireGitHubToken();
|
|
13759
|
+
return executeDailyCheck(token);
|
|
13760
|
+
}
|
|
13761
|
+
async function runDailyForDisplay() {
|
|
13762
|
+
const token = requireGitHubToken();
|
|
13763
|
+
return executeDailyCheckInternal(token);
|
|
13807
13764
|
}
|
|
13808
13765
|
var init_daily = __esm({
|
|
13809
13766
|
"src/commands/daily.ts"() {
|
|
13810
13767
|
"use strict";
|
|
13811
13768
|
init_core();
|
|
13769
|
+
init_errors();
|
|
13812
13770
|
init_json();
|
|
13813
13771
|
init_core();
|
|
13814
13772
|
}
|
|
@@ -13824,39 +13782,21 @@ async function runStatus(options) {
|
|
|
13824
13782
|
const stats = stateManager2.getStats();
|
|
13825
13783
|
const state = stateManager2.getState();
|
|
13826
13784
|
const lastUpdated = state.lastDigestAt || state.lastRunAt;
|
|
13827
|
-
|
|
13828
|
-
|
|
13829
|
-
|
|
13830
|
-
|
|
13831
|
-
|
|
13832
|
-
|
|
13833
|
-
|
|
13834
|
-
|
|
13835
|
-
output.lastUpdated = lastUpdated;
|
|
13836
|
-
}
|
|
13837
|
-
outputJson(output);
|
|
13838
|
-
} else {
|
|
13839
|
-
console.log("\n\u{1F4CA} OSS Status\n");
|
|
13840
|
-
console.log(`Merged PRs: ${stats.mergedPRs}`);
|
|
13841
|
-
console.log(`Closed PRs: ${stats.closedPRs}`);
|
|
13842
|
-
console.log(`Merge Rate: ${stats.mergeRate}`);
|
|
13843
|
-
console.log(`Needs Response: ${stats.needsResponse}`);
|
|
13844
|
-
if (options.offline) {
|
|
13845
|
-
console.log(`
|
|
13846
|
-
Last Updated: ${lastUpdated || "Never"}`);
|
|
13847
|
-
console.log("(Offline mode: showing cached data)");
|
|
13848
|
-
} else {
|
|
13849
|
-
console.log(`
|
|
13850
|
-
Last Run: ${state.lastRunAt || "Never"}`);
|
|
13851
|
-
}
|
|
13852
|
-
console.log("\nRun with --json for structured output");
|
|
13785
|
+
const { totalTracked: _totalTracked, ...outputStats } = stats;
|
|
13786
|
+
const output = {
|
|
13787
|
+
stats: outputStats,
|
|
13788
|
+
lastRunAt: state.lastRunAt
|
|
13789
|
+
};
|
|
13790
|
+
if (options.offline) {
|
|
13791
|
+
output.offline = true;
|
|
13792
|
+
output.lastUpdated = lastUpdated;
|
|
13853
13793
|
}
|
|
13794
|
+
return output;
|
|
13854
13795
|
}
|
|
13855
13796
|
var init_status = __esm({
|
|
13856
13797
|
"src/commands/status.ts"() {
|
|
13857
13798
|
"use strict";
|
|
13858
13799
|
init_core();
|
|
13859
|
-
init_json();
|
|
13860
13800
|
}
|
|
13861
13801
|
});
|
|
13862
13802
|
|
|
@@ -13866,104 +13806,68 @@ __export(search_exports, {
|
|
|
13866
13806
|
runSearch: () => runSearch
|
|
13867
13807
|
});
|
|
13868
13808
|
async function runSearch(options) {
|
|
13869
|
-
const token =
|
|
13809
|
+
const token = requireGitHubToken();
|
|
13870
13810
|
const discovery = new IssueDiscovery(token);
|
|
13871
|
-
if (!options.json) {
|
|
13872
|
-
console.log(`
|
|
13873
|
-
\u{1F50D} Searching for issues (max ${options.maxResults})...
|
|
13874
|
-
`);
|
|
13875
|
-
}
|
|
13876
13811
|
const candidates = await discovery.searchIssues({ maxResults: options.maxResults });
|
|
13877
|
-
|
|
13878
|
-
|
|
13879
|
-
|
|
13880
|
-
|
|
13881
|
-
|
|
13882
|
-
|
|
13883
|
-
|
|
13884
|
-
|
|
13885
|
-
|
|
13886
|
-
|
|
13887
|
-
|
|
13888
|
-
|
|
13889
|
-
|
|
13890
|
-
|
|
13891
|
-
|
|
13892
|
-
|
|
13893
|
-
|
|
13894
|
-
|
|
13895
|
-
|
|
13896
|
-
|
|
13897
|
-
|
|
13898
|
-
|
|
13899
|
-
|
|
13900
|
-
|
|
13901
|
-
|
|
13902
|
-
|
|
13903
|
-
|
|
13904
|
-
|
|
13905
|
-
|
|
13906
|
-
|
|
13907
|
-
|
|
13908
|
-
|
|
13909
|
-
|
|
13910
|
-
|
|
13911
|
-
searchOutput.rateLimitWarning = discovery.rateLimitWarning;
|
|
13912
|
-
}
|
|
13913
|
-
outputJson(searchOutput);
|
|
13914
|
-
} else {
|
|
13915
|
-
if (candidates.length === 0) {
|
|
13916
|
-
if (discovery.rateLimitWarning) {
|
|
13917
|
-
console.warn(`
|
|
13918
|
-
\u26A0 ${discovery.rateLimitWarning}
|
|
13919
|
-
`);
|
|
13920
|
-
} else {
|
|
13921
|
-
console.log("No matching issues found.");
|
|
13922
|
-
}
|
|
13923
|
-
return;
|
|
13924
|
-
}
|
|
13925
|
-
if (discovery.rateLimitWarning) {
|
|
13926
|
-
console.warn(`
|
|
13927
|
-
\u26A0 ${discovery.rateLimitWarning}
|
|
13928
|
-
`);
|
|
13929
|
-
}
|
|
13930
|
-
console.log(`Found ${candidates.length} candidates:
|
|
13931
|
-
`);
|
|
13932
|
-
for (const candidate of candidates) {
|
|
13933
|
-
console.log(discovery.formatCandidate(candidate));
|
|
13934
|
-
console.log("---");
|
|
13935
|
-
}
|
|
13812
|
+
const stateManager2 = getStateManager();
|
|
13813
|
+
const { config } = stateManager2.getState();
|
|
13814
|
+
const excludedRepos = config.excludeRepos || [];
|
|
13815
|
+
const aiPolicyBlocklist = config.aiPolicyBlocklist ?? DEFAULT_CONFIG.aiPolicyBlocklist ?? [];
|
|
13816
|
+
const searchOutput = {
|
|
13817
|
+
candidates: candidates.map((c) => {
|
|
13818
|
+
const repoScoreRecord = stateManager2.getRepoScore(c.issue.repo);
|
|
13819
|
+
return {
|
|
13820
|
+
issue: {
|
|
13821
|
+
repo: c.issue.repo,
|
|
13822
|
+
number: c.issue.number,
|
|
13823
|
+
title: c.issue.title,
|
|
13824
|
+
url: c.issue.url,
|
|
13825
|
+
labels: c.issue.labels
|
|
13826
|
+
},
|
|
13827
|
+
recommendation: c.recommendation,
|
|
13828
|
+
reasonsToApprove: c.reasonsToApprove,
|
|
13829
|
+
reasonsToSkip: c.reasonsToSkip,
|
|
13830
|
+
searchPriority: c.searchPriority,
|
|
13831
|
+
viabilityScore: c.viabilityScore,
|
|
13832
|
+
repoScore: repoScoreRecord ? {
|
|
13833
|
+
score: repoScoreRecord.score,
|
|
13834
|
+
mergedPRCount: repoScoreRecord.mergedPRCount,
|
|
13835
|
+
closedWithoutMergeCount: repoScoreRecord.closedWithoutMergeCount,
|
|
13836
|
+
isResponsive: repoScoreRecord.signals?.isResponsive ?? false,
|
|
13837
|
+
lastMergedAt: repoScoreRecord.lastMergedAt
|
|
13838
|
+
} : void 0
|
|
13839
|
+
};
|
|
13840
|
+
}),
|
|
13841
|
+
excludedRepos,
|
|
13842
|
+
aiPolicyBlocklist
|
|
13843
|
+
};
|
|
13844
|
+
if (discovery.rateLimitWarning) {
|
|
13845
|
+
searchOutput.rateLimitWarning = discovery.rateLimitWarning;
|
|
13936
13846
|
}
|
|
13847
|
+
return searchOutput;
|
|
13937
13848
|
}
|
|
13938
13849
|
var init_search = __esm({
|
|
13939
13850
|
"src/commands/search.ts"() {
|
|
13940
13851
|
"use strict";
|
|
13941
13852
|
init_core();
|
|
13942
|
-
init_json();
|
|
13943
13853
|
}
|
|
13944
13854
|
});
|
|
13945
13855
|
|
|
13946
13856
|
// src/commands/validation.ts
|
|
13947
|
-
function validateGitHubUrl(url, pattern, entityType
|
|
13857
|
+
function validateGitHubUrl(url, pattern, entityType) {
|
|
13948
13858
|
if (pattern.test(url)) return;
|
|
13949
13859
|
const example = entityType === "PR" ? "https://github.com/owner/repo/pull/123" : "https://github.com/owner/repo/issues/123";
|
|
13950
|
-
|
|
13951
|
-
if (json) {
|
|
13952
|
-
outputJsonError(msg);
|
|
13953
|
-
} else {
|
|
13954
|
-
console.error(`Error: ${msg}`);
|
|
13955
|
-
}
|
|
13956
|
-
process.exit(1);
|
|
13860
|
+
throw new ValidationError(`Invalid ${entityType} URL: ${url}. Expected format: ${example}`);
|
|
13957
13861
|
}
|
|
13958
13862
|
function validateUrl(url) {
|
|
13959
13863
|
if (url.length > MAX_URL_LENGTH) {
|
|
13960
|
-
throw new
|
|
13864
|
+
throw new ValidationError(`URL exceeds maximum length of ${MAX_URL_LENGTH} characters`);
|
|
13961
13865
|
}
|
|
13962
13866
|
return url;
|
|
13963
13867
|
}
|
|
13964
13868
|
function validateMessage(message) {
|
|
13965
13869
|
if (message.length > MAX_MESSAGE_LENGTH) {
|
|
13966
|
-
throw new
|
|
13870
|
+
throw new ValidationError(`Message exceeds maximum length of ${MAX_MESSAGE_LENGTH} characters`);
|
|
13967
13871
|
}
|
|
13968
13872
|
return message;
|
|
13969
13873
|
}
|
|
@@ -13996,7 +13900,6 @@ var init_validation = __esm({
|
|
|
13996
13900
|
"src/commands/validation.ts"() {
|
|
13997
13901
|
"use strict";
|
|
13998
13902
|
init_errors();
|
|
13999
|
-
init_json();
|
|
14000
13903
|
PR_URL_PATTERN = /^https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+$/;
|
|
14001
13904
|
ISSUE_URL_PATTERN = /^https:\/\/github\.com\/[^/]+\/[^/]+\/issues\/\d+$/;
|
|
14002
13905
|
MAX_URL_LENGTH = 2048;
|
|
@@ -14014,38 +13917,28 @@ __export(vet_exports, {
|
|
|
14014
13917
|
});
|
|
14015
13918
|
async function runVet(options) {
|
|
14016
13919
|
validateUrl(options.issueUrl);
|
|
14017
|
-
const token =
|
|
13920
|
+
const token = requireGitHubToken();
|
|
14018
13921
|
const discovery = new IssueDiscovery(token);
|
|
14019
|
-
if (!options.json) {
|
|
14020
|
-
console.log(`
|
|
14021
|
-
\u{1F50D} Vetting issue: ${options.issueUrl}
|
|
14022
|
-
`);
|
|
14023
|
-
}
|
|
14024
13922
|
const candidate = await discovery.vetIssue(options.issueUrl);
|
|
14025
|
-
|
|
14026
|
-
|
|
14027
|
-
|
|
14028
|
-
|
|
14029
|
-
|
|
14030
|
-
|
|
14031
|
-
|
|
14032
|
-
|
|
14033
|
-
|
|
14034
|
-
|
|
14035
|
-
|
|
14036
|
-
|
|
14037
|
-
|
|
14038
|
-
|
|
14039
|
-
});
|
|
14040
|
-
} else {
|
|
14041
|
-
console.log(discovery.formatCandidate(candidate));
|
|
14042
|
-
}
|
|
13923
|
+
return {
|
|
13924
|
+
issue: {
|
|
13925
|
+
repo: candidate.issue.repo,
|
|
13926
|
+
number: candidate.issue.number,
|
|
13927
|
+
title: candidate.issue.title,
|
|
13928
|
+
url: candidate.issue.url,
|
|
13929
|
+
labels: candidate.issue.labels
|
|
13930
|
+
},
|
|
13931
|
+
recommendation: candidate.recommendation,
|
|
13932
|
+
reasonsToApprove: candidate.reasonsToApprove,
|
|
13933
|
+
reasonsToSkip: candidate.reasonsToSkip,
|
|
13934
|
+
projectHealth: candidate.projectHealth,
|
|
13935
|
+
vettingResult: candidate.vettingResult
|
|
13936
|
+
};
|
|
14043
13937
|
}
|
|
14044
13938
|
var init_vet = __esm({
|
|
14045
13939
|
"src/commands/vet.ts"() {
|
|
14046
13940
|
"use strict";
|
|
14047
13941
|
init_core();
|
|
14048
|
-
init_json();
|
|
14049
13942
|
init_validation();
|
|
14050
13943
|
}
|
|
14051
13944
|
});
|
|
@@ -14058,59 +13951,37 @@ __export(track_exports, {
|
|
|
14058
13951
|
});
|
|
14059
13952
|
async function runTrack(options) {
|
|
14060
13953
|
validateUrl(options.prUrl);
|
|
14061
|
-
validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR"
|
|
14062
|
-
const token =
|
|
13954
|
+
validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR");
|
|
13955
|
+
const token = requireGitHubToken();
|
|
14063
13956
|
const octokit = getOctokit(token);
|
|
14064
13957
|
const parsed = parseGitHubUrl(options.prUrl);
|
|
14065
13958
|
if (!parsed || parsed.type !== "pull") {
|
|
14066
|
-
|
|
14067
|
-
outputJsonError(`Invalid PR URL: ${options.prUrl}`);
|
|
14068
|
-
} else {
|
|
14069
|
-
console.error(`Error: Invalid PR URL: ${options.prUrl}`);
|
|
14070
|
-
}
|
|
14071
|
-
process.exit(1);
|
|
13959
|
+
throw new Error(`Invalid PR URL: ${options.prUrl}`);
|
|
14072
13960
|
}
|
|
14073
13961
|
const { owner, repo, number } = parsed;
|
|
14074
|
-
if (!options.json) {
|
|
14075
|
-
console.log(`
|
|
14076
|
-
\u{1F4CC} Fetching PR: ${options.prUrl}
|
|
14077
|
-
`);
|
|
14078
|
-
}
|
|
14079
13962
|
const { data: ghPR } = await octokit.pulls.get({ owner, repo, pull_number: number });
|
|
14080
|
-
|
|
14081
|
-
|
|
14082
|
-
|
|
14083
|
-
|
|
14084
|
-
|
|
13963
|
+
return {
|
|
13964
|
+
pr: {
|
|
13965
|
+
repo: `${owner}/${repo}`,
|
|
13966
|
+
number,
|
|
13967
|
+
title: ghPR.title,
|
|
13968
|
+
url: options.prUrl
|
|
13969
|
+
}
|
|
14085
13970
|
};
|
|
14086
|
-
if (options.json) {
|
|
14087
|
-
outputJson({ pr });
|
|
14088
|
-
} else {
|
|
14089
|
-
console.log(`PR: ${pr.repo}#${pr.number} - ${pr.title}`);
|
|
14090
|
-
console.log("Note: In v2, PRs are tracked automatically via the daily run.");
|
|
14091
|
-
}
|
|
14092
13971
|
}
|
|
14093
13972
|
async function runUntrack(options) {
|
|
14094
13973
|
validateUrl(options.prUrl);
|
|
14095
|
-
validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR"
|
|
14096
|
-
|
|
14097
|
-
|
|
14098
|
-
|
|
14099
|
-
|
|
14100
|
-
|
|
14101
|
-
});
|
|
14102
|
-
} else {
|
|
14103
|
-
console.log(
|
|
14104
|
-
"Note: In v2, PRs are fetched fresh on each daily run \u2014 there is no local tracking list to remove from."
|
|
14105
|
-
);
|
|
14106
|
-
console.log("Use `shelve` to temporarily hide a PR from the daily summary.");
|
|
14107
|
-
}
|
|
13974
|
+
validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR");
|
|
13975
|
+
return {
|
|
13976
|
+
removed: false,
|
|
13977
|
+
url: options.prUrl,
|
|
13978
|
+
message: "In v2, PRs are fetched fresh on each daily run \u2014 there is no local tracking list to remove from."
|
|
13979
|
+
};
|
|
14108
13980
|
}
|
|
14109
13981
|
var init_track = __esm({
|
|
14110
13982
|
"src/commands/track.ts"() {
|
|
14111
13983
|
"use strict";
|
|
14112
13984
|
init_core();
|
|
14113
|
-
init_json();
|
|
14114
13985
|
init_validation();
|
|
14115
13986
|
init_utils();
|
|
14116
13987
|
}
|
|
@@ -14123,30 +13994,19 @@ __export(read_exports, {
|
|
|
14123
13994
|
});
|
|
14124
13995
|
async function runRead(options) {
|
|
14125
13996
|
if (!options.all && !options.prUrl) {
|
|
14126
|
-
|
|
14127
|
-
outputJsonError("PR URL or --all flag required");
|
|
14128
|
-
} else {
|
|
14129
|
-
console.error("Usage: oss-autopilot read <pr-url> or oss-autopilot read --all");
|
|
14130
|
-
}
|
|
14131
|
-
process.exit(1);
|
|
13997
|
+
throw new Error("PR URL or --all flag required");
|
|
14132
13998
|
}
|
|
14133
13999
|
if (options.prUrl) {
|
|
14134
14000
|
validateUrl(options.prUrl);
|
|
14135
14001
|
}
|
|
14136
|
-
if (options.
|
|
14137
|
-
|
|
14138
|
-
outputJson({ markedAsRead: 0, all: true, message: "In v2, PR read state is not tracked locally." });
|
|
14139
|
-
} else {
|
|
14140
|
-
outputJson({ marked: false, url: options.prUrl, message: "In v2, PR read state is not tracked locally." });
|
|
14141
|
-
}
|
|
14142
|
-
} else {
|
|
14143
|
-
console.log("Note: In v2, PR read state is not tracked locally. PRs are fetched fresh on each daily run.");
|
|
14002
|
+
if (options.all) {
|
|
14003
|
+
return { markedAsRead: 0, all: true, message: "In v2, PR read state is not tracked locally." };
|
|
14144
14004
|
}
|
|
14005
|
+
return { marked: false, url: options.prUrl, message: "In v2, PR read state is not tracked locally." };
|
|
14145
14006
|
}
|
|
14146
14007
|
var init_read = __esm({
|
|
14147
14008
|
"src/commands/read.ts"() {
|
|
14148
14009
|
"use strict";
|
|
14149
|
-
init_json();
|
|
14150
14010
|
init_validation();
|
|
14151
14011
|
}
|
|
14152
14012
|
});
|
|
@@ -14160,47 +14020,44 @@ __export(comments_exports, {
|
|
|
14160
14020
|
});
|
|
14161
14021
|
async function runComments(options) {
|
|
14162
14022
|
validateUrl(options.prUrl);
|
|
14163
|
-
const token =
|
|
14023
|
+
const token = requireGitHubToken();
|
|
14164
14024
|
const stateManager2 = getStateManager();
|
|
14165
14025
|
const octokit = getOctokit(token);
|
|
14166
14026
|
const parsed = parseGitHubUrl(options.prUrl);
|
|
14167
14027
|
if (!parsed || parsed.type !== "pull") {
|
|
14168
|
-
|
|
14169
|
-
outputJsonError("Invalid PR URL format");
|
|
14170
|
-
} else {
|
|
14171
|
-
console.error("Invalid PR URL format");
|
|
14172
|
-
}
|
|
14173
|
-
process.exit(1);
|
|
14028
|
+
throw new Error("Invalid PR URL format");
|
|
14174
14029
|
}
|
|
14175
14030
|
const { owner, repo, number: pull_number } = parsed;
|
|
14176
14031
|
const { data: pr } = await octokit.pulls.get({ owner, repo, pull_number });
|
|
14177
|
-
const reviewComments = await
|
|
14178
|
-
(
|
|
14179
|
-
|
|
14180
|
-
|
|
14181
|
-
|
|
14182
|
-
|
|
14183
|
-
|
|
14184
|
-
|
|
14185
|
-
|
|
14186
|
-
|
|
14187
|
-
(
|
|
14188
|
-
|
|
14189
|
-
|
|
14190
|
-
|
|
14191
|
-
|
|
14192
|
-
|
|
14193
|
-
|
|
14194
|
-
|
|
14195
|
-
|
|
14196
|
-
(
|
|
14197
|
-
|
|
14198
|
-
|
|
14199
|
-
|
|
14200
|
-
|
|
14201
|
-
|
|
14202
|
-
|
|
14203
|
-
|
|
14032
|
+
const [reviewComments, issueComments, reviews] = await Promise.all([
|
|
14033
|
+
paginateAll(
|
|
14034
|
+
(page) => octokit.pulls.listReviewComments({
|
|
14035
|
+
owner,
|
|
14036
|
+
repo,
|
|
14037
|
+
pull_number,
|
|
14038
|
+
per_page: 100,
|
|
14039
|
+
page
|
|
14040
|
+
})
|
|
14041
|
+
),
|
|
14042
|
+
paginateAll(
|
|
14043
|
+
(page) => octokit.issues.listComments({
|
|
14044
|
+
owner,
|
|
14045
|
+
repo,
|
|
14046
|
+
issue_number: pull_number,
|
|
14047
|
+
per_page: 100,
|
|
14048
|
+
page
|
|
14049
|
+
})
|
|
14050
|
+
),
|
|
14051
|
+
paginateAll(
|
|
14052
|
+
(page) => octokit.pulls.listReviews({
|
|
14053
|
+
owner,
|
|
14054
|
+
repo,
|
|
14055
|
+
pull_number,
|
|
14056
|
+
per_page: 100,
|
|
14057
|
+
page
|
|
14058
|
+
})
|
|
14059
|
+
)
|
|
14060
|
+
]);
|
|
14204
14061
|
const username = stateManager2.getState().config.githubUsername;
|
|
14205
14062
|
const filterComment = (c) => {
|
|
14206
14063
|
if (!c.user) return false;
|
|
@@ -14211,240 +14068,104 @@ async function runComments(options) {
|
|
|
14211
14068
|
const relevantReviewComments = reviewComments.filter(filterComment).sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
|
|
14212
14069
|
const relevantIssueComments = issueComments.filter(filterComment).sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
|
|
14213
14070
|
const relevantReviews = reviews.filter((r) => filterComment(r) && r.body && r.body.trim()).sort((a, b) => new Date(b.submitted_at || 0).getTime() - new Date(a.submitted_at || 0).getTime());
|
|
14214
|
-
|
|
14215
|
-
|
|
14216
|
-
|
|
14217
|
-
|
|
14218
|
-
|
|
14219
|
-
|
|
14220
|
-
|
|
14221
|
-
|
|
14222
|
-
|
|
14223
|
-
|
|
14224
|
-
|
|
14225
|
-
|
|
14226
|
-
|
|
14227
|
-
|
|
14228
|
-
|
|
14229
|
-
|
|
14230
|
-
|
|
14231
|
-
|
|
14232
|
-
|
|
14233
|
-
|
|
14234
|
-
|
|
14235
|
-
|
|
14236
|
-
|
|
14237
|
-
|
|
14238
|
-
|
|
14239
|
-
|
|
14240
|
-
|
|
14241
|
-
|
|
14242
|
-
|
|
14243
|
-
|
|
14244
|
-
discussionCommentCount: relevantIssueComments.length
|
|
14245
|
-
}
|
|
14246
|
-
});
|
|
14247
|
-
return;
|
|
14248
|
-
}
|
|
14249
|
-
console.log(`
|
|
14250
|
-
\u{1F4AC} Fetching comments for: ${options.prUrl}
|
|
14251
|
-
`);
|
|
14252
|
-
console.log(`## ${pr.title}
|
|
14253
|
-
`);
|
|
14254
|
-
console.log(`**Status:** ${pr.state} | **Mergeable:** ${pr.mergeable ?? "checking..."}`);
|
|
14255
|
-
console.log(`**Branch:** ${pr.head.ref} \u2192 ${pr.base.ref}`);
|
|
14256
|
-
console.log(`**URL:** ${pr.html_url}
|
|
14257
|
-
`);
|
|
14258
|
-
if (relevantReviews.length > 0) {
|
|
14259
|
-
console.log("### Reviews (newest first)\n");
|
|
14260
|
-
for (const review of relevantReviews) {
|
|
14261
|
-
const state = review.state === "APPROVED" ? "\u2705" : review.state === "CHANGES_REQUESTED" ? "\u274C" : "\u{1F4AC}";
|
|
14262
|
-
const time = review.submitted_at ? formatRelativeTime(review.submitted_at) : "";
|
|
14263
|
-
console.log(`${state} **@${review.user?.login}** (${review.state}) - ${time}`);
|
|
14264
|
-
if (review.body) {
|
|
14265
|
-
console.log(`> ${review.body.split("\n").join("\n> ")}
|
|
14266
|
-
`);
|
|
14267
|
-
}
|
|
14268
|
-
}
|
|
14269
|
-
}
|
|
14270
|
-
if (relevantReviewComments.length > 0) {
|
|
14271
|
-
console.log("### Inline Comments (newest first)\n");
|
|
14272
|
-
for (const comment of relevantReviewComments) {
|
|
14273
|
-
const time = formatRelativeTime(comment.created_at);
|
|
14274
|
-
console.log(`**@${comment.user?.login}** on \`${comment.path}\` - ${time}`);
|
|
14275
|
-
console.log(`> ${comment.body.split("\n").join("\n> ")}`);
|
|
14276
|
-
if (comment.diff_hunk) {
|
|
14277
|
-
console.log(`\`\`\`diff
|
|
14278
|
-
${comment.diff_hunk.slice(-500)}
|
|
14279
|
-
\`\`\``);
|
|
14280
|
-
}
|
|
14281
|
-
console.log("");
|
|
14282
|
-
}
|
|
14283
|
-
}
|
|
14284
|
-
if (relevantIssueComments.length > 0) {
|
|
14285
|
-
console.log("### Discussion (newest first)\n");
|
|
14286
|
-
for (const comment of relevantIssueComments) {
|
|
14287
|
-
const time = formatRelativeTime(comment.created_at);
|
|
14288
|
-
console.log(`**@${comment.user?.login}** - ${time}`);
|
|
14289
|
-
console.log(`> ${comment.body?.split("\n").join("\n> ")}
|
|
14290
|
-
`);
|
|
14071
|
+
return {
|
|
14072
|
+
pr: {
|
|
14073
|
+
title: pr.title,
|
|
14074
|
+
state: pr.state,
|
|
14075
|
+
mergeable: pr.mergeable,
|
|
14076
|
+
head: pr.head.ref,
|
|
14077
|
+
base: pr.base.ref,
|
|
14078
|
+
url: pr.html_url
|
|
14079
|
+
},
|
|
14080
|
+
reviews: relevantReviews.map((r) => ({
|
|
14081
|
+
user: r.user?.login,
|
|
14082
|
+
state: r.state,
|
|
14083
|
+
body: r.body ?? null,
|
|
14084
|
+
submittedAt: r.submitted_at ?? null
|
|
14085
|
+
})),
|
|
14086
|
+
reviewComments: relevantReviewComments.map((c) => ({
|
|
14087
|
+
user: c.user?.login,
|
|
14088
|
+
body: c.body,
|
|
14089
|
+
path: c.path,
|
|
14090
|
+
createdAt: c.created_at
|
|
14091
|
+
})),
|
|
14092
|
+
issueComments: relevantIssueComments.map((c) => ({
|
|
14093
|
+
user: c.user?.login,
|
|
14094
|
+
body: c.body,
|
|
14095
|
+
createdAt: c.created_at
|
|
14096
|
+
})),
|
|
14097
|
+
summary: {
|
|
14098
|
+
reviewCount: relevantReviews.length,
|
|
14099
|
+
inlineCommentCount: relevantReviewComments.length,
|
|
14100
|
+
discussionCommentCount: relevantIssueComments.length
|
|
14291
14101
|
}
|
|
14292
|
-
}
|
|
14293
|
-
if (relevantReviewComments.length === 0 && relevantIssueComments.length === 0 && relevantReviews.length === 0) {
|
|
14294
|
-
console.log("No comments from other users.\n");
|
|
14295
|
-
}
|
|
14296
|
-
console.log("---");
|
|
14297
|
-
console.log(
|
|
14298
|
-
`**Summary:** ${relevantReviews.length} reviews, ${relevantReviewComments.length} inline comments, ${relevantIssueComments.length} discussion comments`
|
|
14299
|
-
);
|
|
14102
|
+
};
|
|
14300
14103
|
}
|
|
14301
14104
|
async function runPost(options) {
|
|
14302
14105
|
validateUrl(options.url);
|
|
14303
|
-
|
|
14304
|
-
|
|
14305
|
-
if (options.stdin) {
|
|
14306
|
-
const chunks = [];
|
|
14307
|
-
for await (const chunk of process.stdin) {
|
|
14308
|
-
chunks.push(chunk);
|
|
14309
|
-
}
|
|
14310
|
-
message = Buffer.concat(chunks).toString("utf-8").trim();
|
|
14311
|
-
}
|
|
14312
|
-
if (!message) {
|
|
14313
|
-
if (options.json) {
|
|
14314
|
-
outputJsonError("No message provided");
|
|
14315
|
-
} else {
|
|
14316
|
-
console.error("Error: No message provided");
|
|
14317
|
-
}
|
|
14318
|
-
process.exit(1);
|
|
14319
|
-
}
|
|
14320
|
-
try {
|
|
14321
|
-
validateMessage(message);
|
|
14322
|
-
} catch (error) {
|
|
14323
|
-
if (options.json) {
|
|
14324
|
-
outputJsonError(error instanceof Error ? error.message : "Invalid message");
|
|
14325
|
-
} else {
|
|
14326
|
-
console.error(`Error: ${error instanceof Error ? error.message : "Invalid message"}`);
|
|
14327
|
-
}
|
|
14328
|
-
process.exit(1);
|
|
14106
|
+
if (!options.message.trim()) {
|
|
14107
|
+
throw new Error("No message provided");
|
|
14329
14108
|
}
|
|
14109
|
+
validateMessage(options.message);
|
|
14110
|
+
const token = requireGitHubToken();
|
|
14330
14111
|
const parsed = parseGitHubUrl(options.url);
|
|
14331
14112
|
if (!parsed) {
|
|
14332
|
-
|
|
14333
|
-
outputJsonError("Invalid GitHub URL format");
|
|
14334
|
-
} else {
|
|
14335
|
-
console.error("Invalid GitHub URL format");
|
|
14336
|
-
}
|
|
14337
|
-
process.exit(1);
|
|
14113
|
+
throw new Error("Invalid GitHub URL format");
|
|
14338
14114
|
}
|
|
14339
14115
|
const { owner, repo, number } = parsed;
|
|
14340
14116
|
const octokit = getOctokit(token);
|
|
14341
|
-
|
|
14342
|
-
|
|
14343
|
-
|
|
14344
|
-
|
|
14345
|
-
|
|
14346
|
-
}
|
|
14347
|
-
|
|
14348
|
-
|
|
14349
|
-
|
|
14350
|
-
|
|
14351
|
-
issue_number: number,
|
|
14352
|
-
body: message
|
|
14353
|
-
});
|
|
14354
|
-
if (options.json) {
|
|
14355
|
-
outputJson({
|
|
14356
|
-
commentUrl: comment.html_url,
|
|
14357
|
-
url: options.url
|
|
14358
|
-
});
|
|
14359
|
-
} else {
|
|
14360
|
-
console.log("\u2705 Comment posted successfully!");
|
|
14361
|
-
console.log(` ${comment.html_url}`);
|
|
14362
|
-
}
|
|
14363
|
-
} catch (error) {
|
|
14364
|
-
if (options.json) {
|
|
14365
|
-
outputJsonError(error instanceof Error ? error.message : "Unknown error");
|
|
14366
|
-
} else {
|
|
14367
|
-
console.error("\u274C Failed to post comment:", error instanceof Error ? error.message : error);
|
|
14368
|
-
}
|
|
14369
|
-
process.exit(1);
|
|
14370
|
-
}
|
|
14117
|
+
const { data: comment } = await octokit.issues.createComment({
|
|
14118
|
+
owner,
|
|
14119
|
+
repo,
|
|
14120
|
+
issue_number: number,
|
|
14121
|
+
body: options.message
|
|
14122
|
+
});
|
|
14123
|
+
return {
|
|
14124
|
+
commentUrl: comment.html_url,
|
|
14125
|
+
url: options.url
|
|
14126
|
+
};
|
|
14371
14127
|
}
|
|
14372
14128
|
async function runClaim(options) {
|
|
14373
14129
|
validateUrl(options.issueUrl);
|
|
14374
|
-
const token =
|
|
14130
|
+
const token = requireGitHubToken();
|
|
14375
14131
|
const message = options.message || "Hi! I'd like to work on this issue. Could you assign it to me?";
|
|
14376
|
-
|
|
14377
|
-
validateMessage(message);
|
|
14378
|
-
} catch (error) {
|
|
14379
|
-
if (options.json) {
|
|
14380
|
-
outputJsonError(error instanceof Error ? error.message : "Invalid message");
|
|
14381
|
-
} else {
|
|
14382
|
-
console.error(`Error: ${error instanceof Error ? error.message : "Invalid message"}`);
|
|
14383
|
-
}
|
|
14384
|
-
process.exit(1);
|
|
14385
|
-
}
|
|
14132
|
+
validateMessage(message);
|
|
14386
14133
|
const parsed = parseGitHubUrl(options.issueUrl);
|
|
14387
14134
|
if (!parsed || parsed.type !== "issues") {
|
|
14388
|
-
|
|
14389
|
-
outputJsonError("Invalid issue URL format (must be an issue, not a PR)");
|
|
14390
|
-
} else {
|
|
14391
|
-
console.error("Invalid issue URL format (must be an issue, not a PR)");
|
|
14392
|
-
}
|
|
14393
|
-
process.exit(1);
|
|
14135
|
+
throw new Error("Invalid issue URL format (must be an issue, not a PR)");
|
|
14394
14136
|
}
|
|
14395
14137
|
const { owner, repo, number } = parsed;
|
|
14396
|
-
if (!options.json) {
|
|
14397
|
-
console.log("\n\u{1F64B} Claiming issue:", options.issueUrl);
|
|
14398
|
-
console.log("---");
|
|
14399
|
-
console.log(message);
|
|
14400
|
-
console.log("---\n");
|
|
14401
|
-
}
|
|
14402
14138
|
const octokit = getOctokit(token);
|
|
14403
|
-
|
|
14404
|
-
|
|
14405
|
-
|
|
14406
|
-
|
|
14407
|
-
|
|
14408
|
-
|
|
14409
|
-
|
|
14410
|
-
|
|
14411
|
-
|
|
14412
|
-
|
|
14413
|
-
|
|
14414
|
-
|
|
14415
|
-
|
|
14416
|
-
|
|
14417
|
-
|
|
14418
|
-
|
|
14419
|
-
|
|
14420
|
-
|
|
14421
|
-
|
|
14422
|
-
|
|
14423
|
-
|
|
14424
|
-
|
|
14425
|
-
|
|
14426
|
-
|
|
14427
|
-
issueUrl: options.issueUrl
|
|
14428
|
-
});
|
|
14429
|
-
} else {
|
|
14430
|
-
console.log("\u2705 Issue claimed!");
|
|
14431
|
-
console.log(` ${comment.html_url}`);
|
|
14432
|
-
}
|
|
14433
|
-
} catch (error) {
|
|
14434
|
-
if (options.json) {
|
|
14435
|
-
outputJsonError(error instanceof Error ? error.message : "Unknown error");
|
|
14436
|
-
} else {
|
|
14437
|
-
console.error("\u274C Failed to claim issue:", error instanceof Error ? error.message : error);
|
|
14438
|
-
}
|
|
14439
|
-
process.exit(1);
|
|
14440
|
-
}
|
|
14139
|
+
const { data: comment } = await octokit.issues.createComment({
|
|
14140
|
+
owner,
|
|
14141
|
+
repo,
|
|
14142
|
+
issue_number: number,
|
|
14143
|
+
body: message
|
|
14144
|
+
});
|
|
14145
|
+
const stateManager2 = getStateManager();
|
|
14146
|
+
stateManager2.addIssue({
|
|
14147
|
+
id: number,
|
|
14148
|
+
url: options.issueUrl,
|
|
14149
|
+
repo: `${owner}/${repo}`,
|
|
14150
|
+
number,
|
|
14151
|
+
title: "(claimed)",
|
|
14152
|
+
status: "claimed",
|
|
14153
|
+
labels: [],
|
|
14154
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14155
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14156
|
+
vetted: false
|
|
14157
|
+
});
|
|
14158
|
+
stateManager2.save();
|
|
14159
|
+
return {
|
|
14160
|
+
commentUrl: comment.html_url,
|
|
14161
|
+
issueUrl: options.issueUrl
|
|
14162
|
+
};
|
|
14441
14163
|
}
|
|
14442
14164
|
var init_comments = __esm({
|
|
14443
14165
|
"src/commands/comments.ts"() {
|
|
14444
14166
|
"use strict";
|
|
14445
14167
|
init_core();
|
|
14446
14168
|
init_pagination();
|
|
14447
|
-
init_json();
|
|
14448
14169
|
init_validation();
|
|
14449
14170
|
}
|
|
14450
14171
|
});
|
|
@@ -14454,28 +14175,14 @@ var config_exports = {};
|
|
|
14454
14175
|
__export(config_exports, {
|
|
14455
14176
|
runConfig: () => runConfig
|
|
14456
14177
|
});
|
|
14457
|
-
function exitWithError(msg, json) {
|
|
14458
|
-
if (json) {
|
|
14459
|
-
outputJsonError(msg);
|
|
14460
|
-
} else {
|
|
14461
|
-
console.error(msg);
|
|
14462
|
-
}
|
|
14463
|
-
process.exit(1);
|
|
14464
|
-
}
|
|
14465
14178
|
async function runConfig(options) {
|
|
14466
14179
|
const stateManager2 = getStateManager();
|
|
14467
14180
|
const currentConfig = stateManager2.getState().config;
|
|
14468
14181
|
if (!options.key) {
|
|
14469
|
-
|
|
14470
|
-
outputJson({ config: currentConfig });
|
|
14471
|
-
} else {
|
|
14472
|
-
console.log("\n\u2699\uFE0F Current Configuration:\n");
|
|
14473
|
-
console.log(JSON.stringify(currentConfig, null, 2));
|
|
14474
|
-
}
|
|
14475
|
-
return;
|
|
14182
|
+
return { config: currentConfig };
|
|
14476
14183
|
}
|
|
14477
14184
|
if (!options.value) {
|
|
14478
|
-
|
|
14185
|
+
throw new Error("Value required");
|
|
14479
14186
|
}
|
|
14480
14187
|
const value = options.value;
|
|
14481
14188
|
switch (options.key) {
|
|
@@ -14495,9 +14202,8 @@ async function runConfig(options) {
|
|
|
14495
14202
|
case "exclude-repo": {
|
|
14496
14203
|
const parts = value.split("/");
|
|
14497
14204
|
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
14498
|
-
|
|
14499
|
-
`Invalid repo format "${value}". Use "owner/repo" format. To exclude an entire org, use: config exclude-org ${value}
|
|
14500
|
-
options.json
|
|
14205
|
+
throw new Error(
|
|
14206
|
+
`Invalid repo format "${value}". Use "owner/repo" format. To exclude an entire org, use: config exclude-org ${value}`
|
|
14501
14207
|
);
|
|
14502
14208
|
}
|
|
14503
14209
|
const valueLower = value.toLowerCase();
|
|
@@ -14509,9 +14215,8 @@ async function runConfig(options) {
|
|
|
14509
14215
|
}
|
|
14510
14216
|
case "exclude-org": {
|
|
14511
14217
|
if (value.includes("/")) {
|
|
14512
|
-
|
|
14513
|
-
`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}
|
|
14514
|
-
options.json
|
|
14218
|
+
throw new Error(
|
|
14219
|
+
`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}`
|
|
14515
14220
|
);
|
|
14516
14221
|
}
|
|
14517
14222
|
const currentOrgs = currentConfig.excludeOrgs ?? [];
|
|
@@ -14522,20 +14227,15 @@ async function runConfig(options) {
|
|
|
14522
14227
|
break;
|
|
14523
14228
|
}
|
|
14524
14229
|
default:
|
|
14525
|
-
|
|
14230
|
+
throw new Error(`Unknown config key: ${options.key}`);
|
|
14526
14231
|
}
|
|
14527
14232
|
stateManager2.save();
|
|
14528
|
-
|
|
14529
|
-
outputJson({ success: true, key: options.key, value });
|
|
14530
|
-
} else {
|
|
14531
|
-
console.log(`Set ${options.key} to: ${value}`);
|
|
14532
|
-
}
|
|
14233
|
+
return { success: true, key: options.key, value };
|
|
14533
14234
|
}
|
|
14534
14235
|
var init_config = __esm({
|
|
14535
14236
|
"src/commands/config.ts"() {
|
|
14536
14237
|
"use strict";
|
|
14537
14238
|
init_core();
|
|
14538
|
-
init_json();
|
|
14539
14239
|
}
|
|
14540
14240
|
});
|
|
14541
14241
|
|
|
@@ -14547,28 +14247,17 @@ __export(init_exports, {
|
|
|
14547
14247
|
async function runInit(options) {
|
|
14548
14248
|
validateGitHubUsername(options.username);
|
|
14549
14249
|
const stateManager2 = getStateManager();
|
|
14550
|
-
if (!options.json) {
|
|
14551
|
-
console.log(`
|
|
14552
|
-
\u{1F680} Initializing for @${options.username}...
|
|
14553
|
-
`);
|
|
14554
|
-
}
|
|
14555
14250
|
stateManager2.updateConfig({ githubUsername: options.username });
|
|
14556
14251
|
stateManager2.save();
|
|
14557
|
-
|
|
14558
|
-
|
|
14559
|
-
|
|
14560
|
-
|
|
14561
|
-
});
|
|
14562
|
-
} else {
|
|
14563
|
-
console.log(`Username set to @${options.username}.`);
|
|
14564
|
-
console.log("Run `oss-autopilot daily` to fetch your open PRs from GitHub.");
|
|
14565
|
-
}
|
|
14252
|
+
return {
|
|
14253
|
+
username: options.username,
|
|
14254
|
+
message: "Username saved. Run `daily` to fetch your open PRs from GitHub."
|
|
14255
|
+
};
|
|
14566
14256
|
}
|
|
14567
14257
|
var init_init = __esm({
|
|
14568
14258
|
"src/commands/init.ts"() {
|
|
14569
14259
|
"use strict";
|
|
14570
14260
|
init_core();
|
|
14571
|
-
init_json();
|
|
14572
14261
|
init_validation();
|
|
14573
14262
|
}
|
|
14574
14263
|
});
|
|
@@ -14584,6 +14273,7 @@ async function runSetup(options) {
|
|
|
14584
14273
|
const config = stateManager2.getState().config;
|
|
14585
14274
|
if (options.set && options.set.length > 0) {
|
|
14586
14275
|
const results = {};
|
|
14276
|
+
const warnings = [];
|
|
14587
14277
|
for (const setting of options.set) {
|
|
14588
14278
|
const [key, ...valueParts] = setting.split("=");
|
|
14589
14279
|
const value = valueParts.join("=");
|
|
@@ -14649,15 +14339,11 @@ async function runSetup(options) {
|
|
|
14649
14339
|
}
|
|
14650
14340
|
}
|
|
14651
14341
|
if (invalid.length > 0) {
|
|
14652
|
-
|
|
14653
|
-
console.warn(`Warning: Skipping invalid entries (expected "owner/repo" format): ${invalid.join(", ")}`);
|
|
14654
|
-
}
|
|
14342
|
+
warnings.push(`Warning: Skipping invalid entries (expected "owner/repo" format): ${invalid.join(", ")}`);
|
|
14655
14343
|
results["aiPolicyBlocklist_invalidEntries"] = invalid.join(", ");
|
|
14656
14344
|
}
|
|
14657
14345
|
if (valid.length === 0 && entries.length > 0) {
|
|
14658
|
-
|
|
14659
|
-
console.warn("Warning: All entries were invalid. Blocklist not updated.");
|
|
14660
|
-
}
|
|
14346
|
+
warnings.push("Warning: All entries were invalid. Blocklist not updated.");
|
|
14661
14347
|
results[key] = "(all entries invalid)";
|
|
14662
14348
|
break;
|
|
14663
14349
|
}
|
|
@@ -14672,175 +14358,91 @@ async function runSetup(options) {
|
|
|
14672
14358
|
}
|
|
14673
14359
|
break;
|
|
14674
14360
|
default:
|
|
14675
|
-
|
|
14676
|
-
console.warn(`Unknown setting: ${key}`);
|
|
14677
|
-
}
|
|
14361
|
+
warnings.push(`Unknown setting: ${key}`);
|
|
14678
14362
|
}
|
|
14679
14363
|
}
|
|
14680
14364
|
stateManager2.save();
|
|
14681
|
-
|
|
14682
|
-
outputJson({ success: true, settings: results });
|
|
14683
|
-
} else {
|
|
14684
|
-
for (const [key, value] of Object.entries(results)) {
|
|
14685
|
-
console.log(`\u2713 ${key}: ${value}`);
|
|
14686
|
-
}
|
|
14687
|
-
}
|
|
14688
|
-
return;
|
|
14365
|
+
return { success: true, settings: results, warnings: warnings.length > 0 ? warnings : void 0 };
|
|
14689
14366
|
}
|
|
14690
14367
|
if (config.setupComplete && !options.reset) {
|
|
14691
|
-
|
|
14692
|
-
|
|
14693
|
-
|
|
14694
|
-
|
|
14695
|
-
|
|
14696
|
-
|
|
14697
|
-
|
|
14698
|
-
|
|
14699
|
-
|
|
14700
|
-
|
|
14701
|
-
|
|
14702
|
-
});
|
|
14703
|
-
} else {
|
|
14704
|
-
console.log("\n\u2699\uFE0F OSS Autopilot Setup\n");
|
|
14705
|
-
console.log("\u2713 Setup already complete!\n");
|
|
14706
|
-
console.log("Current settings:");
|
|
14707
|
-
console.log(` GitHub username: ${config.githubUsername || "(not set)"}`);
|
|
14708
|
-
console.log(` Max active PRs: ${config.maxActivePRs}`);
|
|
14709
|
-
console.log(` Dormant threshold: ${config.dormantThresholdDays} days`);
|
|
14710
|
-
console.log(` Approaching dormant: ${config.approachingDormantDays} days`);
|
|
14711
|
-
console.log(` Languages: ${config.languages.join(", ")}`);
|
|
14712
|
-
console.log(` Labels: ${config.labels.join(", ")}`);
|
|
14713
|
-
console.log(`
|
|
14714
|
-
Run 'setup --reset' to reconfigure.`);
|
|
14715
|
-
}
|
|
14716
|
-
return;
|
|
14717
|
-
}
|
|
14718
|
-
if (options.json) {
|
|
14719
|
-
outputJson({
|
|
14720
|
-
setupRequired: true,
|
|
14721
|
-
prompts: [
|
|
14722
|
-
{
|
|
14723
|
-
setting: "username",
|
|
14724
|
-
prompt: "What is your GitHub username?",
|
|
14725
|
-
current: config.githubUsername || null,
|
|
14726
|
-
required: true,
|
|
14727
|
-
type: "string"
|
|
14728
|
-
},
|
|
14729
|
-
{
|
|
14730
|
-
setting: "maxActivePRs",
|
|
14731
|
-
prompt: "How many PRs do you want to work on at once?",
|
|
14732
|
-
current: config.maxActivePRs,
|
|
14733
|
-
default: 10,
|
|
14734
|
-
type: "number"
|
|
14735
|
-
},
|
|
14736
|
-
{
|
|
14737
|
-
setting: "dormantDays",
|
|
14738
|
-
prompt: "After how many days of inactivity should a PR be considered dormant?",
|
|
14739
|
-
current: config.dormantThresholdDays,
|
|
14740
|
-
default: 30,
|
|
14741
|
-
type: "number"
|
|
14742
|
-
},
|
|
14743
|
-
{
|
|
14744
|
-
setting: "approachingDays",
|
|
14745
|
-
prompt: "At how many days should we warn about approaching dormancy?",
|
|
14746
|
-
current: config.approachingDormantDays,
|
|
14747
|
-
default: 25,
|
|
14748
|
-
type: "number"
|
|
14749
|
-
},
|
|
14750
|
-
{
|
|
14751
|
-
setting: "languages",
|
|
14752
|
-
prompt: "What programming languages do you want to contribute to?",
|
|
14753
|
-
current: config.languages,
|
|
14754
|
-
default: ["typescript", "javascript"],
|
|
14755
|
-
type: "list"
|
|
14756
|
-
},
|
|
14757
|
-
{
|
|
14758
|
-
setting: "labels",
|
|
14759
|
-
prompt: "What issue labels should we search for?",
|
|
14760
|
-
current: config.labels,
|
|
14761
|
-
default: ["good first issue", "help wanted"],
|
|
14762
|
-
type: "list"
|
|
14763
|
-
},
|
|
14764
|
-
{
|
|
14765
|
-
setting: "aiPolicyBlocklist",
|
|
14766
|
-
prompt: "Repos with anti-AI contribution policies to block (owner/repo, comma-separated)?",
|
|
14767
|
-
current: config.aiPolicyBlocklist ?? DEFAULT_CONFIG.aiPolicyBlocklist,
|
|
14768
|
-
default: ["matplotlib/matplotlib"],
|
|
14769
|
-
type: "list"
|
|
14770
|
-
}
|
|
14771
|
-
]
|
|
14772
|
-
});
|
|
14773
|
-
} else {
|
|
14774
|
-
console.log("\n\u2699\uFE0F OSS Autopilot Setup\n");
|
|
14775
|
-
console.log("SETUP_REQUIRED");
|
|
14776
|
-
console.log("---");
|
|
14777
|
-
console.log("Please configure the following settings:\n");
|
|
14778
|
-
console.log("SETTING: username");
|
|
14779
|
-
console.log("PROMPT: What is your GitHub username?");
|
|
14780
|
-
console.log(`CURRENT: ${config.githubUsername || "(not set)"}`);
|
|
14781
|
-
console.log("REQUIRED: true");
|
|
14782
|
-
console.log("");
|
|
14783
|
-
console.log("SETTING: maxActivePRs");
|
|
14784
|
-
console.log("PROMPT: How many PRs do you want to work on at once?");
|
|
14785
|
-
console.log(`CURRENT: ${config.maxActivePRs}`);
|
|
14786
|
-
console.log("DEFAULT: 10");
|
|
14787
|
-
console.log("TYPE: number");
|
|
14788
|
-
console.log("");
|
|
14789
|
-
console.log("SETTING: dormantDays");
|
|
14790
|
-
console.log("PROMPT: After how many days of inactivity should a PR be considered dormant?");
|
|
14791
|
-
console.log(`CURRENT: ${config.dormantThresholdDays}`);
|
|
14792
|
-
console.log("DEFAULT: 30");
|
|
14793
|
-
console.log("TYPE: number");
|
|
14794
|
-
console.log("");
|
|
14795
|
-
console.log("SETTING: approachingDays");
|
|
14796
|
-
console.log("PROMPT: At how many days should we warn about approaching dormancy?");
|
|
14797
|
-
console.log(`CURRENT: ${config.approachingDormantDays}`);
|
|
14798
|
-
console.log("DEFAULT: 25");
|
|
14799
|
-
console.log("TYPE: number");
|
|
14800
|
-
console.log("");
|
|
14801
|
-
console.log("SETTING: languages");
|
|
14802
|
-
console.log("PROMPT: What programming languages do you want to contribute to? (comma-separated)");
|
|
14803
|
-
console.log(`CURRENT: ${config.languages.join(", ")}`);
|
|
14804
|
-
console.log("DEFAULT: typescript, javascript");
|
|
14805
|
-
console.log("TYPE: list");
|
|
14806
|
-
console.log("");
|
|
14807
|
-
console.log("SETTING: labels");
|
|
14808
|
-
console.log("PROMPT: What issue labels should we search for? (comma-separated)");
|
|
14809
|
-
console.log(`CURRENT: ${config.labels.join(", ")}`);
|
|
14810
|
-
console.log("DEFAULT: good first issue, help wanted");
|
|
14811
|
-
console.log("TYPE: list");
|
|
14812
|
-
console.log("");
|
|
14813
|
-
console.log("SETTING: aiPolicyBlocklist");
|
|
14814
|
-
console.log("PROMPT: Repos with anti-AI contribution policies to block? (owner/repo, comma-separated)");
|
|
14815
|
-
console.log(`CURRENT: ${(config.aiPolicyBlocklist ?? DEFAULT_CONFIG.aiPolicyBlocklist ?? []).join(", ")}`);
|
|
14816
|
-
console.log("DEFAULT: matplotlib/matplotlib");
|
|
14817
|
-
console.log("TYPE: list");
|
|
14818
|
-
console.log("");
|
|
14819
|
-
console.log("---");
|
|
14820
|
-
console.log("END_SETUP_PROMPTS");
|
|
14368
|
+
return {
|
|
14369
|
+
setupComplete: true,
|
|
14370
|
+
config: {
|
|
14371
|
+
githubUsername: config.githubUsername,
|
|
14372
|
+
maxActivePRs: config.maxActivePRs,
|
|
14373
|
+
dormantThresholdDays: config.dormantThresholdDays,
|
|
14374
|
+
approachingDormantDays: config.approachingDormantDays,
|
|
14375
|
+
languages: config.languages,
|
|
14376
|
+
labels: config.labels
|
|
14377
|
+
}
|
|
14378
|
+
};
|
|
14821
14379
|
}
|
|
14380
|
+
return {
|
|
14381
|
+
setupRequired: true,
|
|
14382
|
+
prompts: [
|
|
14383
|
+
{
|
|
14384
|
+
setting: "username",
|
|
14385
|
+
prompt: "What is your GitHub username?",
|
|
14386
|
+
current: config.githubUsername || null,
|
|
14387
|
+
required: true,
|
|
14388
|
+
type: "string"
|
|
14389
|
+
},
|
|
14390
|
+
{
|
|
14391
|
+
setting: "maxActivePRs",
|
|
14392
|
+
prompt: "How many PRs do you want to work on at once?",
|
|
14393
|
+
current: config.maxActivePRs,
|
|
14394
|
+
default: 10,
|
|
14395
|
+
type: "number"
|
|
14396
|
+
},
|
|
14397
|
+
{
|
|
14398
|
+
setting: "dormantDays",
|
|
14399
|
+
prompt: "After how many days of inactivity should a PR be considered dormant?",
|
|
14400
|
+
current: config.dormantThresholdDays,
|
|
14401
|
+
default: 30,
|
|
14402
|
+
type: "number"
|
|
14403
|
+
},
|
|
14404
|
+
{
|
|
14405
|
+
setting: "approachingDays",
|
|
14406
|
+
prompt: "At how many days should we warn about approaching dormancy?",
|
|
14407
|
+
current: config.approachingDormantDays,
|
|
14408
|
+
default: 25,
|
|
14409
|
+
type: "number"
|
|
14410
|
+
},
|
|
14411
|
+
{
|
|
14412
|
+
setting: "languages",
|
|
14413
|
+
prompt: "What programming languages do you want to contribute to?",
|
|
14414
|
+
current: config.languages,
|
|
14415
|
+
default: ["typescript", "javascript"],
|
|
14416
|
+
type: "list"
|
|
14417
|
+
},
|
|
14418
|
+
{
|
|
14419
|
+
setting: "labels",
|
|
14420
|
+
prompt: "What issue labels should we search for?",
|
|
14421
|
+
current: config.labels,
|
|
14422
|
+
default: ["good first issue", "help wanted"],
|
|
14423
|
+
type: "list"
|
|
14424
|
+
},
|
|
14425
|
+
{
|
|
14426
|
+
setting: "aiPolicyBlocklist",
|
|
14427
|
+
prompt: "Repos with anti-AI contribution policies to block (owner/repo, comma-separated)?",
|
|
14428
|
+
current: config.aiPolicyBlocklist ?? DEFAULT_CONFIG.aiPolicyBlocklist ?? null,
|
|
14429
|
+
default: ["matplotlib/matplotlib"],
|
|
14430
|
+
type: "list"
|
|
14431
|
+
}
|
|
14432
|
+
]
|
|
14433
|
+
};
|
|
14822
14434
|
}
|
|
14823
|
-
async function runCheckSetup(
|
|
14435
|
+
async function runCheckSetup() {
|
|
14824
14436
|
const stateManager2 = getStateManager();
|
|
14825
|
-
|
|
14826
|
-
|
|
14827
|
-
|
|
14828
|
-
|
|
14829
|
-
});
|
|
14830
|
-
} else {
|
|
14831
|
-
if (stateManager2.isSetupComplete()) {
|
|
14832
|
-
console.log("SETUP_COMPLETE");
|
|
14833
|
-
console.log(`username=${stateManager2.getState().config.githubUsername}`);
|
|
14834
|
-
} else {
|
|
14835
|
-
console.log("SETUP_INCOMPLETE");
|
|
14836
|
-
}
|
|
14837
|
-
}
|
|
14437
|
+
return {
|
|
14438
|
+
setupComplete: stateManager2.isSetupComplete(),
|
|
14439
|
+
username: stateManager2.getState().config.githubUsername
|
|
14440
|
+
};
|
|
14838
14441
|
}
|
|
14839
14442
|
var init_setup = __esm({
|
|
14840
14443
|
"src/commands/setup.ts"() {
|
|
14841
14444
|
"use strict";
|
|
14842
14445
|
init_core();
|
|
14843
|
-
init_json();
|
|
14844
14446
|
init_validation();
|
|
14845
14447
|
}
|
|
14846
14448
|
});
|
|
@@ -14853,17 +14455,17 @@ async function fetchDashboardData(token) {
|
|
|
14853
14455
|
const [{ prs, failures }, recentlyClosedPRs, recentlyMergedPRs, mergedResult, closedResult, fetchedIssues] = await Promise.all([
|
|
14854
14456
|
prMonitor.fetchUserOpenPRs(),
|
|
14855
14457
|
prMonitor.fetchRecentlyClosedPRs().catch((err) => {
|
|
14856
|
-
console.error(`Warning: Failed to fetch recently closed PRs: ${err
|
|
14458
|
+
console.error(`Warning: Failed to fetch recently closed PRs: ${errorMessage(err)}`);
|
|
14857
14459
|
return [];
|
|
14858
14460
|
}),
|
|
14859
14461
|
prMonitor.fetchRecentlyMergedPRs().catch((err) => {
|
|
14860
|
-
console.error(`Warning: Failed to fetch recently merged PRs: ${err
|
|
14462
|
+
console.error(`Warning: Failed to fetch recently merged PRs: ${errorMessage(err)}`);
|
|
14861
14463
|
return [];
|
|
14862
14464
|
}),
|
|
14863
14465
|
prMonitor.fetchUserMergedPRCounts(),
|
|
14864
14466
|
prMonitor.fetchUserClosedPRCounts(),
|
|
14865
14467
|
issueMonitor.fetchCommentedIssues().catch((error) => {
|
|
14866
|
-
const msg =
|
|
14468
|
+
const msg = errorMessage(error);
|
|
14867
14469
|
if (msg.includes("No GitHub username configured")) {
|
|
14868
14470
|
console.error(`[DASHBOARD] Issue conversation tracking requires setup: ${msg}`);
|
|
14869
14471
|
} else {
|
|
@@ -14887,12 +14489,12 @@ async function fetchDashboardData(token) {
|
|
|
14887
14489
|
try {
|
|
14888
14490
|
stateManager2.setMonthlyMergedCounts(monthlyCounts);
|
|
14889
14491
|
} catch (error) {
|
|
14890
|
-
console.error("[DASHBOARD] Failed to store monthly merged counts:", error
|
|
14492
|
+
console.error("[DASHBOARD] Failed to store monthly merged counts:", errorMessage(error));
|
|
14891
14493
|
}
|
|
14892
14494
|
try {
|
|
14893
14495
|
stateManager2.setMonthlyClosedCounts(monthlyClosedCounts);
|
|
14894
14496
|
} catch (error) {
|
|
14895
|
-
console.error("[DASHBOARD] Failed to store monthly closed counts:", error
|
|
14497
|
+
console.error("[DASHBOARD] Failed to store monthly closed counts:", errorMessage(error));
|
|
14896
14498
|
}
|
|
14897
14499
|
try {
|
|
14898
14500
|
const combinedOpenedCounts = { ...openedFromMerged };
|
|
@@ -14907,7 +14509,7 @@ async function fetchDashboardData(token) {
|
|
|
14907
14509
|
}
|
|
14908
14510
|
stateManager2.setMonthlyOpenedCounts(combinedOpenedCounts);
|
|
14909
14511
|
} catch (error) {
|
|
14910
|
-
console.error("[DASHBOARD] Failed to store monthly opened counts:", error
|
|
14512
|
+
console.error("[DASHBOARD] Failed to store monthly opened counts:", errorMessage(error));
|
|
14911
14513
|
}
|
|
14912
14514
|
const digest = prMonitor.generateDigest(prs, recentlyClosedPRs, recentlyMergedPRs);
|
|
14913
14515
|
const shelvedUrls = new Set(stateManager2.getState().config.shelvedPRUrls || []);
|
|
@@ -14947,6 +14549,7 @@ var init_dashboard_data = __esm({
|
|
|
14947
14549
|
"src/commands/dashboard-data.ts"() {
|
|
14948
14550
|
"use strict";
|
|
14949
14551
|
init_core();
|
|
14552
|
+
init_errors();
|
|
14950
14553
|
init_daily();
|
|
14951
14554
|
}
|
|
14952
14555
|
});
|
|
@@ -16532,64 +16135,380 @@ var init_dashboard_templates = __esm({
|
|
|
16532
16135
|
}
|
|
16533
16136
|
});
|
|
16534
16137
|
|
|
16535
|
-
// src/commands/dashboard.ts
|
|
16536
|
-
var
|
|
16537
|
-
__export(
|
|
16538
|
-
|
|
16539
|
-
writeDashboardFromState: () => writeDashboardFromState
|
|
16138
|
+
// src/commands/dashboard-server.ts
|
|
16139
|
+
var dashboard_server_exports = {};
|
|
16140
|
+
__export(dashboard_server_exports, {
|
|
16141
|
+
startDashboardServer: () => startDashboardServer
|
|
16540
16142
|
});
|
|
16541
|
-
|
|
16542
|
-
const
|
|
16543
|
-
const
|
|
16544
|
-
|
|
16545
|
-
|
|
16546
|
-
|
|
16547
|
-
|
|
16548
|
-
|
|
16549
|
-
|
|
16550
|
-
|
|
16551
|
-
|
|
16552
|
-
|
|
16553
|
-
|
|
16143
|
+
function buildDashboardJson(digest, state, commentedIssues) {
|
|
16144
|
+
const prsByRepo = computePRsByRepo(digest, state);
|
|
16145
|
+
const topRepos = computeTopRepos(prsByRepo);
|
|
16146
|
+
const { monthlyMerged } = getMonthlyData(state);
|
|
16147
|
+
const stats = buildDashboardStats(digest, state);
|
|
16148
|
+
const issueResponses = commentedIssues.filter((i) => i.status === "new_response");
|
|
16149
|
+
return {
|
|
16150
|
+
stats,
|
|
16151
|
+
prsByRepo,
|
|
16152
|
+
topRepos: topRepos.map(([repo, data]) => ({ repo, ...data })),
|
|
16153
|
+
monthlyMerged,
|
|
16154
|
+
activePRs: digest.openPRs || [],
|
|
16155
|
+
shelvedPRUrls: state.config.shelvedPRUrls || [],
|
|
16156
|
+
commentedIssues,
|
|
16157
|
+
issueResponses
|
|
16158
|
+
};
|
|
16159
|
+
}
|
|
16160
|
+
function readBody(req, maxBytes = MAX_BODY_BYTES) {
|
|
16161
|
+
return new Promise((resolve5, reject) => {
|
|
16162
|
+
const chunks = [];
|
|
16163
|
+
let totalLength = 0;
|
|
16164
|
+
let aborted = false;
|
|
16165
|
+
req.on("data", (chunk) => {
|
|
16166
|
+
if (aborted) return;
|
|
16167
|
+
totalLength += chunk.length;
|
|
16168
|
+
if (totalLength > maxBytes) {
|
|
16169
|
+
aborted = true;
|
|
16170
|
+
req.destroy();
|
|
16171
|
+
reject(new Error("Body too large"));
|
|
16172
|
+
return;
|
|
16554
16173
|
}
|
|
16555
|
-
|
|
16556
|
-
}
|
|
16557
|
-
|
|
16558
|
-
|
|
16559
|
-
|
|
16560
|
-
|
|
16174
|
+
chunks.push(chunk);
|
|
16175
|
+
});
|
|
16176
|
+
req.on("end", () => {
|
|
16177
|
+
if (!aborted) resolve5(Buffer.concat(chunks).toString("utf-8"));
|
|
16178
|
+
});
|
|
16179
|
+
req.on("error", (err) => {
|
|
16180
|
+
if (!aborted) reject(err);
|
|
16181
|
+
});
|
|
16182
|
+
});
|
|
16183
|
+
}
|
|
16184
|
+
function sendJson(res, statusCode, data) {
|
|
16185
|
+
const body = JSON.stringify(data);
|
|
16186
|
+
res.writeHead(statusCode, {
|
|
16187
|
+
"Content-Type": "application/json",
|
|
16188
|
+
"Content-Length": Buffer.byteLength(body)
|
|
16189
|
+
});
|
|
16190
|
+
res.end(body);
|
|
16191
|
+
}
|
|
16192
|
+
function sendError(res, statusCode, message) {
|
|
16193
|
+
sendJson(res, statusCode, { error: message });
|
|
16194
|
+
}
|
|
16195
|
+
async function startDashboardServer(options) {
|
|
16196
|
+
const { port: requestedPort, assetsDir, token, open } = options;
|
|
16197
|
+
const stateManager2 = getStateManager();
|
|
16198
|
+
const resolvedAssetsDir = path5.resolve(assetsDir);
|
|
16199
|
+
let cachedDigest;
|
|
16200
|
+
let cachedCommentedIssues = [];
|
|
16201
|
+
if (token) {
|
|
16561
16202
|
try {
|
|
16203
|
+
console.error("Fetching dashboard data from GitHub...");
|
|
16562
16204
|
const result = await fetchDashboardData(token);
|
|
16563
|
-
|
|
16564
|
-
|
|
16205
|
+
cachedDigest = result.digest;
|
|
16206
|
+
cachedCommentedIssues = result.commentedIssues;
|
|
16565
16207
|
} catch (error) {
|
|
16566
|
-
console.error("Failed to fetch
|
|
16567
|
-
console.error("Falling back to cached data
|
|
16568
|
-
|
|
16208
|
+
console.error("Failed to fetch data from GitHub:", error);
|
|
16209
|
+
console.error("Falling back to cached data...");
|
|
16210
|
+
cachedDigest = stateManager2.getState().lastDigest;
|
|
16569
16211
|
}
|
|
16570
16212
|
} else {
|
|
16571
|
-
|
|
16213
|
+
cachedDigest = stateManager2.getState().lastDigest;
|
|
16572
16214
|
}
|
|
16573
|
-
if (!
|
|
16574
|
-
|
|
16575
|
-
|
|
16576
|
-
|
|
16577
|
-
console.error("No dashboard data available. Run the daily check first:");
|
|
16578
|
-
console.error(" GITHUB_TOKEN=$(gh auth token) npm start -- daily");
|
|
16579
|
-
}
|
|
16580
|
-
return;
|
|
16215
|
+
if (!cachedDigest) {
|
|
16216
|
+
console.error("No dashboard data available. Run the daily check first:");
|
|
16217
|
+
console.error(" GITHUB_TOKEN=$(gh auth token) npm start -- daily");
|
|
16218
|
+
process.exit(1);
|
|
16581
16219
|
}
|
|
16582
|
-
|
|
16583
|
-
|
|
16584
|
-
|
|
16585
|
-
|
|
16586
|
-
|
|
16587
|
-
|
|
16588
|
-
|
|
16589
|
-
|
|
16590
|
-
|
|
16591
|
-
|
|
16592
|
-
|
|
16220
|
+
let cachedJsonData;
|
|
16221
|
+
try {
|
|
16222
|
+
cachedJsonData = buildDashboardJson(cachedDigest, stateManager2.getState(), cachedCommentedIssues);
|
|
16223
|
+
} catch (error) {
|
|
16224
|
+
console.error("Failed to build dashboard data from cached digest:", error);
|
|
16225
|
+
console.error("Your state data may be corrupted. Try running: daily --json");
|
|
16226
|
+
process.exit(1);
|
|
16227
|
+
}
|
|
16228
|
+
const server = http.createServer(async (req, res) => {
|
|
16229
|
+
const method = req.method || "GET";
|
|
16230
|
+
const url = req.url || "/";
|
|
16231
|
+
try {
|
|
16232
|
+
if (url === "/api/data" && method === "GET") {
|
|
16233
|
+
sendJson(res, 200, cachedJsonData);
|
|
16234
|
+
return;
|
|
16235
|
+
}
|
|
16236
|
+
if (url === "/api/action" && method === "POST") {
|
|
16237
|
+
await handleAction(req, res);
|
|
16238
|
+
return;
|
|
16239
|
+
}
|
|
16240
|
+
if (url === "/api/refresh" && method === "POST") {
|
|
16241
|
+
await handleRefresh(req, res);
|
|
16242
|
+
return;
|
|
16243
|
+
}
|
|
16244
|
+
if (method === "GET") {
|
|
16245
|
+
serveStaticFile(url, res);
|
|
16246
|
+
return;
|
|
16247
|
+
}
|
|
16248
|
+
sendError(res, 405, "Method not allowed");
|
|
16249
|
+
} catch (error) {
|
|
16250
|
+
console.error("Unhandled request error:", method, url, error);
|
|
16251
|
+
if (!res.headersSent) {
|
|
16252
|
+
sendError(res, 500, "Internal server error");
|
|
16253
|
+
}
|
|
16254
|
+
}
|
|
16255
|
+
});
|
|
16256
|
+
async function handleAction(req, res) {
|
|
16257
|
+
let body;
|
|
16258
|
+
try {
|
|
16259
|
+
const raw = await readBody(req);
|
|
16260
|
+
body = JSON.parse(raw);
|
|
16261
|
+
} catch (e) {
|
|
16262
|
+
const isBodyTooLarge = e instanceof Error && e.message === "Body too large";
|
|
16263
|
+
sendError(res, isBodyTooLarge ? 413 : 400, isBodyTooLarge ? "Request body too large" : "Invalid JSON body");
|
|
16264
|
+
return;
|
|
16265
|
+
}
|
|
16266
|
+
if (!body.action || !VALID_ACTIONS.has(body.action)) {
|
|
16267
|
+
sendError(res, 400, `Invalid action. Must be one of: ${[...VALID_ACTIONS].join(", ")}`);
|
|
16268
|
+
return;
|
|
16269
|
+
}
|
|
16270
|
+
if (!body.url || typeof body.url !== "string") {
|
|
16271
|
+
sendError(res, 400, 'Missing or invalid "url" field');
|
|
16272
|
+
return;
|
|
16273
|
+
}
|
|
16274
|
+
try {
|
|
16275
|
+
switch (body.action) {
|
|
16276
|
+
case "shelve":
|
|
16277
|
+
stateManager2.shelvePR(body.url);
|
|
16278
|
+
break;
|
|
16279
|
+
case "unshelve":
|
|
16280
|
+
stateManager2.unshelvePR(body.url);
|
|
16281
|
+
break;
|
|
16282
|
+
case "snooze":
|
|
16283
|
+
stateManager2.snoozePR(body.url, body.reason || "Snoozed via dashboard", body.days || 7);
|
|
16284
|
+
break;
|
|
16285
|
+
case "unsnooze":
|
|
16286
|
+
stateManager2.unsnoozePR(body.url);
|
|
16287
|
+
break;
|
|
16288
|
+
}
|
|
16289
|
+
stateManager2.save();
|
|
16290
|
+
} catch (error) {
|
|
16291
|
+
console.error("Action failed:", body.action, body.url, error);
|
|
16292
|
+
sendError(res, 500, `Action failed: ${errorMessage(error)}`);
|
|
16293
|
+
return;
|
|
16294
|
+
}
|
|
16295
|
+
if (cachedDigest) {
|
|
16296
|
+
cachedJsonData = buildDashboardJson(cachedDigest, stateManager2.getState(), cachedCommentedIssues);
|
|
16297
|
+
}
|
|
16298
|
+
sendJson(res, 200, cachedJsonData);
|
|
16299
|
+
}
|
|
16300
|
+
async function handleRefresh(_req, res) {
|
|
16301
|
+
const currentToken = token || getGitHubToken();
|
|
16302
|
+
if (!currentToken) {
|
|
16303
|
+
sendError(res, 401, "No GitHub token available. Cannot refresh data.");
|
|
16304
|
+
return;
|
|
16305
|
+
}
|
|
16306
|
+
try {
|
|
16307
|
+
console.error("Refreshing dashboard data from GitHub...");
|
|
16308
|
+
const result = await fetchDashboardData(currentToken);
|
|
16309
|
+
cachedDigest = result.digest;
|
|
16310
|
+
cachedCommentedIssues = result.commentedIssues;
|
|
16311
|
+
cachedJsonData = buildDashboardJson(cachedDigest, stateManager2.getState(), cachedCommentedIssues);
|
|
16312
|
+
sendJson(res, 200, cachedJsonData);
|
|
16313
|
+
} catch (error) {
|
|
16314
|
+
console.error("Dashboard refresh failed:", error);
|
|
16315
|
+
sendError(res, 500, `Refresh failed: ${errorMessage(error)}`);
|
|
16316
|
+
}
|
|
16317
|
+
}
|
|
16318
|
+
function serveStaticFile(requestUrl, res) {
|
|
16319
|
+
let urlPath;
|
|
16320
|
+
try {
|
|
16321
|
+
urlPath = decodeURIComponent(requestUrl.split("?")[0]);
|
|
16322
|
+
} catch (err) {
|
|
16323
|
+
console.error("Malformed URL received:", requestUrl, err);
|
|
16324
|
+
sendError(res, 400, "Malformed URL");
|
|
16325
|
+
return;
|
|
16326
|
+
}
|
|
16327
|
+
if (urlPath.includes("..")) {
|
|
16328
|
+
sendError(res, 403, "Forbidden");
|
|
16329
|
+
return;
|
|
16330
|
+
}
|
|
16331
|
+
const relativePath = urlPath === "/" ? "index.html" : urlPath.replace(/^\/+/, "");
|
|
16332
|
+
let filePath = path5.join(resolvedAssetsDir, relativePath);
|
|
16333
|
+
if (!filePath.startsWith(resolvedAssetsDir + path5.sep) && filePath !== resolvedAssetsDir) {
|
|
16334
|
+
sendError(res, 403, "Forbidden");
|
|
16335
|
+
return;
|
|
16336
|
+
}
|
|
16337
|
+
try {
|
|
16338
|
+
const stat = fs5.statSync(filePath);
|
|
16339
|
+
if (stat.isDirectory()) {
|
|
16340
|
+
filePath = path5.join(resolvedAssetsDir, "index.html");
|
|
16341
|
+
}
|
|
16342
|
+
} catch (err) {
|
|
16343
|
+
const nodeErr = err;
|
|
16344
|
+
if (nodeErr.code === "ENOENT") {
|
|
16345
|
+
filePath = path5.join(resolvedAssetsDir, "index.html");
|
|
16346
|
+
} else {
|
|
16347
|
+
console.error("Failed to stat file:", filePath, err);
|
|
16348
|
+
sendError(res, 500, "Internal server error");
|
|
16349
|
+
return;
|
|
16350
|
+
}
|
|
16351
|
+
}
|
|
16352
|
+
const ext = path5.extname(filePath).toLowerCase();
|
|
16353
|
+
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
16354
|
+
try {
|
|
16355
|
+
const content = fs5.readFileSync(filePath);
|
|
16356
|
+
res.writeHead(200, {
|
|
16357
|
+
"Content-Type": contentType,
|
|
16358
|
+
"Content-Length": content.length
|
|
16359
|
+
});
|
|
16360
|
+
res.end(content);
|
|
16361
|
+
} catch (error) {
|
|
16362
|
+
const nodeErr = error;
|
|
16363
|
+
if (nodeErr.code === "ENOENT") {
|
|
16364
|
+
sendError(res, 404, "Not found");
|
|
16365
|
+
} else {
|
|
16366
|
+
console.error("Failed to serve static file:", filePath, error);
|
|
16367
|
+
sendError(res, 500, "Failed to read file");
|
|
16368
|
+
}
|
|
16369
|
+
}
|
|
16370
|
+
}
|
|
16371
|
+
const MAX_PORT_ATTEMPTS = 10;
|
|
16372
|
+
let actualPort = requestedPort;
|
|
16373
|
+
for (let attempt = 0; attempt < MAX_PORT_ATTEMPTS; attempt++) {
|
|
16374
|
+
try {
|
|
16375
|
+
await new Promise((resolve5, reject) => {
|
|
16376
|
+
server.once("error", reject);
|
|
16377
|
+
server.listen(actualPort, "127.0.0.1", () => resolve5());
|
|
16378
|
+
});
|
|
16379
|
+
break;
|
|
16380
|
+
} catch (err) {
|
|
16381
|
+
const nodeErr = err;
|
|
16382
|
+
if (nodeErr.code === "EADDRINUSE" && attempt < MAX_PORT_ATTEMPTS - 1) {
|
|
16383
|
+
console.error(`Port ${actualPort} is in use, trying ${actualPort + 1}...`);
|
|
16384
|
+
actualPort++;
|
|
16385
|
+
continue;
|
|
16386
|
+
}
|
|
16387
|
+
console.error(`Failed to start server: ${nodeErr.message}`);
|
|
16388
|
+
process.exit(1);
|
|
16389
|
+
}
|
|
16390
|
+
}
|
|
16391
|
+
const serverUrl = `http://localhost:${actualPort}`;
|
|
16392
|
+
console.error(`Dashboard server running at ${serverUrl}`);
|
|
16393
|
+
if (open) {
|
|
16394
|
+
const { execFile: execFile4 } = await import("child_process");
|
|
16395
|
+
let openCmd;
|
|
16396
|
+
let args;
|
|
16397
|
+
switch (process.platform) {
|
|
16398
|
+
case "darwin":
|
|
16399
|
+
openCmd = "open";
|
|
16400
|
+
args = [serverUrl];
|
|
16401
|
+
break;
|
|
16402
|
+
case "win32":
|
|
16403
|
+
openCmd = "cmd";
|
|
16404
|
+
args = ["/c", "start", "", serverUrl];
|
|
16405
|
+
break;
|
|
16406
|
+
default:
|
|
16407
|
+
openCmd = "xdg-open";
|
|
16408
|
+
args = [serverUrl];
|
|
16409
|
+
break;
|
|
16410
|
+
}
|
|
16411
|
+
execFile4(openCmd, args, (error) => {
|
|
16412
|
+
if (error) {
|
|
16413
|
+
console.error("Failed to open browser:", error.message);
|
|
16414
|
+
console.error(`Open manually: ${serverUrl}`);
|
|
16415
|
+
}
|
|
16416
|
+
});
|
|
16417
|
+
}
|
|
16418
|
+
const shutdown = () => {
|
|
16419
|
+
console.error("\nShutting down dashboard server...");
|
|
16420
|
+
server.close(() => {
|
|
16421
|
+
process.exit(0);
|
|
16422
|
+
});
|
|
16423
|
+
setTimeout(() => process.exit(0), 3e3).unref();
|
|
16424
|
+
};
|
|
16425
|
+
process.on("SIGINT", shutdown);
|
|
16426
|
+
process.on("SIGTERM", shutdown);
|
|
16427
|
+
}
|
|
16428
|
+
var http, fs5, path5, VALID_ACTIONS, MAX_BODY_BYTES, MIME_TYPES;
|
|
16429
|
+
var init_dashboard_server = __esm({
|
|
16430
|
+
"src/commands/dashboard-server.ts"() {
|
|
16431
|
+
"use strict";
|
|
16432
|
+
http = __toESM(require("http"), 1);
|
|
16433
|
+
fs5 = __toESM(require("fs"), 1);
|
|
16434
|
+
path5 = __toESM(require("path"), 1);
|
|
16435
|
+
init_core();
|
|
16436
|
+
init_errors();
|
|
16437
|
+
init_dashboard_data();
|
|
16438
|
+
init_dashboard_templates();
|
|
16439
|
+
VALID_ACTIONS = /* @__PURE__ */ new Set(["shelve", "unshelve", "snooze", "unsnooze"]);
|
|
16440
|
+
MAX_BODY_BYTES = 10240;
|
|
16441
|
+
MIME_TYPES = {
|
|
16442
|
+
".html": "text/html",
|
|
16443
|
+
".js": "application/javascript",
|
|
16444
|
+
".css": "text/css",
|
|
16445
|
+
".svg": "image/svg+xml",
|
|
16446
|
+
".json": "application/json",
|
|
16447
|
+
".png": "image/png",
|
|
16448
|
+
".ico": "image/x-icon"
|
|
16449
|
+
};
|
|
16450
|
+
}
|
|
16451
|
+
});
|
|
16452
|
+
|
|
16453
|
+
// src/commands/dashboard.ts
|
|
16454
|
+
var dashboard_exports = {};
|
|
16455
|
+
__export(dashboard_exports, {
|
|
16456
|
+
runDashboard: () => runDashboard,
|
|
16457
|
+
serveDashboard: () => serveDashboard,
|
|
16458
|
+
writeDashboardFromState: () => writeDashboardFromState
|
|
16459
|
+
});
|
|
16460
|
+
async function runDashboard(options) {
|
|
16461
|
+
const stateManager2 = getStateManager();
|
|
16462
|
+
const token = options.offline ? null : getGitHubToken();
|
|
16463
|
+
let digest;
|
|
16464
|
+
let commentedIssues = [];
|
|
16465
|
+
if (options.offline) {
|
|
16466
|
+
const state2 = stateManager2.getState();
|
|
16467
|
+
digest = state2.lastDigest;
|
|
16468
|
+
if (!digest) {
|
|
16469
|
+
if (options.json) {
|
|
16470
|
+
outputJson({ error: "No cached data found. Run without --offline first.", offline: true });
|
|
16471
|
+
} else {
|
|
16472
|
+
console.error("No cached data found. Run without --offline first.");
|
|
16473
|
+
}
|
|
16474
|
+
return;
|
|
16475
|
+
}
|
|
16476
|
+
const lastUpdated = digest.generatedAt || state2.lastDigestAt || state2.lastRunAt;
|
|
16477
|
+
console.error(`Offline mode: using cached data from ${lastUpdated}`);
|
|
16478
|
+
} else if (token) {
|
|
16479
|
+
console.error("Fetching fresh data from GitHub...");
|
|
16480
|
+
try {
|
|
16481
|
+
const result = await fetchDashboardData(token);
|
|
16482
|
+
digest = result.digest;
|
|
16483
|
+
commentedIssues = result.commentedIssues;
|
|
16484
|
+
} catch (error) {
|
|
16485
|
+
console.error("Failed to fetch fresh data:", errorMessage(error));
|
|
16486
|
+
console.error("Falling back to cached data (issue conversations unavailable)...");
|
|
16487
|
+
digest = stateManager2.getState().lastDigest;
|
|
16488
|
+
}
|
|
16489
|
+
} else {
|
|
16490
|
+
digest = stateManager2.getState().lastDigest;
|
|
16491
|
+
}
|
|
16492
|
+
if (!digest) {
|
|
16493
|
+
if (options.json) {
|
|
16494
|
+
outputJson({ error: "No data available. Run daily check first with GITHUB_TOKEN." });
|
|
16495
|
+
} else {
|
|
16496
|
+
console.error("No dashboard data available. Run the daily check first:");
|
|
16497
|
+
console.error(" GITHUB_TOKEN=$(gh auth token) npm start -- daily");
|
|
16498
|
+
}
|
|
16499
|
+
return;
|
|
16500
|
+
}
|
|
16501
|
+
const state = stateManager2.getState();
|
|
16502
|
+
const prsByRepo = computePRsByRepo(digest, state);
|
|
16503
|
+
const topRepos = computeTopRepos(prsByRepo);
|
|
16504
|
+
const { monthlyMerged, monthlyClosed, monthlyOpened } = getMonthlyData(state);
|
|
16505
|
+
const stats = buildDashboardStats(digest, state);
|
|
16506
|
+
if (options.json) {
|
|
16507
|
+
const issueResponses2 = commentedIssues.filter((i) => i.status === "new_response");
|
|
16508
|
+
const jsonData = {
|
|
16509
|
+
stats,
|
|
16510
|
+
prsByRepo,
|
|
16511
|
+
topRepos: topRepos.map(([repo, data]) => ({ repo, ...data })),
|
|
16593
16512
|
monthlyMerged,
|
|
16594
16513
|
activePRs: digest.openPRs || [],
|
|
16595
16514
|
commentedIssues,
|
|
@@ -16605,8 +16524,8 @@ async function runDashboard(options) {
|
|
|
16605
16524
|
const issueResponses = commentedIssues.filter((i) => i.status === "new_response");
|
|
16606
16525
|
const html = generateDashboardHtml(stats, monthlyMerged, monthlyClosed, monthlyOpened, digest, state, issueResponses);
|
|
16607
16526
|
const dashboardPath = getDashboardPath();
|
|
16608
|
-
|
|
16609
|
-
|
|
16527
|
+
fs6.writeFileSync(dashboardPath, html, { mode: 420 });
|
|
16528
|
+
fs6.chmodSync(dashboardPath, 420);
|
|
16610
16529
|
if (options.offline) {
|
|
16611
16530
|
const lastUpdated = digest.generatedAt || state.lastDigestAt || state.lastRunAt;
|
|
16612
16531
|
console.log(`
|
|
@@ -16641,17 +16560,59 @@ function writeDashboardFromState() {
|
|
|
16641
16560
|
const stats = buildDashboardStats(digest, state);
|
|
16642
16561
|
const html = generateDashboardHtml(stats, monthlyMerged, monthlyClosed, monthlyOpened, digest, state);
|
|
16643
16562
|
const dashboardPath = getDashboardPath();
|
|
16644
|
-
|
|
16645
|
-
|
|
16563
|
+
fs6.writeFileSync(dashboardPath, html, { mode: 420 });
|
|
16564
|
+
fs6.chmodSync(dashboardPath, 420);
|
|
16646
16565
|
return dashboardPath;
|
|
16647
16566
|
}
|
|
16648
|
-
|
|
16567
|
+
function resolveAssetsDir() {
|
|
16568
|
+
const devPath = path6.resolve(__dirname, "../../dashboard/dist");
|
|
16569
|
+
if (fs6.existsSync(path6.join(devPath, "index.html"))) {
|
|
16570
|
+
return devPath;
|
|
16571
|
+
}
|
|
16572
|
+
const bundlePath = path6.resolve(path6.dirname(process.argv[1]), "../../dashboard/dist");
|
|
16573
|
+
if (fs6.existsSync(path6.join(bundlePath, "index.html"))) {
|
|
16574
|
+
return bundlePath;
|
|
16575
|
+
}
|
|
16576
|
+
try {
|
|
16577
|
+
const dashboardPkgPath = require.resolve("@oss-autopilot/dashboard/package.json");
|
|
16578
|
+
const dashboardDist = path6.join(path6.dirname(dashboardPkgPath), "dist");
|
|
16579
|
+
if (fs6.existsSync(path6.join(dashboardDist, "index.html"))) {
|
|
16580
|
+
return dashboardDist;
|
|
16581
|
+
}
|
|
16582
|
+
} catch (error) {
|
|
16583
|
+
const code = error.code;
|
|
16584
|
+
if (code !== "MODULE_NOT_FOUND") {
|
|
16585
|
+
console.error("Error resolving dashboard package:", error);
|
|
16586
|
+
}
|
|
16587
|
+
}
|
|
16588
|
+
return null;
|
|
16589
|
+
}
|
|
16590
|
+
async function serveDashboard(options) {
|
|
16591
|
+
const assetsDir = resolveAssetsDir();
|
|
16592
|
+
if (!assetsDir) {
|
|
16593
|
+
console.error("Could not find dashboard SPA assets.");
|
|
16594
|
+
console.error("Make sure packages/dashboard has been built:");
|
|
16595
|
+
console.error(" cd packages/dashboard && pnpm run build");
|
|
16596
|
+
process.exit(1);
|
|
16597
|
+
}
|
|
16598
|
+
const token = getGitHubToken();
|
|
16599
|
+
const { startDashboardServer: startDashboardServer2 } = await Promise.resolve().then(() => (init_dashboard_server(), dashboard_server_exports));
|
|
16600
|
+
await startDashboardServer2({
|
|
16601
|
+
port: options.port,
|
|
16602
|
+
assetsDir,
|
|
16603
|
+
token,
|
|
16604
|
+
open: options.open
|
|
16605
|
+
});
|
|
16606
|
+
}
|
|
16607
|
+
var fs6, path6, import_child_process2;
|
|
16649
16608
|
var init_dashboard = __esm({
|
|
16650
16609
|
"src/commands/dashboard.ts"() {
|
|
16651
16610
|
"use strict";
|
|
16652
|
-
|
|
16611
|
+
fs6 = __toESM(require("fs"), 1);
|
|
16612
|
+
path6 = __toESM(require("path"), 1);
|
|
16653
16613
|
import_child_process2 = require("child_process");
|
|
16654
16614
|
init_core();
|
|
16615
|
+
init_errors();
|
|
16655
16616
|
init_json();
|
|
16656
16617
|
init_dashboard_data();
|
|
16657
16618
|
init_dashboard_templates();
|
|
@@ -16682,7 +16643,7 @@ function extractTitle(line) {
|
|
|
16682
16643
|
cleaned = cleaned.replace(/\[[ xX]\]\s*/, "");
|
|
16683
16644
|
cleaned = cleaned.replace(/~~/g, "");
|
|
16684
16645
|
cleaned = cleaned.replace(/\b(Done|DONE|done)\b/g, "");
|
|
16685
|
-
cleaned = cleaned.replace(/^[\s
|
|
16646
|
+
cleaned = cleaned.replace(/^[\s\-\u2013\u2014:]+/, "").replace(/[\s\-\u2013\u2014:]+$/, "");
|
|
16686
16647
|
return cleaned.trim();
|
|
16687
16648
|
}
|
|
16688
16649
|
function isCompleted(line) {
|
|
@@ -16729,57 +16690,26 @@ function parseIssueList(content) {
|
|
|
16729
16690
|
};
|
|
16730
16691
|
}
|
|
16731
16692
|
async function runParseList(options) {
|
|
16732
|
-
const filePath =
|
|
16733
|
-
if (!
|
|
16734
|
-
|
|
16735
|
-
outputJsonError(`File not found: ${filePath}`);
|
|
16736
|
-
} else {
|
|
16737
|
-
console.error(`Error: File not found: ${filePath}`);
|
|
16738
|
-
}
|
|
16739
|
-
process.exit(1);
|
|
16693
|
+
const filePath = path7.resolve(options.filePath);
|
|
16694
|
+
if (!fs7.existsSync(filePath)) {
|
|
16695
|
+
throw new Error(`File not found: ${filePath}`);
|
|
16740
16696
|
}
|
|
16741
16697
|
let content;
|
|
16742
16698
|
try {
|
|
16743
|
-
content =
|
|
16699
|
+
content = fs7.readFileSync(filePath, "utf-8");
|
|
16744
16700
|
} catch (error) {
|
|
16745
|
-
const msg =
|
|
16746
|
-
|
|
16747
|
-
outputJsonError(`Failed to read file: ${msg}`);
|
|
16748
|
-
} else {
|
|
16749
|
-
console.error(`Error: Failed to read file: ${msg}`);
|
|
16750
|
-
}
|
|
16751
|
-
process.exit(1);
|
|
16752
|
-
}
|
|
16753
|
-
const result = parseIssueList(content);
|
|
16754
|
-
if (options.json) {
|
|
16755
|
-
outputJson(result);
|
|
16756
|
-
} else {
|
|
16757
|
-
console.log(`
|
|
16758
|
-
\u{1F4CB} Issue List: ${filePath}
|
|
16759
|
-
`);
|
|
16760
|
-
console.log(`Available: ${result.availableCount} | Completed: ${result.completedCount}
|
|
16761
|
-
`);
|
|
16762
|
-
if (result.available.length > 0) {
|
|
16763
|
-
console.log("--- Available ---");
|
|
16764
|
-
for (const item of result.available) {
|
|
16765
|
-
console.log(` [${item.tier}] ${item.repo}#${item.number}: ${item.title}`);
|
|
16766
|
-
}
|
|
16767
|
-
}
|
|
16768
|
-
if (result.completed.length > 0) {
|
|
16769
|
-
console.log("\n--- Completed ---");
|
|
16770
|
-
for (const item of result.completed) {
|
|
16771
|
-
console.log(` [${item.tier}] ${item.repo}#${item.number}: ${item.title}`);
|
|
16772
|
-
}
|
|
16773
|
-
}
|
|
16701
|
+
const msg = errorMessage(error);
|
|
16702
|
+
throw new Error(`Failed to read file: ${msg}`, { cause: error });
|
|
16774
16703
|
}
|
|
16704
|
+
return parseIssueList(content);
|
|
16775
16705
|
}
|
|
16776
|
-
var
|
|
16706
|
+
var fs7, path7;
|
|
16777
16707
|
var init_parse_list = __esm({
|
|
16778
16708
|
"src/commands/parse-list.ts"() {
|
|
16779
16709
|
"use strict";
|
|
16780
|
-
|
|
16781
|
-
|
|
16782
|
-
|
|
16710
|
+
fs7 = __toESM(require("fs"), 1);
|
|
16711
|
+
path7 = __toESM(require("path"), 1);
|
|
16712
|
+
init_errors();
|
|
16783
16713
|
}
|
|
16784
16714
|
});
|
|
16785
16715
|
|
|
@@ -16789,25 +16719,25 @@ __export(check_integration_exports, {
|
|
|
16789
16719
|
runCheckIntegration: () => runCheckIntegration
|
|
16790
16720
|
});
|
|
16791
16721
|
function getImportName(filePath) {
|
|
16792
|
-
const ext =
|
|
16793
|
-
const base =
|
|
16722
|
+
const ext = path8.extname(filePath);
|
|
16723
|
+
const base = path8.basename(filePath, ext);
|
|
16794
16724
|
if (base === "index") {
|
|
16795
|
-
return
|
|
16725
|
+
return path8.basename(path8.dirname(filePath));
|
|
16796
16726
|
}
|
|
16797
16727
|
return base;
|
|
16798
16728
|
}
|
|
16799
16729
|
function suggestEntryPoints(newFile, existingFiles) {
|
|
16800
|
-
const dir =
|
|
16730
|
+
const dir = path8.dirname(newFile);
|
|
16801
16731
|
const suggestions = [];
|
|
16802
16732
|
for (const ext of [".ts", ".tsx", ".js", ".jsx"]) {
|
|
16803
|
-
const indexFile =
|
|
16733
|
+
const indexFile = path8.join(dir, `index${ext}`);
|
|
16804
16734
|
if (existingFiles.includes(indexFile)) {
|
|
16805
16735
|
suggestions.push(indexFile);
|
|
16806
16736
|
}
|
|
16807
16737
|
}
|
|
16808
|
-
const parentDir =
|
|
16738
|
+
const parentDir = path8.dirname(dir);
|
|
16809
16739
|
for (const ext of [".ts", ".tsx", ".js", ".jsx"]) {
|
|
16810
|
-
const parentIndex =
|
|
16740
|
+
const parentIndex = path8.join(parentDir, `index${ext}`);
|
|
16811
16741
|
if (existingFiles.includes(parentIndex)) {
|
|
16812
16742
|
suggestions.push(parentIndex);
|
|
16813
16743
|
}
|
|
@@ -16818,33 +16748,22 @@ async function runCheckIntegration(options) {
|
|
|
16818
16748
|
const base = options.base;
|
|
16819
16749
|
let newFiles;
|
|
16820
16750
|
try {
|
|
16821
|
-
const
|
|
16751
|
+
const output = (0, import_child_process3.execFileSync)("git", ["diff", "--name-only", "--diff-filter=A", `${base}...HEAD`], {
|
|
16822
16752
|
encoding: "utf-8",
|
|
16823
16753
|
timeout: 1e4
|
|
16824
16754
|
}).trim();
|
|
16825
|
-
newFiles =
|
|
16755
|
+
newFiles = output ? output.split("\n").filter(Boolean) : [];
|
|
16826
16756
|
} catch (error) {
|
|
16827
|
-
const msg =
|
|
16828
|
-
|
|
16829
|
-
outputJsonError(`Failed to run git diff: ${msg}`);
|
|
16830
|
-
} else {
|
|
16831
|
-
console.error(`Error: Failed to run git diff: ${msg}`);
|
|
16832
|
-
}
|
|
16833
|
-
process.exit(1);
|
|
16757
|
+
const msg = errorMessage(error);
|
|
16758
|
+
throw new Error(`Failed to run git diff: ${msg}`, { cause: error });
|
|
16834
16759
|
}
|
|
16835
16760
|
const codeFiles = newFiles.filter((f) => {
|
|
16836
|
-
const ext =
|
|
16761
|
+
const ext = path8.extname(f);
|
|
16837
16762
|
if (!CODE_EXTENSIONS.has(ext)) return false;
|
|
16838
16763
|
return !IGNORED_PATTERNS.some((p) => p.test(f));
|
|
16839
16764
|
});
|
|
16840
16765
|
if (codeFiles.length === 0) {
|
|
16841
|
-
|
|
16842
|
-
if (options.json) {
|
|
16843
|
-
outputJson(result);
|
|
16844
|
-
} else {
|
|
16845
|
-
console.log("\nNo new code files to check.");
|
|
16846
|
-
}
|
|
16847
|
-
return;
|
|
16766
|
+
return { newFiles: [], unreferencedCount: 0 };
|
|
16848
16767
|
}
|
|
16849
16768
|
let allFiles;
|
|
16850
16769
|
try {
|
|
@@ -16879,10 +16798,10 @@ async function runCheckIntegration(options) {
|
|
|
16879
16798
|
referencedBy.push(...matches);
|
|
16880
16799
|
}
|
|
16881
16800
|
} catch (error) {
|
|
16882
|
-
const exitCode = error && typeof error === "object" && "status" in error ? error.status :
|
|
16883
|
-
if (exitCode !==
|
|
16884
|
-
const msg =
|
|
16885
|
-
|
|
16801
|
+
const exitCode = error && typeof error === "object" && "status" in error ? error.status : void 0;
|
|
16802
|
+
if (exitCode !== void 0 && exitCode !== 1) {
|
|
16803
|
+
const msg = errorMessage(error);
|
|
16804
|
+
debug("check-integration", `git grep failed for "${pattern}": ${msg}`);
|
|
16886
16805
|
}
|
|
16887
16806
|
}
|
|
16888
16807
|
}
|
|
@@ -16899,37 +16818,16 @@ async function runCheckIntegration(options) {
|
|
|
16899
16818
|
results.push(info);
|
|
16900
16819
|
}
|
|
16901
16820
|
const unreferencedCount = results.filter((r) => !r.isIntegrated).length;
|
|
16902
|
-
|
|
16903
|
-
if (options.json) {
|
|
16904
|
-
outputJson(output);
|
|
16905
|
-
} else {
|
|
16906
|
-
console.log(`
|
|
16907
|
-
\u{1F50D} Integration Check (base: ${base})
|
|
16908
|
-
`);
|
|
16909
|
-
console.log(`New files: ${results.length} | Unreferenced: ${unreferencedCount}
|
|
16910
|
-
`);
|
|
16911
|
-
for (const file of results) {
|
|
16912
|
-
const status = file.isIntegrated ? "\u2705" : "\u26A0\uFE0F";
|
|
16913
|
-
console.log(`${status} ${file.path}`);
|
|
16914
|
-
if (file.isIntegrated) {
|
|
16915
|
-
console.log(` Referenced by: ${file.referencedBy.join(", ")}`);
|
|
16916
|
-
} else {
|
|
16917
|
-
console.log(" Not referenced by any file");
|
|
16918
|
-
if (file.suggestedEntryPoints && file.suggestedEntryPoints.length > 0) {
|
|
16919
|
-
console.log(` Suggested entry points: ${file.suggestedEntryPoints.join(", ")}`);
|
|
16920
|
-
}
|
|
16921
|
-
}
|
|
16922
|
-
}
|
|
16923
|
-
}
|
|
16821
|
+
return { newFiles: results, unreferencedCount };
|
|
16924
16822
|
}
|
|
16925
|
-
var
|
|
16823
|
+
var path8, import_child_process3, CODE_EXTENSIONS, IGNORED_PATTERNS;
|
|
16926
16824
|
var init_check_integration = __esm({
|
|
16927
16825
|
"src/commands/check-integration.ts"() {
|
|
16928
16826
|
"use strict";
|
|
16929
|
-
|
|
16827
|
+
path8 = __toESM(require("path"), 1);
|
|
16930
16828
|
import_child_process3 = require("child_process");
|
|
16931
|
-
init_json();
|
|
16932
16829
|
init_core();
|
|
16830
|
+
init_errors();
|
|
16933
16831
|
CODE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
16934
16832
|
".ts",
|
|
16935
16833
|
".tsx",
|
|
@@ -17005,7 +16903,7 @@ function getCurrentBranch(repoPath) {
|
|
|
17005
16903
|
function scanForRepos(scanPaths) {
|
|
17006
16904
|
const repos = {};
|
|
17007
16905
|
for (const scanPath of scanPaths) {
|
|
17008
|
-
if (!
|
|
16906
|
+
if (!fs8.existsSync(scanPath)) continue;
|
|
17009
16907
|
let gitDirs;
|
|
17010
16908
|
try {
|
|
17011
16909
|
const output = (0, import_child_process4.execFileSync)("find", [scanPath, "-maxdepth", "4", "-name", ".git", "-type", "d"], {
|
|
@@ -17019,7 +16917,7 @@ function scanForRepos(scanPaths) {
|
|
|
17019
16917
|
continue;
|
|
17020
16918
|
}
|
|
17021
16919
|
for (const gitDir of gitDirs) {
|
|
17022
|
-
const repoPath =
|
|
16920
|
+
const repoPath = path9.dirname(gitDir);
|
|
17023
16921
|
const remote = getGitHubRemote(repoPath);
|
|
17024
16922
|
if (!remote) continue;
|
|
17025
16923
|
const currentBranch = getCurrentBranch(repoPath);
|
|
@@ -17035,79 +16933,49 @@ function scanForRepos(scanPaths) {
|
|
|
17035
16933
|
async function runLocalRepos(options) {
|
|
17036
16934
|
const stateManager2 = getStateManager();
|
|
17037
16935
|
const state = stateManager2.getState();
|
|
17038
|
-
const scanPaths = options.paths?.map((p) =>
|
|
16936
|
+
const scanPaths = options.paths?.map((p) => path9.resolve(p)) ?? state.config.localRepoScanPaths ?? DEFAULT_SCAN_PATHS.filter((p) => fs8.existsSync(p));
|
|
17039
16937
|
if (!options.scan && state.localRepoCache) {
|
|
17040
16938
|
const cache = state.localRepoCache;
|
|
17041
|
-
|
|
16939
|
+
return {
|
|
17042
16940
|
repos: cache.repos,
|
|
17043
16941
|
scanPaths: cache.scanPaths,
|
|
17044
16942
|
cachedAt: cache.cachedAt,
|
|
17045
16943
|
fromCache: true
|
|
17046
16944
|
};
|
|
17047
|
-
if (options.json) {
|
|
17048
|
-
outputJson(result2);
|
|
17049
|
-
} else {
|
|
17050
|
-
console.log(`
|
|
17051
|
-
\u{1F4C1} Local Repos (cached ${cache.cachedAt})
|
|
17052
|
-
`);
|
|
17053
|
-
printRepos(cache.repos);
|
|
17054
|
-
}
|
|
17055
|
-
return;
|
|
17056
|
-
}
|
|
17057
|
-
if (!options.json) {
|
|
17058
|
-
console.log(`
|
|
17059
|
-
\u{1F50D} Scanning for local repos in ${scanPaths.length} directories...
|
|
17060
|
-
`);
|
|
17061
16945
|
}
|
|
17062
16946
|
const repos = scanForRepos(scanPaths);
|
|
17063
|
-
const repoCount = Object.keys(repos).length;
|
|
17064
16947
|
const cachedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
17065
16948
|
try {
|
|
17066
16949
|
stateManager2.setLocalRepoCache({ repos, scanPaths, cachedAt });
|
|
17067
16950
|
stateManager2.save();
|
|
17068
16951
|
} catch (error) {
|
|
17069
|
-
const msg =
|
|
17070
|
-
|
|
16952
|
+
const msg = errorMessage(error);
|
|
16953
|
+
debug("local-repos", `Failed to cache scan results: ${msg}`);
|
|
17071
16954
|
}
|
|
17072
|
-
|
|
16955
|
+
return {
|
|
17073
16956
|
repos,
|
|
17074
16957
|
scanPaths,
|
|
17075
16958
|
cachedAt,
|
|
17076
16959
|
fromCache: false
|
|
17077
16960
|
};
|
|
17078
|
-
if (options.json) {
|
|
17079
|
-
outputJson(result);
|
|
17080
|
-
} else {
|
|
17081
|
-
console.log(`Found ${repoCount} repos:
|
|
17082
|
-
`);
|
|
17083
|
-
printRepos(repos);
|
|
17084
|
-
}
|
|
17085
|
-
}
|
|
17086
|
-
function printRepos(repos) {
|
|
17087
|
-
const entries = Object.entries(repos).sort(([a], [b]) => a.localeCompare(b));
|
|
17088
|
-
for (const [remote, info] of entries) {
|
|
17089
|
-
const branch = info.currentBranch ? ` (${info.currentBranch})` : "";
|
|
17090
|
-
console.log(` ${remote}${branch}`);
|
|
17091
|
-
console.log(` ${info.path}`);
|
|
17092
|
-
}
|
|
17093
16961
|
}
|
|
17094
|
-
var
|
|
16962
|
+
var fs8, path9, os2, import_child_process4, DEFAULT_SCAN_PATHS;
|
|
17095
16963
|
var init_local_repos = __esm({
|
|
17096
16964
|
"src/commands/local-repos.ts"() {
|
|
17097
16965
|
"use strict";
|
|
17098
|
-
|
|
17099
|
-
|
|
16966
|
+
fs8 = __toESM(require("fs"), 1);
|
|
16967
|
+
path9 = __toESM(require("path"), 1);
|
|
17100
16968
|
os2 = __toESM(require("os"), 1);
|
|
17101
16969
|
import_child_process4 = require("child_process");
|
|
17102
16970
|
init_core();
|
|
17103
|
-
|
|
16971
|
+
init_errors();
|
|
17104
16972
|
DEFAULT_SCAN_PATHS = [
|
|
17105
|
-
|
|
17106
|
-
|
|
17107
|
-
|
|
17108
|
-
|
|
17109
|
-
|
|
17110
|
-
|
|
16973
|
+
path9.join(os2.homedir(), "Documents", "oss"),
|
|
16974
|
+
path9.join(os2.homedir(), "dev"),
|
|
16975
|
+
path9.join(os2.homedir(), "projects"),
|
|
16976
|
+
path9.join(os2.homedir(), "src"),
|
|
16977
|
+
path9.join(os2.homedir(), "code"),
|
|
16978
|
+
path9.join(os2.homedir(), "repos")
|
|
17111
16979
|
];
|
|
17112
16980
|
}
|
|
17113
16981
|
});
|
|
@@ -17120,15 +16988,6 @@ __export(startup_exports, {
|
|
|
17120
16988
|
parseIssueListPathFromConfig: () => parseIssueListPathFromConfig,
|
|
17121
16989
|
runStartup: () => runStartup
|
|
17122
16990
|
});
|
|
17123
|
-
function getVersion() {
|
|
17124
|
-
try {
|
|
17125
|
-
const pkgPath = path8.join(path8.dirname(process.argv[1]), "..", "package.json");
|
|
17126
|
-
return JSON.parse(fs8.readFileSync(pkgPath, "utf-8")).version;
|
|
17127
|
-
} catch (error) {
|
|
17128
|
-
console.error("[STARTUP] Failed to detect CLI version:", error instanceof Error ? error.message : error);
|
|
17129
|
-
return "0.0.0";
|
|
17130
|
-
}
|
|
17131
|
-
}
|
|
17132
16991
|
function parseIssueListPathFromConfig(configContent) {
|
|
17133
16992
|
const match = configContent.match(/^---\n([\s\S]*?)\n---/);
|
|
17134
16993
|
if (!match) return void 0;
|
|
@@ -17155,22 +17014,22 @@ function detectIssueList() {
|
|
|
17155
17014
|
let issueListPath = "";
|
|
17156
17015
|
let source = "auto-detected";
|
|
17157
17016
|
const configPath = ".claude/oss-autopilot/config.md";
|
|
17158
|
-
if (
|
|
17017
|
+
if (fs9.existsSync(configPath)) {
|
|
17159
17018
|
try {
|
|
17160
|
-
const configContent =
|
|
17019
|
+
const configContent = fs9.readFileSync(configPath, "utf-8");
|
|
17161
17020
|
const configuredPath = parseIssueListPathFromConfig(configContent);
|
|
17162
|
-
if (configuredPath &&
|
|
17021
|
+
if (configuredPath && fs9.existsSync(configuredPath)) {
|
|
17163
17022
|
issueListPath = configuredPath;
|
|
17164
17023
|
source = "configured";
|
|
17165
17024
|
}
|
|
17166
17025
|
} catch (error) {
|
|
17167
|
-
console.error("[STARTUP] Failed to read config:", error
|
|
17026
|
+
console.error("[STARTUP] Failed to read config:", errorMessage(error));
|
|
17168
17027
|
}
|
|
17169
17028
|
}
|
|
17170
17029
|
if (!issueListPath) {
|
|
17171
17030
|
const probes = ["open-source/potential-issue-list.md", "oss/issue-list.md", "issues.md"];
|
|
17172
17031
|
for (const probe of probes) {
|
|
17173
|
-
if (
|
|
17032
|
+
if (fs9.existsSync(probe)) {
|
|
17174
17033
|
issueListPath = probe;
|
|
17175
17034
|
source = "auto-detected";
|
|
17176
17035
|
break;
|
|
@@ -17179,14 +17038,11 @@ function detectIssueList() {
|
|
|
17179
17038
|
}
|
|
17180
17039
|
if (!issueListPath) return void 0;
|
|
17181
17040
|
try {
|
|
17182
|
-
const content =
|
|
17041
|
+
const content = fs9.readFileSync(issueListPath, "utf-8");
|
|
17183
17042
|
const { availableCount, completedCount } = countIssueListItems(content);
|
|
17184
17043
|
return { path: issueListPath, source, availableCount, completedCount };
|
|
17185
17044
|
} catch (error) {
|
|
17186
|
-
console.error(
|
|
17187
|
-
`[STARTUP] Failed to read issue list at ${issueListPath}:`,
|
|
17188
|
-
error instanceof Error ? error.message : error
|
|
17189
|
-
);
|
|
17045
|
+
console.error(`[STARTUP] Failed to read issue list at ${issueListPath}:`, errorMessage(error));
|
|
17190
17046
|
return { path: issueListPath, source, availableCount: 0, completedCount: 0 };
|
|
17191
17047
|
}
|
|
17192
17048
|
}
|
|
@@ -17200,82 +17056,52 @@ function openInBrowser(filePath) {
|
|
|
17200
17056
|
}
|
|
17201
17057
|
});
|
|
17202
17058
|
}
|
|
17203
|
-
async function runStartup(
|
|
17204
|
-
const version =
|
|
17059
|
+
async function runStartup() {
|
|
17060
|
+
const version = getCLIVersion();
|
|
17205
17061
|
const stateManager2 = getStateManager();
|
|
17206
17062
|
if (!stateManager2.isSetupComplete()) {
|
|
17207
|
-
|
|
17208
|
-
outputJson({ version, setupComplete: false });
|
|
17209
|
-
} else {
|
|
17210
|
-
console.log("Setup incomplete. Run /setup-oss first.");
|
|
17211
|
-
}
|
|
17212
|
-
return;
|
|
17063
|
+
return { version, setupComplete: false };
|
|
17213
17064
|
}
|
|
17214
17065
|
const token = getGitHubToken();
|
|
17215
17066
|
if (!token) {
|
|
17216
|
-
|
|
17217
|
-
|
|
17218
|
-
|
|
17219
|
-
|
|
17220
|
-
|
|
17221
|
-
});
|
|
17222
|
-
} else {
|
|
17223
|
-
console.error("Error: GitHub authentication required.");
|
|
17224
|
-
}
|
|
17225
|
-
return;
|
|
17067
|
+
return {
|
|
17068
|
+
version,
|
|
17069
|
+
setupComplete: true,
|
|
17070
|
+
authError: 'GitHub authentication required. Install GitHub CLI (https://cli.github.com/) and run "gh auth login", or set GITHUB_TOKEN.'
|
|
17071
|
+
};
|
|
17226
17072
|
}
|
|
17073
|
+
const daily = await executeDailyCheck(token);
|
|
17074
|
+
let dashboardPath;
|
|
17075
|
+
let dashboardOpened = false;
|
|
17227
17076
|
try {
|
|
17228
|
-
|
|
17229
|
-
|
|
17230
|
-
|
|
17231
|
-
|
|
17232
|
-
dashboardPath = writeDashboardFromState();
|
|
17233
|
-
if (daily.digest.summary.totalActivePRs > 0) {
|
|
17234
|
-
openInBrowser(dashboardPath);
|
|
17235
|
-
dashboardOpened = true;
|
|
17236
|
-
}
|
|
17237
|
-
} catch (error) {
|
|
17238
|
-
console.error("[STARTUP] Dashboard generation failed:", error instanceof Error ? error.message : error);
|
|
17239
|
-
}
|
|
17240
|
-
if (dashboardOpened) {
|
|
17241
|
-
daily.briefSummary += " | Dashboard opened in browser";
|
|
17242
|
-
}
|
|
17243
|
-
const issueList = detectIssueList();
|
|
17244
|
-
if (options.json) {
|
|
17245
|
-
outputJson({
|
|
17246
|
-
version,
|
|
17247
|
-
setupComplete: true,
|
|
17248
|
-
daily,
|
|
17249
|
-
dashboardPath,
|
|
17250
|
-
issueList
|
|
17251
|
-
});
|
|
17252
|
-
} else {
|
|
17253
|
-
console.log(`OSS Autopilot v${version}`);
|
|
17254
|
-
console.log(daily.briefSummary);
|
|
17255
|
-
if (dashboardPath) console.log(`Dashboard: ${dashboardPath}`);
|
|
17077
|
+
dashboardPath = writeDashboardFromState();
|
|
17078
|
+
if (daily.digest.summary.totalActivePRs > 0) {
|
|
17079
|
+
openInBrowser(dashboardPath);
|
|
17080
|
+
dashboardOpened = true;
|
|
17256
17081
|
}
|
|
17257
17082
|
} catch (error) {
|
|
17258
|
-
|
|
17259
|
-
if (options.json) {
|
|
17260
|
-
outputJsonError(`Daily check failed: ${msg}`);
|
|
17261
|
-
} else {
|
|
17262
|
-
console.error(`[FATAL] Daily check failed: ${msg}`);
|
|
17263
|
-
if (error instanceof Error && error.stack) {
|
|
17264
|
-
console.error(error.stack);
|
|
17265
|
-
}
|
|
17266
|
-
}
|
|
17267
|
-
process.exit(1);
|
|
17083
|
+
console.error("[STARTUP] Dashboard generation failed:", errorMessage(error));
|
|
17268
17084
|
}
|
|
17085
|
+
if (dashboardOpened) {
|
|
17086
|
+
daily.briefSummary += " | Dashboard opened in browser";
|
|
17087
|
+
}
|
|
17088
|
+
const issueList = detectIssueList();
|
|
17089
|
+
return {
|
|
17090
|
+
version,
|
|
17091
|
+
setupComplete: true,
|
|
17092
|
+
daily,
|
|
17093
|
+
dashboardPath,
|
|
17094
|
+
issueList
|
|
17095
|
+
};
|
|
17269
17096
|
}
|
|
17270
|
-
var
|
|
17097
|
+
var fs9, import_child_process5;
|
|
17271
17098
|
var init_startup = __esm({
|
|
17272
17099
|
"src/commands/startup.ts"() {
|
|
17273
17100
|
"use strict";
|
|
17274
|
-
|
|
17275
|
-
path8 = __toESM(require("path"), 1);
|
|
17101
|
+
fs9 = __toESM(require("fs"), 1);
|
|
17276
17102
|
import_child_process5 = require("child_process");
|
|
17277
17103
|
init_core();
|
|
17278
|
-
|
|
17104
|
+
init_errors();
|
|
17279
17105
|
init_daily();
|
|
17280
17106
|
init_dashboard();
|
|
17281
17107
|
}
|
|
@@ -17290,44 +17116,28 @@ __export(shelve_exports, {
|
|
|
17290
17116
|
});
|
|
17291
17117
|
async function runShelve(options) {
|
|
17292
17118
|
validateUrl(options.prUrl);
|
|
17293
|
-
validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR"
|
|
17119
|
+
validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR");
|
|
17294
17120
|
const stateManager2 = getStateManager();
|
|
17295
17121
|
const added = stateManager2.shelvePR(options.prUrl);
|
|
17296
17122
|
if (added) {
|
|
17297
17123
|
stateManager2.save();
|
|
17298
17124
|
}
|
|
17299
|
-
|
|
17300
|
-
outputJson({ shelved: added, url: options.prUrl });
|
|
17301
|
-
} else if (added) {
|
|
17302
|
-
console.log(`Shelved: ${options.prUrl}`);
|
|
17303
|
-
console.log("This PR is now excluded from capacity and actionable issues.");
|
|
17304
|
-
console.log("It will auto-unshelve if a maintainer engages.");
|
|
17305
|
-
} else {
|
|
17306
|
-
console.log("PR is already shelved.");
|
|
17307
|
-
}
|
|
17125
|
+
return { shelved: added, url: options.prUrl };
|
|
17308
17126
|
}
|
|
17309
17127
|
async function runUnshelve(options) {
|
|
17310
17128
|
validateUrl(options.prUrl);
|
|
17311
|
-
validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR"
|
|
17129
|
+
validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR");
|
|
17312
17130
|
const stateManager2 = getStateManager();
|
|
17313
17131
|
const removed = stateManager2.unshelvePR(options.prUrl);
|
|
17314
17132
|
if (removed) {
|
|
17315
17133
|
stateManager2.save();
|
|
17316
17134
|
}
|
|
17317
|
-
|
|
17318
|
-
outputJson({ unshelved: removed, url: options.prUrl });
|
|
17319
|
-
} else if (removed) {
|
|
17320
|
-
console.log(`Unshelved: ${options.prUrl}`);
|
|
17321
|
-
console.log("This PR is now active again.");
|
|
17322
|
-
} else {
|
|
17323
|
-
console.log("PR was not shelved.");
|
|
17324
|
-
}
|
|
17135
|
+
return { unshelved: removed, url: options.prUrl };
|
|
17325
17136
|
}
|
|
17326
17137
|
var init_shelve = __esm({
|
|
17327
17138
|
"src/commands/shelve.ts"() {
|
|
17328
17139
|
"use strict";
|
|
17329
17140
|
init_core();
|
|
17330
|
-
init_json();
|
|
17331
17141
|
init_validation();
|
|
17332
17142
|
}
|
|
17333
17143
|
});
|
|
@@ -17341,44 +17151,28 @@ __export(dismiss_exports, {
|
|
|
17341
17151
|
});
|
|
17342
17152
|
async function runDismiss(options) {
|
|
17343
17153
|
validateUrl(options.issueUrl);
|
|
17344
|
-
validateGitHubUrl(options.issueUrl, ISSUE_URL_PATTERN, "issue"
|
|
17154
|
+
validateGitHubUrl(options.issueUrl, ISSUE_URL_PATTERN, "issue");
|
|
17345
17155
|
const stateManager2 = getStateManager();
|
|
17346
17156
|
const added = stateManager2.dismissIssue(options.issueUrl, (/* @__PURE__ */ new Date()).toISOString());
|
|
17347
17157
|
if (added) {
|
|
17348
17158
|
stateManager2.save();
|
|
17349
17159
|
}
|
|
17350
|
-
|
|
17351
|
-
outputJson({ dismissed: added, url: options.issueUrl });
|
|
17352
|
-
} else if (added) {
|
|
17353
|
-
console.log(`Dismissed: ${options.issueUrl}`);
|
|
17354
|
-
console.log("Issue reply notifications are now muted.");
|
|
17355
|
-
console.log("New responses after this point will resurface automatically.");
|
|
17356
|
-
} else {
|
|
17357
|
-
console.log("Issue is already dismissed.");
|
|
17358
|
-
}
|
|
17160
|
+
return { dismissed: added, url: options.issueUrl };
|
|
17359
17161
|
}
|
|
17360
17162
|
async function runUndismiss(options) {
|
|
17361
17163
|
validateUrl(options.issueUrl);
|
|
17362
|
-
validateGitHubUrl(options.issueUrl, ISSUE_URL_PATTERN, "issue"
|
|
17164
|
+
validateGitHubUrl(options.issueUrl, ISSUE_URL_PATTERN, "issue");
|
|
17363
17165
|
const stateManager2 = getStateManager();
|
|
17364
17166
|
const removed = stateManager2.undismissIssue(options.issueUrl);
|
|
17365
17167
|
if (removed) {
|
|
17366
17168
|
stateManager2.save();
|
|
17367
17169
|
}
|
|
17368
|
-
|
|
17369
|
-
outputJson({ undismissed: removed, url: options.issueUrl });
|
|
17370
|
-
} else if (removed) {
|
|
17371
|
-
console.log(`Undismissed: ${options.issueUrl}`);
|
|
17372
|
-
console.log("Issue reply notifications are active again.");
|
|
17373
|
-
} else {
|
|
17374
|
-
console.log("Issue was not dismissed.");
|
|
17375
|
-
}
|
|
17170
|
+
return { undismissed: removed, url: options.issueUrl };
|
|
17376
17171
|
}
|
|
17377
17172
|
var init_dismiss = __esm({
|
|
17378
17173
|
"src/commands/dismiss.ts"() {
|
|
17379
17174
|
"use strict";
|
|
17380
17175
|
init_core();
|
|
17381
|
-
init_json();
|
|
17382
17176
|
init_validation();
|
|
17383
17177
|
}
|
|
17384
17178
|
});
|
|
@@ -17391,77 +17185,41 @@ __export(snooze_exports, {
|
|
|
17391
17185
|
});
|
|
17392
17186
|
async function runSnooze(options) {
|
|
17393
17187
|
validateUrl(options.prUrl);
|
|
17394
|
-
validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR"
|
|
17188
|
+
validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR");
|
|
17395
17189
|
validateMessage(options.reason);
|
|
17396
17190
|
const days = options.days ?? DEFAULT_SNOOZE_DAYS;
|
|
17397
17191
|
if (!Number.isFinite(days) || days <= 0) {
|
|
17398
|
-
|
|
17399
|
-
outputJsonError("Snooze duration must be a positive number of days.");
|
|
17400
|
-
} else {
|
|
17401
|
-
console.error("Error: Snooze duration must be a positive number of days.");
|
|
17402
|
-
}
|
|
17403
|
-
process.exit(1);
|
|
17192
|
+
throw new Error("Snooze duration must be a positive number of days.");
|
|
17404
17193
|
}
|
|
17405
|
-
|
|
17406
|
-
|
|
17407
|
-
|
|
17408
|
-
|
|
17409
|
-
stateManager2.save();
|
|
17410
|
-
}
|
|
17411
|
-
const snoozeInfo = stateManager2.getSnoozeInfo(options.prUrl);
|
|
17412
|
-
if (options.json) {
|
|
17413
|
-
outputJson({
|
|
17414
|
-
snoozed: added,
|
|
17415
|
-
url: options.prUrl,
|
|
17416
|
-
days,
|
|
17417
|
-
reason: options.reason,
|
|
17418
|
-
expiresAt: snoozeInfo?.expiresAt
|
|
17419
|
-
});
|
|
17420
|
-
} else if (added) {
|
|
17421
|
-
console.log(`Snoozed: ${options.prUrl}`);
|
|
17422
|
-
console.log(`Reason: ${options.reason}`);
|
|
17423
|
-
console.log(`Duration: ${days} day${days === 1 ? "" : "s"}`);
|
|
17424
|
-
console.log(`Expires: ${snoozeInfo?.expiresAt ? new Date(snoozeInfo.expiresAt).toLocaleString() : "unknown"}`);
|
|
17425
|
-
console.log("CI failure notifications are now muted for this PR.");
|
|
17426
|
-
} else {
|
|
17427
|
-
console.log("PR is already snoozed.");
|
|
17428
|
-
if (snoozeInfo) {
|
|
17429
|
-
console.log(`Expires: ${new Date(snoozeInfo.expiresAt).toLocaleString()}`);
|
|
17430
|
-
}
|
|
17431
|
-
}
|
|
17432
|
-
} catch (error) {
|
|
17433
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
17434
|
-
if (options.json) {
|
|
17435
|
-
outputJsonError(`Snooze failed: ${msg}`);
|
|
17436
|
-
} else {
|
|
17437
|
-
console.error(`Error: Snooze failed: ${msg}`);
|
|
17438
|
-
}
|
|
17439
|
-
process.exit(1);
|
|
17194
|
+
const stateManager2 = getStateManager();
|
|
17195
|
+
const added = stateManager2.snoozePR(options.prUrl, options.reason, days);
|
|
17196
|
+
if (added) {
|
|
17197
|
+
stateManager2.save();
|
|
17440
17198
|
}
|
|
17199
|
+
const snoozeInfo = stateManager2.getSnoozeInfo(options.prUrl);
|
|
17200
|
+
return {
|
|
17201
|
+
snoozed: added,
|
|
17202
|
+
url: options.prUrl,
|
|
17203
|
+
days,
|
|
17204
|
+
reason: options.reason,
|
|
17205
|
+
expiresAt: snoozeInfo?.expiresAt
|
|
17206
|
+
};
|
|
17441
17207
|
}
|
|
17442
17208
|
async function runUnsnooze(options) {
|
|
17443
17209
|
validateUrl(options.prUrl);
|
|
17444
|
-
validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR"
|
|
17210
|
+
validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR");
|
|
17445
17211
|
const stateManager2 = getStateManager();
|
|
17446
17212
|
const removed = stateManager2.unsnoozePR(options.prUrl);
|
|
17447
17213
|
if (removed) {
|
|
17448
17214
|
stateManager2.save();
|
|
17449
17215
|
}
|
|
17450
|
-
|
|
17451
|
-
outputJson({ unsnoozed: removed, url: options.prUrl });
|
|
17452
|
-
} else if (removed) {
|
|
17453
|
-
console.log(`Unsnoozed: ${options.prUrl}`);
|
|
17454
|
-
console.log("CI failure notifications are active again for this PR.");
|
|
17455
|
-
} else {
|
|
17456
|
-
console.log("PR was not snoozed.");
|
|
17457
|
-
}
|
|
17216
|
+
return { unsnoozed: removed, url: options.prUrl };
|
|
17458
17217
|
}
|
|
17459
17218
|
var DEFAULT_SNOOZE_DAYS;
|
|
17460
17219
|
var init_snooze = __esm({
|
|
17461
17220
|
"src/commands/snooze.ts"() {
|
|
17462
17221
|
"use strict";
|
|
17463
17222
|
init_core();
|
|
17464
|
-
init_json();
|
|
17465
17223
|
init_validation();
|
|
17466
17224
|
DEFAULT_SNOOZE_DAYS = 7;
|
|
17467
17225
|
}
|
|
@@ -17486,16 +17244,26 @@ var {
|
|
|
17486
17244
|
|
|
17487
17245
|
// src/cli.ts
|
|
17488
17246
|
init_core();
|
|
17489
|
-
|
|
17490
|
-
|
|
17491
|
-
|
|
17492
|
-
|
|
17493
|
-
|
|
17494
|
-
|
|
17495
|
-
}
|
|
17496
|
-
|
|
17247
|
+
init_errors();
|
|
17248
|
+
init_json();
|
|
17249
|
+
function printRepos(repos) {
|
|
17250
|
+
const entries = Object.entries(repos).sort(([a], [b]) => a.localeCompare(b));
|
|
17251
|
+
for (const [remote, info] of entries) {
|
|
17252
|
+
const branch = info.currentBranch ? ` (${info.currentBranch})` : "";
|
|
17253
|
+
console.log(` ${remote}${branch}`);
|
|
17254
|
+
console.log(` ${info.path}`);
|
|
17497
17255
|
}
|
|
17498
|
-
}
|
|
17256
|
+
}
|
|
17257
|
+
function handleCommandError(err, json) {
|
|
17258
|
+
const msg = errorMessage(err);
|
|
17259
|
+
if (json) {
|
|
17260
|
+
outputJsonError(msg);
|
|
17261
|
+
} else {
|
|
17262
|
+
console.error(`Error: ${msg}`);
|
|
17263
|
+
}
|
|
17264
|
+
process.exit(1);
|
|
17265
|
+
}
|
|
17266
|
+
var VERSION10 = getCLIVersion();
|
|
17499
17267
|
var LOCAL_ONLY_COMMANDS = [
|
|
17500
17268
|
"help",
|
|
17501
17269
|
"status",
|
|
@@ -17506,6 +17274,7 @@ var LOCAL_ONLY_COMMANDS = [
|
|
|
17506
17274
|
"setup",
|
|
17507
17275
|
"checkSetup",
|
|
17508
17276
|
"dashboard",
|
|
17277
|
+
"serve",
|
|
17509
17278
|
"parse-issue-list",
|
|
17510
17279
|
"check-integration",
|
|
17511
17280
|
"local-repos",
|
|
@@ -17520,106 +17289,571 @@ var LOCAL_ONLY_COMMANDS = [
|
|
|
17520
17289
|
var program2 = new Command();
|
|
17521
17290
|
program2.name("oss-autopilot").description("AI-powered autopilot for managing open source contributions").version(VERSION10).option("--debug", "Enable debug logging");
|
|
17522
17291
|
program2.command("daily").description("Run daily check on all tracked PRs").option("--json", "Output as JSON").action(async (options) => {
|
|
17523
|
-
|
|
17524
|
-
|
|
17292
|
+
try {
|
|
17293
|
+
if (options.json) {
|
|
17294
|
+
const { runDaily: runDaily2 } = await Promise.resolve().then(() => (init_daily(), daily_exports));
|
|
17295
|
+
const data = await runDaily2();
|
|
17296
|
+
outputJson(data);
|
|
17297
|
+
} else {
|
|
17298
|
+
const { runDailyForDisplay: runDailyForDisplay2, printDigest: printDigest2 } = await Promise.resolve().then(() => (init_daily(), daily_exports));
|
|
17299
|
+
const result = await runDailyForDisplay2();
|
|
17300
|
+
printDigest2(result.digest, result.capacity, result.commentedIssues);
|
|
17301
|
+
}
|
|
17302
|
+
} catch (err) {
|
|
17303
|
+
handleCommandError(err, options.json);
|
|
17304
|
+
}
|
|
17525
17305
|
});
|
|
17526
17306
|
program2.command("status").description("Show current status and stats").option("--json", "Output as JSON").option("--offline", "Use cached data only (no GitHub API calls)").action(async (options) => {
|
|
17527
|
-
|
|
17528
|
-
|
|
17307
|
+
try {
|
|
17308
|
+
const { runStatus: runStatus2 } = await Promise.resolve().then(() => (init_status(), status_exports));
|
|
17309
|
+
const data = await runStatus2({ offline: options.offline });
|
|
17310
|
+
if (options.json) {
|
|
17311
|
+
outputJson(data);
|
|
17312
|
+
} else {
|
|
17313
|
+
console.log("\n\u{1F4CA} OSS Status\n");
|
|
17314
|
+
console.log(`Merged PRs: ${data.stats.mergedPRs}`);
|
|
17315
|
+
console.log(`Closed PRs: ${data.stats.closedPRs}`);
|
|
17316
|
+
console.log(`Merge Rate: ${data.stats.mergeRate}`);
|
|
17317
|
+
console.log(`Needs Response: ${data.stats.needsResponse}`);
|
|
17318
|
+
if (data.offline) {
|
|
17319
|
+
console.log(`
|
|
17320
|
+
Last Updated: ${data.lastUpdated || "Never"}`);
|
|
17321
|
+
console.log("(Offline mode: showing cached data)");
|
|
17322
|
+
} else {
|
|
17323
|
+
console.log(`
|
|
17324
|
+
Last Run: ${data.lastRunAt || "Never"}`);
|
|
17325
|
+
}
|
|
17326
|
+
console.log("\nRun with --json for structured output");
|
|
17327
|
+
}
|
|
17328
|
+
} catch (err) {
|
|
17329
|
+
handleCommandError(err, options.json);
|
|
17330
|
+
}
|
|
17529
17331
|
});
|
|
17530
17332
|
program2.command("search [count]").description("Search for new issues to work on").option("--json", "Output as JSON").action(async (count, options) => {
|
|
17531
|
-
|
|
17532
|
-
|
|
17333
|
+
try {
|
|
17334
|
+
const { runSearch: runSearch2 } = await Promise.resolve().then(() => (init_search(), search_exports));
|
|
17335
|
+
if (!options.json) {
|
|
17336
|
+
console.log(`
|
|
17337
|
+
Searching for issues (max ${parseInt(count) || 5})...
|
|
17338
|
+
`);
|
|
17339
|
+
}
|
|
17340
|
+
const data = await runSearch2({ maxResults: parseInt(count) || 5 });
|
|
17341
|
+
if (options.json) {
|
|
17342
|
+
outputJson(data);
|
|
17343
|
+
} else {
|
|
17344
|
+
if (data.candidates.length === 0) {
|
|
17345
|
+
if (data.rateLimitWarning) {
|
|
17346
|
+
console.warn(`
|
|
17347
|
+
${data.rateLimitWarning}
|
|
17348
|
+
`);
|
|
17349
|
+
} else {
|
|
17350
|
+
console.log("No matching issues found.");
|
|
17351
|
+
}
|
|
17352
|
+
return;
|
|
17353
|
+
}
|
|
17354
|
+
if (data.rateLimitWarning) {
|
|
17355
|
+
console.warn(`
|
|
17356
|
+
${data.rateLimitWarning}
|
|
17357
|
+
`);
|
|
17358
|
+
}
|
|
17359
|
+
console.log(`Found ${data.candidates.length} candidates:
|
|
17360
|
+
`);
|
|
17361
|
+
for (const candidate of data.candidates) {
|
|
17362
|
+
const { issue, recommendation, reasonsToApprove, reasonsToSkip, viabilityScore } = candidate;
|
|
17363
|
+
console.log(`[${recommendation.toUpperCase()}] ${issue.repo}#${issue.number}: ${issue.title}`);
|
|
17364
|
+
console.log(` URL: ${issue.url}`);
|
|
17365
|
+
console.log(` Viability: ${viabilityScore}/100`);
|
|
17366
|
+
if (reasonsToApprove.length > 0) console.log(` Approve: ${reasonsToApprove.join(", ")}`);
|
|
17367
|
+
if (reasonsToSkip.length > 0) console.log(` Skip: ${reasonsToSkip.join(", ")}`);
|
|
17368
|
+
console.log("---");
|
|
17369
|
+
}
|
|
17370
|
+
}
|
|
17371
|
+
} catch (err) {
|
|
17372
|
+
handleCommandError(err, options.json);
|
|
17373
|
+
}
|
|
17533
17374
|
});
|
|
17534
17375
|
program2.command("vet <issue-url>").description("Vet a specific issue before working on it").option("--json", "Output as JSON").action(async (issueUrl, options) => {
|
|
17535
|
-
|
|
17536
|
-
|
|
17376
|
+
try {
|
|
17377
|
+
const { runVet: runVet2 } = await Promise.resolve().then(() => (init_vet(), vet_exports));
|
|
17378
|
+
const data = await runVet2({ issueUrl });
|
|
17379
|
+
if (options.json) {
|
|
17380
|
+
outputJson(data);
|
|
17381
|
+
} else {
|
|
17382
|
+
const { issue, recommendation, reasonsToApprove, reasonsToSkip } = data;
|
|
17383
|
+
console.log(`
|
|
17384
|
+
Vetting issue: ${issueUrl}
|
|
17385
|
+
`);
|
|
17386
|
+
console.log(`[${recommendation.toUpperCase()}] ${issue.repo}#${issue.number}: ${issue.title}`);
|
|
17387
|
+
console.log(` URL: ${issue.url}`);
|
|
17388
|
+
if (reasonsToApprove.length > 0) console.log(` Approve: ${reasonsToApprove.join(", ")}`);
|
|
17389
|
+
if (reasonsToSkip.length > 0) console.log(` Skip: ${reasonsToSkip.join(", ")}`);
|
|
17390
|
+
}
|
|
17391
|
+
} catch (err) {
|
|
17392
|
+
handleCommandError(err, options.json);
|
|
17393
|
+
}
|
|
17537
17394
|
});
|
|
17538
17395
|
program2.command("track <pr-url>").description("Add a PR to track").option("--json", "Output as JSON").action(async (prUrl, options) => {
|
|
17539
|
-
|
|
17540
|
-
|
|
17396
|
+
try {
|
|
17397
|
+
const { runTrack: runTrack2 } = await Promise.resolve().then(() => (init_track(), track_exports));
|
|
17398
|
+
const data = await runTrack2({ prUrl });
|
|
17399
|
+
if (options.json) {
|
|
17400
|
+
outputJson(data);
|
|
17401
|
+
} else {
|
|
17402
|
+
console.log(`
|
|
17403
|
+
PR: ${data.pr.repo}#${data.pr.number} - ${data.pr.title}`);
|
|
17404
|
+
console.log("Note: In v2, PRs are tracked automatically via the daily run.");
|
|
17405
|
+
}
|
|
17406
|
+
} catch (err) {
|
|
17407
|
+
handleCommandError(err, options.json);
|
|
17408
|
+
}
|
|
17541
17409
|
});
|
|
17542
17410
|
program2.command("untrack <pr-url>").description("Stop tracking a PR").option("--json", "Output as JSON").action(async (prUrl, options) => {
|
|
17543
|
-
|
|
17544
|
-
|
|
17411
|
+
try {
|
|
17412
|
+
const { runUntrack: runUntrack2 } = await Promise.resolve().then(() => (init_track(), track_exports));
|
|
17413
|
+
const data = await runUntrack2({ prUrl });
|
|
17414
|
+
if (options.json) {
|
|
17415
|
+
outputJson(data);
|
|
17416
|
+
} else {
|
|
17417
|
+
console.log(
|
|
17418
|
+
"Note: In v2, PRs are fetched fresh on each daily run \u2014 there is no local tracking list to remove from."
|
|
17419
|
+
);
|
|
17420
|
+
console.log("Use `shelve` to temporarily hide a PR from the daily summary.");
|
|
17421
|
+
}
|
|
17422
|
+
} catch (err) {
|
|
17423
|
+
handleCommandError(err, options.json);
|
|
17424
|
+
}
|
|
17545
17425
|
});
|
|
17546
17426
|
program2.command("read [pr-url]").description("Mark PR comments as read").option("--all", "Mark all PRs as read").option("--json", "Output as JSON").action(async (prUrl, options) => {
|
|
17547
|
-
|
|
17548
|
-
|
|
17427
|
+
try {
|
|
17428
|
+
const { runRead: runRead2 } = await Promise.resolve().then(() => (init_read(), read_exports));
|
|
17429
|
+
const data = await runRead2({ prUrl, all: options.all });
|
|
17430
|
+
if (options.json) {
|
|
17431
|
+
outputJson(data);
|
|
17432
|
+
} else {
|
|
17433
|
+
console.log("Note: In v2, PR read state is not tracked locally. PRs are fetched fresh on each daily run.");
|
|
17434
|
+
}
|
|
17435
|
+
} catch (err) {
|
|
17436
|
+
handleCommandError(err, options.json);
|
|
17437
|
+
}
|
|
17549
17438
|
});
|
|
17550
17439
|
program2.command("comments <pr-url>").description("Show all comments on a PR").option("--bots", "Include bot comments").option("--json", "Output as JSON").action(async (prUrl, options) => {
|
|
17551
|
-
|
|
17552
|
-
|
|
17440
|
+
try {
|
|
17441
|
+
const { runComments: runComments2 } = await Promise.resolve().then(() => (init_comments(), comments_exports));
|
|
17442
|
+
const data = await runComments2({ prUrl, showBots: options.bots });
|
|
17443
|
+
if (options.json) {
|
|
17444
|
+
outputJson(data);
|
|
17445
|
+
} else {
|
|
17446
|
+
console.log(`
|
|
17447
|
+
Fetching comments for: ${prUrl}
|
|
17448
|
+
`);
|
|
17449
|
+
console.log(`## ${data.pr.title}
|
|
17450
|
+
`);
|
|
17451
|
+
console.log(`**Status:** ${data.pr.state} | **Mergeable:** ${data.pr.mergeable ?? "checking..."}`);
|
|
17452
|
+
console.log(`**Branch:** ${data.pr.head} -> ${data.pr.base}`);
|
|
17453
|
+
console.log(`**URL:** ${data.pr.url}
|
|
17454
|
+
`);
|
|
17455
|
+
const REVIEW_STATE_LABELS = {
|
|
17456
|
+
APPROVED: "[Approved]",
|
|
17457
|
+
CHANGES_REQUESTED: "[Changes]"
|
|
17458
|
+
};
|
|
17459
|
+
if (data.reviews.length > 0) {
|
|
17460
|
+
console.log("### Reviews (newest first)\n");
|
|
17461
|
+
for (const review of data.reviews) {
|
|
17462
|
+
const state = REVIEW_STATE_LABELS[review.state] ?? "[Comment]";
|
|
17463
|
+
const time = review.submittedAt ? formatRelativeTime(review.submittedAt) : "";
|
|
17464
|
+
console.log(`${state} **@${review.user}** (${review.state}) - ${time}`);
|
|
17465
|
+
if (review.body) {
|
|
17466
|
+
console.log(`> ${review.body.split("\n").join("\n> ")}
|
|
17467
|
+
`);
|
|
17468
|
+
}
|
|
17469
|
+
}
|
|
17470
|
+
}
|
|
17471
|
+
if (data.reviewComments.length > 0) {
|
|
17472
|
+
console.log("### Inline Comments (newest first)\n");
|
|
17473
|
+
for (const comment of data.reviewComments) {
|
|
17474
|
+
const time = formatRelativeTime(comment.createdAt);
|
|
17475
|
+
console.log(`**@${comment.user}** on \`${comment.path}\` - ${time}`);
|
|
17476
|
+
console.log(`> ${comment.body.split("\n").join("\n> ")}`);
|
|
17477
|
+
console.log("");
|
|
17478
|
+
}
|
|
17479
|
+
}
|
|
17480
|
+
if (data.issueComments.length > 0) {
|
|
17481
|
+
console.log("### Discussion (newest first)\n");
|
|
17482
|
+
for (const comment of data.issueComments) {
|
|
17483
|
+
const time = formatRelativeTime(comment.createdAt);
|
|
17484
|
+
console.log(`**@${comment.user}** - ${time}`);
|
|
17485
|
+
console.log(`> ${comment.body?.split("\n").join("\n> ")}
|
|
17486
|
+
`);
|
|
17487
|
+
}
|
|
17488
|
+
}
|
|
17489
|
+
if (data.reviewComments.length === 0 && data.issueComments.length === 0 && data.reviews.length === 0) {
|
|
17490
|
+
console.log("No comments from other users.\n");
|
|
17491
|
+
}
|
|
17492
|
+
console.log("---");
|
|
17493
|
+
console.log(
|
|
17494
|
+
`**Summary:** ${data.summary.reviewCount} reviews, ${data.summary.inlineCommentCount} inline comments, ${data.summary.discussionCommentCount} discussion comments`
|
|
17495
|
+
);
|
|
17496
|
+
}
|
|
17497
|
+
} catch (err) {
|
|
17498
|
+
handleCommandError(err, options.json);
|
|
17499
|
+
}
|
|
17553
17500
|
});
|
|
17554
17501
|
program2.command("post <url> [message...]").description("Post a comment to a PR or issue").option("--stdin", "Read message from stdin").option("--json", "Output as JSON").action(async (url, messageParts, options) => {
|
|
17555
|
-
|
|
17556
|
-
|
|
17557
|
-
|
|
17502
|
+
try {
|
|
17503
|
+
let message;
|
|
17504
|
+
if (options.stdin) {
|
|
17505
|
+
const chunks = [];
|
|
17506
|
+
for await (const chunk of process.stdin) {
|
|
17507
|
+
chunks.push(chunk);
|
|
17508
|
+
}
|
|
17509
|
+
message = Buffer.concat(chunks).toString("utf-8").trim();
|
|
17510
|
+
} else {
|
|
17511
|
+
message = messageParts.join(" ");
|
|
17512
|
+
}
|
|
17513
|
+
const { runPost: runPost2 } = await Promise.resolve().then(() => (init_comments(), comments_exports));
|
|
17514
|
+
const data = await runPost2({ url, message });
|
|
17515
|
+
if (options.json) {
|
|
17516
|
+
outputJson(data);
|
|
17517
|
+
} else {
|
|
17518
|
+
console.log(`Comment posted: ${data.commentUrl}`);
|
|
17519
|
+
}
|
|
17520
|
+
} catch (err) {
|
|
17521
|
+
handleCommandError(err, options.json);
|
|
17522
|
+
}
|
|
17558
17523
|
});
|
|
17559
17524
|
program2.command("claim <issue-url> [message...]").description("Claim an issue by posting a comment").option("--json", "Output as JSON").action(async (issueUrl, messageParts, options) => {
|
|
17560
|
-
|
|
17561
|
-
|
|
17562
|
-
|
|
17525
|
+
try {
|
|
17526
|
+
const { runClaim: runClaim2 } = await Promise.resolve().then(() => (init_comments(), comments_exports));
|
|
17527
|
+
const message = messageParts.length > 0 ? messageParts.join(" ") : void 0;
|
|
17528
|
+
const data = await runClaim2({ issueUrl, message });
|
|
17529
|
+
if (options.json) {
|
|
17530
|
+
outputJson(data);
|
|
17531
|
+
} else {
|
|
17532
|
+
console.log(`Issue claimed: ${data.commentUrl}`);
|
|
17533
|
+
}
|
|
17534
|
+
} catch (err) {
|
|
17535
|
+
handleCommandError(err, options.json);
|
|
17536
|
+
}
|
|
17563
17537
|
});
|
|
17564
17538
|
program2.command("config [key] [value]").description("Show or update configuration").option("--json", "Output as JSON").action(async (key, value, options) => {
|
|
17565
|
-
|
|
17566
|
-
|
|
17539
|
+
try {
|
|
17540
|
+
const { runConfig: runConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
17541
|
+
const data = await runConfig2({ key, value });
|
|
17542
|
+
if (options.json) {
|
|
17543
|
+
outputJson(data);
|
|
17544
|
+
} else if ("config" in data) {
|
|
17545
|
+
console.log("\n\u2699\uFE0F Current Configuration:\n");
|
|
17546
|
+
console.log(JSON.stringify(data.config, null, 2));
|
|
17547
|
+
} else {
|
|
17548
|
+
console.log(`Set ${data.key} to: ${data.value}`);
|
|
17549
|
+
}
|
|
17550
|
+
} catch (err) {
|
|
17551
|
+
handleCommandError(err, options.json);
|
|
17552
|
+
}
|
|
17567
17553
|
});
|
|
17568
17554
|
program2.command("init <username>").description("Initialize with your GitHub username and import open PRs").option("--json", "Output as JSON").action(async (username, options) => {
|
|
17569
|
-
|
|
17570
|
-
|
|
17555
|
+
try {
|
|
17556
|
+
const { runInit: runInit2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
17557
|
+
const data = await runInit2({ username });
|
|
17558
|
+
if (options.json) {
|
|
17559
|
+
outputJson(data);
|
|
17560
|
+
} else {
|
|
17561
|
+
console.log(`
|
|
17562
|
+
Username set to @${data.username}.`);
|
|
17563
|
+
console.log("Run `oss-autopilot daily` to fetch your open PRs from GitHub.");
|
|
17564
|
+
}
|
|
17565
|
+
} catch (err) {
|
|
17566
|
+
handleCommandError(err, options.json);
|
|
17567
|
+
}
|
|
17571
17568
|
});
|
|
17572
17569
|
program2.command("setup").description("Interactive setup / configuration").option("--reset", "Re-run setup even if already complete").option("--set <settings...>", "Set specific values (key=value)").option("--json", "Output as JSON").action(async (options) => {
|
|
17573
|
-
|
|
17574
|
-
|
|
17570
|
+
try {
|
|
17571
|
+
const { runSetup: runSetup2 } = await Promise.resolve().then(() => (init_setup(), setup_exports));
|
|
17572
|
+
const data = await runSetup2({ reset: options.reset, set: options.set });
|
|
17573
|
+
if (options.json) {
|
|
17574
|
+
outputJson(data);
|
|
17575
|
+
} else if ("success" in data) {
|
|
17576
|
+
for (const [key, value] of Object.entries(data.settings)) {
|
|
17577
|
+
console.log(`\u2713 ${key}: ${value}`);
|
|
17578
|
+
}
|
|
17579
|
+
if (data.warnings) {
|
|
17580
|
+
for (const w of data.warnings) {
|
|
17581
|
+
console.warn(w);
|
|
17582
|
+
}
|
|
17583
|
+
}
|
|
17584
|
+
} else if ("setupComplete" in data && data.setupComplete) {
|
|
17585
|
+
console.log("\n\u2699\uFE0F OSS Autopilot Setup\n");
|
|
17586
|
+
console.log("\u2713 Setup already complete!\n");
|
|
17587
|
+
console.log("Current settings:");
|
|
17588
|
+
console.log(` GitHub username: ${data.config.githubUsername || "(not set)"}`);
|
|
17589
|
+
console.log(` Max active PRs: ${data.config.maxActivePRs}`);
|
|
17590
|
+
console.log(` Dormant threshold: ${data.config.dormantThresholdDays} days`);
|
|
17591
|
+
console.log(` Approaching dormant: ${data.config.approachingDormantDays} days`);
|
|
17592
|
+
console.log(` Languages: ${data.config.languages.join(", ")}`);
|
|
17593
|
+
console.log(` Labels: ${data.config.labels.join(", ")}`);
|
|
17594
|
+
console.log(`
|
|
17595
|
+
Run 'setup --reset' to reconfigure.`);
|
|
17596
|
+
} else if ("setupRequired" in data) {
|
|
17597
|
+
console.log("\n\u2699\uFE0F OSS Autopilot Setup\n");
|
|
17598
|
+
console.log("SETUP_REQUIRED");
|
|
17599
|
+
console.log("---");
|
|
17600
|
+
console.log("Please configure the following settings:\n");
|
|
17601
|
+
for (const prompt of data.prompts) {
|
|
17602
|
+
console.log(`SETTING: ${prompt.setting}`);
|
|
17603
|
+
console.log(`PROMPT: ${prompt.prompt}`);
|
|
17604
|
+
const currentVal = Array.isArray(prompt.current) ? prompt.current.join(", ") : prompt.current;
|
|
17605
|
+
console.log(`CURRENT: ${currentVal ?? "(not set)"}`);
|
|
17606
|
+
if (prompt.required) console.log("REQUIRED: true");
|
|
17607
|
+
if (prompt.default !== void 0) {
|
|
17608
|
+
const defaultVal = Array.isArray(prompt.default) ? prompt.default.join(", ") : prompt.default;
|
|
17609
|
+
console.log(`DEFAULT: ${defaultVal}`);
|
|
17610
|
+
}
|
|
17611
|
+
if (prompt.type) console.log(`TYPE: ${prompt.type}`);
|
|
17612
|
+
console.log("");
|
|
17613
|
+
}
|
|
17614
|
+
console.log("---");
|
|
17615
|
+
console.log("END_SETUP_PROMPTS");
|
|
17616
|
+
}
|
|
17617
|
+
} catch (err) {
|
|
17618
|
+
handleCommandError(err, options.json);
|
|
17619
|
+
}
|
|
17575
17620
|
});
|
|
17576
17621
|
program2.command("checkSetup").description("Check if setup is complete").option("--json", "Output as JSON").action(async (options) => {
|
|
17577
|
-
|
|
17578
|
-
|
|
17622
|
+
try {
|
|
17623
|
+
const { runCheckSetup: runCheckSetup2 } = await Promise.resolve().then(() => (init_setup(), setup_exports));
|
|
17624
|
+
const data = await runCheckSetup2();
|
|
17625
|
+
if (options.json) {
|
|
17626
|
+
outputJson(data);
|
|
17627
|
+
} else if (data.setupComplete) {
|
|
17628
|
+
console.log("SETUP_COMPLETE");
|
|
17629
|
+
console.log(`username=${data.username}`);
|
|
17630
|
+
} else {
|
|
17631
|
+
console.log("SETUP_INCOMPLETE");
|
|
17632
|
+
}
|
|
17633
|
+
} catch (err) {
|
|
17634
|
+
handleCommandError(err, options.json);
|
|
17635
|
+
}
|
|
17579
17636
|
});
|
|
17580
|
-
program2.command("dashboard").description("
|
|
17637
|
+
var dashboardCmd = program2.command("dashboard").description("Dashboard commands");
|
|
17638
|
+
dashboardCmd.command("serve").description("Start interactive dashboard server").option("--port <port>", "Port to listen on", "3000").option("--no-open", "Do not open browser automatically").action(async (options) => {
|
|
17639
|
+
const port = parseInt(options.port, 10);
|
|
17640
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
17641
|
+
console.error(`Invalid port number: "${options.port}". Must be an integer between 1 and 65535.`);
|
|
17642
|
+
process.exit(1);
|
|
17643
|
+
}
|
|
17644
|
+
const { serveDashboard: serveDashboard2 } = await Promise.resolve().then(() => (init_dashboard(), dashboard_exports));
|
|
17645
|
+
await serveDashboard2({ port, open: options.open });
|
|
17646
|
+
});
|
|
17647
|
+
dashboardCmd.option("--open", "Open in browser").option("--json", "Output as JSON").option("--offline", "Use cached data only (no GitHub API calls)").action(async (options) => {
|
|
17581
17648
|
const { runDashboard: runDashboard2 } = await Promise.resolve().then(() => (init_dashboard(), dashboard_exports));
|
|
17582
17649
|
await runDashboard2({ open: options.open, json: options.json, offline: options.offline });
|
|
17583
17650
|
});
|
|
17584
17651
|
program2.command("parse-issue-list <path>").description("Parse a markdown issue list into structured JSON").option("--json", "Output as JSON").action(async (filePath, options) => {
|
|
17585
|
-
|
|
17586
|
-
|
|
17652
|
+
try {
|
|
17653
|
+
const { runParseList: runParseList2 } = await Promise.resolve().then(() => (init_parse_list(), parse_list_exports));
|
|
17654
|
+
const data = await runParseList2({ filePath });
|
|
17655
|
+
if (options.json) {
|
|
17656
|
+
outputJson(data);
|
|
17657
|
+
} else {
|
|
17658
|
+
const path10 = await import("path");
|
|
17659
|
+
const resolvedPath = path10.resolve(filePath);
|
|
17660
|
+
console.log(`
|
|
17661
|
+
\u{1F4CB} Issue List: ${resolvedPath}
|
|
17662
|
+
`);
|
|
17663
|
+
console.log(`Available: ${data.availableCount} | Completed: ${data.completedCount}
|
|
17664
|
+
`);
|
|
17665
|
+
if (data.available.length > 0) {
|
|
17666
|
+
console.log("--- Available ---");
|
|
17667
|
+
for (const item of data.available) {
|
|
17668
|
+
console.log(` [${item.tier}] ${item.repo}#${item.number}: ${item.title}`);
|
|
17669
|
+
}
|
|
17670
|
+
}
|
|
17671
|
+
if (data.completed.length > 0) {
|
|
17672
|
+
console.log("\n--- Completed ---");
|
|
17673
|
+
for (const item of data.completed) {
|
|
17674
|
+
console.log(` [${item.tier}] ${item.repo}#${item.number}: ${item.title}`);
|
|
17675
|
+
}
|
|
17676
|
+
}
|
|
17677
|
+
}
|
|
17678
|
+
} catch (err) {
|
|
17679
|
+
handleCommandError(err, options.json);
|
|
17680
|
+
}
|
|
17587
17681
|
});
|
|
17588
17682
|
program2.command("check-integration").description("Detect new files not referenced by the codebase").option("--base <branch>", "Base branch to compare against", "main").option("--json", "Output as JSON").action(async (options) => {
|
|
17589
|
-
|
|
17590
|
-
|
|
17683
|
+
try {
|
|
17684
|
+
const { runCheckIntegration: runCheckIntegration2 } = await Promise.resolve().then(() => (init_check_integration(), check_integration_exports));
|
|
17685
|
+
const data = await runCheckIntegration2({ base: options.base });
|
|
17686
|
+
if (options.json) {
|
|
17687
|
+
outputJson(data);
|
|
17688
|
+
} else if (data.newFiles.length === 0) {
|
|
17689
|
+
console.log("\nNo new code files to check.");
|
|
17690
|
+
} else {
|
|
17691
|
+
console.log(`
|
|
17692
|
+
\u{1F50D} Integration Check (base: ${options.base})
|
|
17693
|
+
`);
|
|
17694
|
+
console.log(`New files: ${data.newFiles.length} | Unreferenced: ${data.unreferencedCount}
|
|
17695
|
+
`);
|
|
17696
|
+
for (const file of data.newFiles) {
|
|
17697
|
+
const status = file.isIntegrated ? "\u2705" : "\u26A0\uFE0F";
|
|
17698
|
+
console.log(`${status} ${file.path}`);
|
|
17699
|
+
if (file.isIntegrated) {
|
|
17700
|
+
console.log(` Referenced by: ${file.referencedBy.join(", ")}`);
|
|
17701
|
+
} else {
|
|
17702
|
+
console.log(" Not referenced by any file");
|
|
17703
|
+
if (file.suggestedEntryPoints && file.suggestedEntryPoints.length > 0) {
|
|
17704
|
+
console.log(` Suggested entry points: ${file.suggestedEntryPoints.join(", ")}`);
|
|
17705
|
+
}
|
|
17706
|
+
}
|
|
17707
|
+
}
|
|
17708
|
+
}
|
|
17709
|
+
} catch (err) {
|
|
17710
|
+
handleCommandError(err, options.json);
|
|
17711
|
+
}
|
|
17591
17712
|
});
|
|
17592
17713
|
program2.command("local-repos").description("Scan filesystem for local git clones").option("--scan", "Force re-scan (ignores cache)").option("--paths <dirs...>", "Directories to scan").option("--json", "Output as JSON").action(async (options) => {
|
|
17593
|
-
|
|
17594
|
-
|
|
17714
|
+
try {
|
|
17715
|
+
const { runLocalRepos: runLocalRepos2 } = await Promise.resolve().then(() => (init_local_repos(), local_repos_exports));
|
|
17716
|
+
const data = await runLocalRepos2({ scan: options.scan, paths: options.paths });
|
|
17717
|
+
if (options.json) {
|
|
17718
|
+
outputJson(data);
|
|
17719
|
+
} else if (data.fromCache) {
|
|
17720
|
+
console.log(`
|
|
17721
|
+
\u{1F4C1} Local Repos (cached ${data.cachedAt})
|
|
17722
|
+
`);
|
|
17723
|
+
printRepos(data.repos);
|
|
17724
|
+
} else {
|
|
17725
|
+
console.log(`Found ${Object.keys(data.repos).length} repos:
|
|
17726
|
+
`);
|
|
17727
|
+
printRepos(data.repos);
|
|
17728
|
+
}
|
|
17729
|
+
} catch (err) {
|
|
17730
|
+
handleCommandError(err, options.json);
|
|
17731
|
+
}
|
|
17595
17732
|
});
|
|
17596
17733
|
program2.command("startup").description("Run all pre-flight checks and daily fetch in one call").option("--json", "Output as JSON").action(async (options) => {
|
|
17597
|
-
|
|
17598
|
-
|
|
17734
|
+
try {
|
|
17735
|
+
const { runStartup: runStartup2 } = await Promise.resolve().then(() => (init_startup(), startup_exports));
|
|
17736
|
+
const data = await runStartup2();
|
|
17737
|
+
if (options.json) {
|
|
17738
|
+
outputJson(data);
|
|
17739
|
+
} else {
|
|
17740
|
+
if (!data.setupComplete) {
|
|
17741
|
+
console.log("Setup incomplete. Run /setup-oss first.");
|
|
17742
|
+
} else if (data.authError) {
|
|
17743
|
+
console.error(`Error: ${data.authError}`);
|
|
17744
|
+
} else {
|
|
17745
|
+
console.log(`OSS Autopilot v${data.version}`);
|
|
17746
|
+
console.log(data.daily?.briefSummary ?? "");
|
|
17747
|
+
if (data.dashboardPath) console.log(`Dashboard: ${data.dashboardPath}`);
|
|
17748
|
+
}
|
|
17749
|
+
}
|
|
17750
|
+
} catch (err) {
|
|
17751
|
+
handleCommandError(err, options.json);
|
|
17752
|
+
}
|
|
17599
17753
|
});
|
|
17600
17754
|
program2.command("shelve <pr-url>").description("Shelve a PR (exclude from capacity and actionable issues)").option("--json", "Output as JSON").action(async (prUrl, options) => {
|
|
17601
|
-
|
|
17602
|
-
|
|
17755
|
+
try {
|
|
17756
|
+
const { runShelve: runShelve2 } = await Promise.resolve().then(() => (init_shelve(), shelve_exports));
|
|
17757
|
+
const data = await runShelve2({ prUrl });
|
|
17758
|
+
if (options.json) {
|
|
17759
|
+
outputJson(data);
|
|
17760
|
+
} else if (data.shelved) {
|
|
17761
|
+
console.log(`Shelved: ${prUrl}`);
|
|
17762
|
+
console.log("This PR is now excluded from capacity and actionable issues.");
|
|
17763
|
+
console.log("It will auto-unshelve if a maintainer engages.");
|
|
17764
|
+
} else {
|
|
17765
|
+
console.log("PR is already shelved.");
|
|
17766
|
+
}
|
|
17767
|
+
} catch (err) {
|
|
17768
|
+
handleCommandError(err, options.json);
|
|
17769
|
+
}
|
|
17603
17770
|
});
|
|
17604
17771
|
program2.command("unshelve <pr-url>").description("Unshelve a PR (include in capacity and actionable issues again)").option("--json", "Output as JSON").action(async (prUrl, options) => {
|
|
17605
|
-
|
|
17606
|
-
|
|
17772
|
+
try {
|
|
17773
|
+
const { runUnshelve: runUnshelve2 } = await Promise.resolve().then(() => (init_shelve(), shelve_exports));
|
|
17774
|
+
const data = await runUnshelve2({ prUrl });
|
|
17775
|
+
if (options.json) {
|
|
17776
|
+
outputJson(data);
|
|
17777
|
+
} else if (data.unshelved) {
|
|
17778
|
+
console.log(`Unshelved: ${prUrl}`);
|
|
17779
|
+
console.log("This PR is now active again.");
|
|
17780
|
+
} else {
|
|
17781
|
+
console.log("PR was not shelved.");
|
|
17782
|
+
}
|
|
17783
|
+
} catch (err) {
|
|
17784
|
+
handleCommandError(err, options.json);
|
|
17785
|
+
}
|
|
17607
17786
|
});
|
|
17608
17787
|
program2.command("dismiss <issue-url>").description("Dismiss issue reply notifications (resurfaces on new activity)").option("--json", "Output as JSON").action(async (issueUrl, options) => {
|
|
17609
|
-
|
|
17610
|
-
|
|
17788
|
+
try {
|
|
17789
|
+
const { runDismiss: runDismiss2 } = await Promise.resolve().then(() => (init_dismiss(), dismiss_exports));
|
|
17790
|
+
const data = await runDismiss2({ issueUrl });
|
|
17791
|
+
if (options.json) {
|
|
17792
|
+
outputJson(data);
|
|
17793
|
+
} else if (data.dismissed) {
|
|
17794
|
+
console.log(`Dismissed: ${issueUrl}`);
|
|
17795
|
+
console.log("Issue reply notifications are now muted.");
|
|
17796
|
+
console.log("New responses after this point will resurface automatically.");
|
|
17797
|
+
} else {
|
|
17798
|
+
console.log("Issue is already dismissed.");
|
|
17799
|
+
}
|
|
17800
|
+
} catch (err) {
|
|
17801
|
+
handleCommandError(err, options.json);
|
|
17802
|
+
}
|
|
17611
17803
|
});
|
|
17612
17804
|
program2.command("undismiss <issue-url>").description("Undismiss an issue (re-enable reply notifications)").option("--json", "Output as JSON").action(async (issueUrl, options) => {
|
|
17613
|
-
|
|
17614
|
-
|
|
17805
|
+
try {
|
|
17806
|
+
const { runUndismiss: runUndismiss2 } = await Promise.resolve().then(() => (init_dismiss(), dismiss_exports));
|
|
17807
|
+
const data = await runUndismiss2({ issueUrl });
|
|
17808
|
+
if (options.json) {
|
|
17809
|
+
outputJson(data);
|
|
17810
|
+
} else if (data.undismissed) {
|
|
17811
|
+
console.log(`Undismissed: ${issueUrl}`);
|
|
17812
|
+
console.log("Issue reply notifications are active again.");
|
|
17813
|
+
} else {
|
|
17814
|
+
console.log("Issue was not dismissed.");
|
|
17815
|
+
}
|
|
17816
|
+
} catch (err) {
|
|
17817
|
+
handleCommandError(err, options.json);
|
|
17818
|
+
}
|
|
17615
17819
|
});
|
|
17616
17820
|
program2.command("snooze <pr-url>").description("Snooze CI failure notifications for a PR").requiredOption("--reason <reason>", 'Reason for snoozing (e.g., "upstream infrastructure issue")').option("--days <days>", "Number of days to snooze (default: 7)", "7").option("--json", "Output as JSON").action(async (prUrl, options) => {
|
|
17617
|
-
|
|
17618
|
-
|
|
17821
|
+
try {
|
|
17822
|
+
const { runSnooze: runSnooze2 } = await Promise.resolve().then(() => (init_snooze(), snooze_exports));
|
|
17823
|
+
const data = await runSnooze2({ prUrl, reason: options.reason, days: parseInt(options.days, 10) });
|
|
17824
|
+
if (options.json) {
|
|
17825
|
+
outputJson(data);
|
|
17826
|
+
} else if (data.snoozed) {
|
|
17827
|
+
console.log(`Snoozed: ${prUrl}`);
|
|
17828
|
+
console.log(`Reason: ${data.reason}`);
|
|
17829
|
+
console.log(`Duration: ${data.days} day${data.days === 1 ? "" : "s"}`);
|
|
17830
|
+
console.log(`Expires: ${data.expiresAt ? new Date(data.expiresAt).toLocaleString() : "unknown"}`);
|
|
17831
|
+
console.log("CI failure notifications are now muted for this PR.");
|
|
17832
|
+
} else {
|
|
17833
|
+
console.log("PR is already snoozed.");
|
|
17834
|
+
if (data.expiresAt) {
|
|
17835
|
+
console.log(`Expires: ${new Date(data.expiresAt).toLocaleString()}`);
|
|
17836
|
+
}
|
|
17837
|
+
}
|
|
17838
|
+
} catch (err) {
|
|
17839
|
+
handleCommandError(err, options.json);
|
|
17840
|
+
}
|
|
17619
17841
|
});
|
|
17620
17842
|
program2.command("unsnooze <pr-url>").description("Unsnooze a PR (re-enable CI failure notifications)").option("--json", "Output as JSON").action(async (prUrl, options) => {
|
|
17621
|
-
|
|
17622
|
-
|
|
17843
|
+
try {
|
|
17844
|
+
const { runUnsnooze: runUnsnooze2 } = await Promise.resolve().then(() => (init_snooze(), snooze_exports));
|
|
17845
|
+
const data = await runUnsnooze2({ prUrl });
|
|
17846
|
+
if (options.json) {
|
|
17847
|
+
outputJson(data);
|
|
17848
|
+
} else if (data.unsnoozed) {
|
|
17849
|
+
console.log(`Unsnoozed: ${prUrl}`);
|
|
17850
|
+
console.log("CI failure notifications are active again for this PR.");
|
|
17851
|
+
} else {
|
|
17852
|
+
console.log("PR was not snoozed.");
|
|
17853
|
+
}
|
|
17854
|
+
} catch (err) {
|
|
17855
|
+
handleCommandError(err, options.json);
|
|
17856
|
+
}
|
|
17623
17857
|
});
|
|
17624
17858
|
program2.hook("preAction", async (thisCommand, actionCommand) => {
|
|
17625
17859
|
const globalOpts = thisCommand.opts();
|