@oss-autopilot/mcp 1.0.0 → 1.0.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/mcp-server.bundle.cjs +724 -703
- package/dist/prompts.js +1 -1
- package/dist/prompts.js.map +1 -1
- package/dist/resources.js +3 -3
- package/dist/resources.js.map +1 -1
- package/dist/tools.d.ts +0 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +7 -10
- package/dist/tools.js.map +1 -1
- package/package.json +2 -2
|
@@ -235,10 +235,10 @@ function assignProp(target, prop, value) {
|
|
|
235
235
|
configurable: true
|
|
236
236
|
});
|
|
237
237
|
}
|
|
238
|
-
function getElementAtPath(obj,
|
|
239
|
-
if (!
|
|
238
|
+
function getElementAtPath(obj, path6) {
|
|
239
|
+
if (!path6)
|
|
240
240
|
return obj;
|
|
241
|
-
return
|
|
241
|
+
return path6.reduce((acc, key) => acc?.[key], obj);
|
|
242
242
|
}
|
|
243
243
|
function promiseAllObject(promisesObj) {
|
|
244
244
|
const keys = Object.keys(promisesObj);
|
|
@@ -487,11 +487,11 @@ function aborted(x, startIndex = 0) {
|
|
|
487
487
|
}
|
|
488
488
|
return false;
|
|
489
489
|
}
|
|
490
|
-
function prefixIssues(
|
|
490
|
+
function prefixIssues(path6, issues) {
|
|
491
491
|
return issues.map((iss) => {
|
|
492
492
|
var _a;
|
|
493
493
|
(_a = iss).path ?? (_a.path = []);
|
|
494
|
-
iss.path.unshift(
|
|
494
|
+
iss.path.unshift(path6);
|
|
495
495
|
return iss;
|
|
496
496
|
});
|
|
497
497
|
}
|
|
@@ -9454,8 +9454,8 @@ var require_utils = __commonJS({
|
|
|
9454
9454
|
}
|
|
9455
9455
|
return ind;
|
|
9456
9456
|
}
|
|
9457
|
-
function removeDotSegments(
|
|
9458
|
-
let input =
|
|
9457
|
+
function removeDotSegments(path6) {
|
|
9458
|
+
let input = path6;
|
|
9459
9459
|
const output = [];
|
|
9460
9460
|
let nextSlash = -1;
|
|
9461
9461
|
let len = 0;
|
|
@@ -9654,8 +9654,8 @@ var require_schemes = __commonJS({
|
|
|
9654
9654
|
wsComponent.secure = void 0;
|
|
9655
9655
|
}
|
|
9656
9656
|
if (wsComponent.resourceName) {
|
|
9657
|
-
const [
|
|
9658
|
-
wsComponent.path =
|
|
9657
|
+
const [path6, query] = wsComponent.resourceName.split("?");
|
|
9658
|
+
wsComponent.path = path6 && path6 !== "/" ? path6 : void 0;
|
|
9659
9659
|
wsComponent.query = query;
|
|
9660
9660
|
wsComponent.resourceName = void 0;
|
|
9661
9661
|
}
|
|
@@ -13069,6 +13069,16 @@ var init_types2 = __esm({
|
|
|
13069
13069
|
});
|
|
13070
13070
|
|
|
13071
13071
|
// ../core/dist/core/errors.js
|
|
13072
|
+
function errorMessage(e) {
|
|
13073
|
+
return e instanceof Error ? e.message : String(e);
|
|
13074
|
+
}
|
|
13075
|
+
function getHttpStatusCode(error2) {
|
|
13076
|
+
if (error2 && typeof error2 === "object" && "status" in error2) {
|
|
13077
|
+
const status = error2.status;
|
|
13078
|
+
return typeof status === "number" && Number.isFinite(status) ? status : void 0;
|
|
13079
|
+
}
|
|
13080
|
+
return void 0;
|
|
13081
|
+
}
|
|
13072
13082
|
var OssAutopilotError, ConfigurationError, ValidationError;
|
|
13073
13083
|
var init_errors3 = __esm({
|
|
13074
13084
|
"../core/dist/core/errors.js"() {
|
|
@@ -13103,6 +13113,10 @@ function debug(module2, message, ...args) {
|
|
|
13103
13113
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
13104
13114
|
console.error(`[${timestamp}] [DEBUG] [${module2}] ${message}`, ...args);
|
|
13105
13115
|
}
|
|
13116
|
+
function info(module2, message, ...args) {
|
|
13117
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
13118
|
+
console.error(`[${timestamp}] [INFO] [${module2}] ${message}`, ...args);
|
|
13119
|
+
}
|
|
13106
13120
|
function warn(module2, message, ...args) {
|
|
13107
13121
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
13108
13122
|
console.error(`[${timestamp}] [WARN] [${module2}] ${message}`, ...args);
|
|
@@ -13214,6 +13228,17 @@ function splitRepo(repoFullName) {
|
|
|
13214
13228
|
const [owner, repo] = repoFullName.split("/");
|
|
13215
13229
|
return { owner, repo };
|
|
13216
13230
|
}
|
|
13231
|
+
function isOwnRepo(owner, username) {
|
|
13232
|
+
return owner.toLowerCase() === username.toLowerCase();
|
|
13233
|
+
}
|
|
13234
|
+
function getCLIVersion() {
|
|
13235
|
+
try {
|
|
13236
|
+
const pkgPath = path.join(path.dirname(process.argv[1]), "..", "package.json");
|
|
13237
|
+
return JSON.parse(fs.readFileSync(pkgPath, "utf-8")).version;
|
|
13238
|
+
} catch {
|
|
13239
|
+
return "0.0.0";
|
|
13240
|
+
}
|
|
13241
|
+
}
|
|
13217
13242
|
function formatRelativeTime(dateStr) {
|
|
13218
13243
|
const date3 = new Date(dateStr);
|
|
13219
13244
|
const diffMs = Date.now() - date3.getTime();
|
|
@@ -13503,8 +13528,7 @@ var init_state = __esm({
|
|
|
13503
13528
|
debug(MODULE2, "Migration complete!");
|
|
13504
13529
|
return true;
|
|
13505
13530
|
} catch (error2) {
|
|
13506
|
-
|
|
13507
|
-
warn(MODULE2, `Failed to migrate state: ${errorMessage2}`);
|
|
13531
|
+
warn(MODULE2, `Failed to migrate state: ${errorMessage(error2)}`);
|
|
13508
13532
|
const newStatePath2 = getStatePath();
|
|
13509
13533
|
if (fs2.existsSync(newStatePath2) && fs2.existsSync(LEGACY_STATE_FILE)) {
|
|
13510
13534
|
try {
|
|
@@ -13658,11 +13682,11 @@ var init_state = __esm({
|
|
|
13658
13682
|
try {
|
|
13659
13683
|
fs2.unlinkSync(path2.join(backupDir, file));
|
|
13660
13684
|
} catch (error2) {
|
|
13661
|
-
warn(MODULE2, `Could not delete old backup ${file}:`, error2
|
|
13685
|
+
warn(MODULE2, `Could not delete old backup ${file}:`, errorMessage(error2));
|
|
13662
13686
|
}
|
|
13663
13687
|
}
|
|
13664
13688
|
} catch (error2) {
|
|
13665
|
-
warn(MODULE2, "Could not clean up backups:", error2
|
|
13689
|
+
warn(MODULE2, "Could not clean up backups:", errorMessage(error2));
|
|
13666
13690
|
}
|
|
13667
13691
|
}
|
|
13668
13692
|
/**
|
|
@@ -13989,12 +14013,12 @@ var init_state = __esm({
|
|
|
13989
14013
|
* @returns true if the PR is snoozed and the snooze has not expired.
|
|
13990
14014
|
*/
|
|
13991
14015
|
isSnoozed(url) {
|
|
13992
|
-
const
|
|
13993
|
-
if (!
|
|
14016
|
+
const info2 = this.getSnoozeInfo(url);
|
|
14017
|
+
if (!info2)
|
|
13994
14018
|
return false;
|
|
13995
|
-
const expiresAtMs = new Date(
|
|
14019
|
+
const expiresAtMs = new Date(info2.expiresAt).getTime();
|
|
13996
14020
|
if (isNaN(expiresAtMs)) {
|
|
13997
|
-
warn(MODULE2, `Invalid expiresAt for snoozed PR ${url}: "${
|
|
14021
|
+
warn(MODULE2, `Invalid expiresAt for snoozed PR ${url}: "${info2.expiresAt}". Treating as not snoozed.`);
|
|
13998
14022
|
return false;
|
|
13999
14023
|
}
|
|
14000
14024
|
return expiresAtMs > Date.now();
|
|
@@ -14016,8 +14040,8 @@ var init_state = __esm({
|
|
|
14016
14040
|
return [];
|
|
14017
14041
|
const expired = [];
|
|
14018
14042
|
const now = Date.now();
|
|
14019
|
-
for (const [url,
|
|
14020
|
-
const expiresAtMs = new Date(
|
|
14043
|
+
for (const [url, info2] of Object.entries(this.state.config.snoozedPRs)) {
|
|
14044
|
+
const expiresAtMs = new Date(info2.expiresAt).getTime();
|
|
14021
14045
|
if (isNaN(expiresAtMs) || expiresAtMs <= now) {
|
|
14022
14046
|
expired.push(url);
|
|
14023
14047
|
}
|
|
@@ -15444,17 +15468,17 @@ function requestLog(octokit) {
|
|
|
15444
15468
|
octokit.log.debug("request", options);
|
|
15445
15469
|
const start = Date.now();
|
|
15446
15470
|
const requestOptions = octokit.request.endpoint.parse(options);
|
|
15447
|
-
const
|
|
15471
|
+
const path6 = requestOptions.url.replace(options.baseUrl, "");
|
|
15448
15472
|
return request2(options).then((response) => {
|
|
15449
15473
|
const requestId = response.headers["x-github-request-id"];
|
|
15450
15474
|
octokit.log.info(
|
|
15451
|
-
`${requestOptions.method} ${
|
|
15475
|
+
`${requestOptions.method} ${path6} - ${response.status} with id ${requestId} in ${Date.now() - start}ms`
|
|
15452
15476
|
);
|
|
15453
15477
|
return response;
|
|
15454
15478
|
}).catch((error2) => {
|
|
15455
15479
|
const requestId = error2.response?.headers["x-github-request-id"] || "UNKNOWN";
|
|
15456
15480
|
octokit.log.error(
|
|
15457
|
-
`${requestOptions.method} ${
|
|
15481
|
+
`${requestOptions.method} ${path6} - ${error2.status} with id ${requestId} in ${Date.now() - start}ms`
|
|
15458
15482
|
);
|
|
15459
15483
|
throw error2;
|
|
15460
15484
|
});
|
|
@@ -19435,7 +19459,7 @@ function isAuthRequest(method, pathname) {
|
|
|
19435
19459
|
}
|
|
19436
19460
|
function routeMatcher(paths) {
|
|
19437
19461
|
const regexes = paths.map(
|
|
19438
|
-
(
|
|
19462
|
+
(path6) => path6.split("/").map((c) => c.startsWith("{") ? "(?:.+?)" : c).join("/")
|
|
19439
19463
|
);
|
|
19440
19464
|
const regex2 = `^(?:${regexes.map((r) => `(?:${r})`).join("|")})[^/]*$`;
|
|
19441
19465
|
return new RegExp(regex2, "i");
|
|
@@ -19492,8 +19516,8 @@ function throttling(octokit, octokitOptions) {
|
|
|
19492
19516
|
"error",
|
|
19493
19517
|
(e) => octokit.log.warn("Error in throttling-plugin limit handler", e)
|
|
19494
19518
|
);
|
|
19495
|
-
state.retryLimiter.on("failed", async function(error2,
|
|
19496
|
-
const [state2, request2, options] =
|
|
19519
|
+
state.retryLimiter.on("failed", async function(error2, info2) {
|
|
19520
|
+
const [state2, request2, options] = info2.args;
|
|
19497
19521
|
const { pathname } = new URL(options.url, "http://github.test");
|
|
19498
19522
|
const shouldRetryGraphQL = pathname.startsWith("/graphql") && error2.status !== 401;
|
|
19499
19523
|
if (!(shouldRetryGraphQL || error2.status === 403 || error2.status === 429)) {
|
|
@@ -19753,10 +19777,7 @@ async function cachedRequest(cache, url, fetcher) {
|
|
|
19753
19777
|
}
|
|
19754
19778
|
}
|
|
19755
19779
|
function isNotModifiedError(err2) {
|
|
19756
|
-
|
|
19757
|
-
return err2.status === 304;
|
|
19758
|
-
}
|
|
19759
|
-
return false;
|
|
19780
|
+
return getHttpStatusCode(err2) === 304;
|
|
19760
19781
|
}
|
|
19761
19782
|
var fs3, path3, crypto2, MODULE4, DEFAULT_MAX_AGE_MS, HttpCache, _httpCache;
|
|
19762
19783
|
var init_http_cache = __esm({
|
|
@@ -19767,6 +19788,7 @@ var init_http_cache = __esm({
|
|
|
19767
19788
|
crypto2 = __toESM(require("crypto"), 1);
|
|
19768
19789
|
init_utils();
|
|
19769
19790
|
init_logger();
|
|
19791
|
+
init_errors3();
|
|
19770
19792
|
MODULE4 = "http-cache";
|
|
19771
19793
|
DEFAULT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
19772
19794
|
HttpCache = class {
|
|
@@ -19784,6 +19806,20 @@ var init_http_cache = __esm({
|
|
|
19784
19806
|
pathFor(url) {
|
|
19785
19807
|
return path3.join(this.cacheDir, `${this.keyFor(url)}.json`);
|
|
19786
19808
|
}
|
|
19809
|
+
/**
|
|
19810
|
+
* Return the cached body if the entry exists and is younger than `maxAgeMs`.
|
|
19811
|
+
* Useful for time-based caching where ETag validation isn't applicable
|
|
19812
|
+
* (e.g., caching aggregated results from paginated API calls).
|
|
19813
|
+
*/
|
|
19814
|
+
getIfFresh(key, maxAgeMs) {
|
|
19815
|
+
const entry = this.get(key);
|
|
19816
|
+
if (!entry)
|
|
19817
|
+
return null;
|
|
19818
|
+
const age = Date.now() - new Date(entry.cachedAt).getTime();
|
|
19819
|
+
if (!Number.isFinite(age) || age < 0 || age > maxAgeMs)
|
|
19820
|
+
return null;
|
|
19821
|
+
return entry.body;
|
|
19822
|
+
}
|
|
19787
19823
|
/**
|
|
19788
19824
|
* Look up a cached response. Returns `null` if no cache entry exists.
|
|
19789
19825
|
*/
|
|
@@ -20135,7 +20171,7 @@ function checkUnrespondedComments(comments, reviews, reviewComments, username) {
|
|
|
20135
20171
|
if (!review.submitted_at)
|
|
20136
20172
|
continue;
|
|
20137
20173
|
const body = (review.body || "").trim();
|
|
20138
|
-
if (!body && review.state !== "COMMENTED")
|
|
20174
|
+
if (!body && review.state !== "COMMENTED" && review.state !== "CHANGES_REQUESTED")
|
|
20139
20175
|
continue;
|
|
20140
20176
|
const author = review.user?.login || "unknown";
|
|
20141
20177
|
if (!body && review.state === "COMMENTED" && review.id != null) {
|
|
@@ -20143,7 +20179,7 @@ function checkUnrespondedComments(comments, reviews, reviewComments, username) {
|
|
|
20143
20179
|
continue;
|
|
20144
20180
|
}
|
|
20145
20181
|
}
|
|
20146
|
-
const resolvedBody = body || (review.id != null ? getInlineCommentBody(review.id, reviewComments) : void 0) || "(posted inline review comments)";
|
|
20182
|
+
const resolvedBody = body || (review.id != null ? getInlineCommentBody(review.id, reviewComments) : void 0) || (review.state === "CHANGES_REQUESTED" ? "(requested changes via inline review comments)" : "(posted inline review comments)");
|
|
20147
20183
|
timeline.push({
|
|
20148
20184
|
author,
|
|
20149
20185
|
body: resolvedBody,
|
|
@@ -20369,11 +20405,29 @@ var init_display_utils = __esm({
|
|
|
20369
20405
|
});
|
|
20370
20406
|
|
|
20371
20407
|
// ../core/dist/core/github-stats.js
|
|
20372
|
-
|
|
20408
|
+
function isCachedPRCounts(v) {
|
|
20409
|
+
if (typeof v !== "object" || v === null)
|
|
20410
|
+
return false;
|
|
20411
|
+
const obj = v;
|
|
20412
|
+
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;
|
|
20413
|
+
}
|
|
20414
|
+
async function fetchUserPRCounts(octokit, githubUsername, query, label, accumulateRepo) {
|
|
20373
20415
|
if (!githubUsername) {
|
|
20374
20416
|
return { repos: /* @__PURE__ */ new Map(), monthlyCounts: {}, monthlyOpenedCounts: {}, dailyActivityCounts: {} };
|
|
20375
20417
|
}
|
|
20376
|
-
|
|
20418
|
+
const cache = getHttpCache();
|
|
20419
|
+
const cacheKey2 = `pr-counts:${label}:${githubUsername}`;
|
|
20420
|
+
const cached2 = cache.getIfFresh(cacheKey2, PR_COUNTS_CACHE_TTL_MS);
|
|
20421
|
+
if (cached2 && isCachedPRCounts(cached2)) {
|
|
20422
|
+
debug(MODULE6, `Using cached ${label} PR counts for @${githubUsername}`);
|
|
20423
|
+
return {
|
|
20424
|
+
repos: new Map(cached2.reposEntries),
|
|
20425
|
+
monthlyCounts: cached2.monthlyCounts,
|
|
20426
|
+
monthlyOpenedCounts: cached2.monthlyOpenedCounts,
|
|
20427
|
+
dailyActivityCounts: cached2.dailyActivityCounts
|
|
20428
|
+
};
|
|
20429
|
+
}
|
|
20430
|
+
debug(MODULE6, `Fetching ${label} PR counts for @${githubUsername}...`);
|
|
20377
20431
|
const repos = /* @__PURE__ */ new Map();
|
|
20378
20432
|
const monthlyCounts = {};
|
|
20379
20433
|
const monthlyOpenedCounts = {};
|
|
@@ -20382,7 +20436,7 @@ async function fetchUserMergedPRCounts(octokit, githubUsername) {
|
|
|
20382
20436
|
let fetched = 0;
|
|
20383
20437
|
while (true) {
|
|
20384
20438
|
const { data } = await octokit.search.issuesAndPullRequests({
|
|
20385
|
-
q: `is:pr
|
|
20439
|
+
q: `is:pr ${query} author:${githubUsername}`,
|
|
20386
20440
|
sort: "updated",
|
|
20387
20441
|
order: "desc",
|
|
20388
20442
|
per_page: 100,
|
|
@@ -20391,26 +20445,20 @@ async function fetchUserMergedPRCounts(octokit, githubUsername) {
|
|
|
20391
20445
|
for (const item of data.items) {
|
|
20392
20446
|
const parsed = extractOwnerRepo(item.html_url);
|
|
20393
20447
|
if (!parsed) {
|
|
20394
|
-
warn(MODULE6, `Skipping
|
|
20448
|
+
warn(MODULE6, `Skipping ${label} PR with unparseable URL: ${item.html_url}`);
|
|
20395
20449
|
continue;
|
|
20396
20450
|
}
|
|
20397
20451
|
const { owner } = parsed;
|
|
20398
20452
|
const repo = `${owner}/${parsed.repo}`;
|
|
20399
|
-
if (owner
|
|
20453
|
+
if (isOwnRepo(owner, githubUsername))
|
|
20400
20454
|
continue;
|
|
20401
|
-
const
|
|
20402
|
-
|
|
20403
|
-
|
|
20404
|
-
existing.count += 1;
|
|
20405
|
-
if (mergedAt && mergedAt > existing.lastMergedAt) {
|
|
20406
|
-
existing.lastMergedAt = mergedAt;
|
|
20407
|
-
}
|
|
20408
|
-
} else {
|
|
20409
|
-
repos.set(repo, { count: 1, lastMergedAt: mergedAt });
|
|
20410
|
-
}
|
|
20411
|
-
if (mergedAt) {
|
|
20412
|
-
const month = mergedAt.slice(0, 7);
|
|
20455
|
+
const primaryDate = accumulateRepo(repos, repo, item);
|
|
20456
|
+
if (primaryDate) {
|
|
20457
|
+
const month = primaryDate.slice(0, 7);
|
|
20413
20458
|
monthlyCounts[month] = (monthlyCounts[month] || 0) + 1;
|
|
20459
|
+
const day = primaryDate.slice(0, 10);
|
|
20460
|
+
if (day.length === 10)
|
|
20461
|
+
dailyActivityCounts[day] = (dailyActivityCounts[day] || 0) + 1;
|
|
20414
20462
|
}
|
|
20415
20463
|
if (item.created_at) {
|
|
20416
20464
|
const openedMonth = item.created_at.slice(0, 7);
|
|
@@ -20419,11 +20467,6 @@ async function fetchUserMergedPRCounts(octokit, githubUsername) {
|
|
|
20419
20467
|
if (openedDay.length === 10)
|
|
20420
20468
|
dailyActivityCounts[openedDay] = (dailyActivityCounts[openedDay] || 0) + 1;
|
|
20421
20469
|
}
|
|
20422
|
-
if (mergedAt) {
|
|
20423
|
-
const mergedDay = mergedAt.slice(0, 10);
|
|
20424
|
-
if (mergedDay.length === 10)
|
|
20425
|
-
dailyActivityCounts[mergedDay] = (dailyActivityCounts[mergedDay] || 0) + 1;
|
|
20426
|
-
}
|
|
20427
20470
|
}
|
|
20428
20471
|
fetched += data.items.length;
|
|
20429
20472
|
if (fetched >= data.total_count || fetched >= 1e3 || data.items.length === 0) {
|
|
@@ -20431,62 +20474,38 @@ async function fetchUserMergedPRCounts(octokit, githubUsername) {
|
|
|
20431
20474
|
}
|
|
20432
20475
|
page++;
|
|
20433
20476
|
}
|
|
20434
|
-
debug(MODULE6, `Found ${fetched}
|
|
20477
|
+
debug(MODULE6, `Found ${fetched} ${label} PRs across ${repos.size} repos`);
|
|
20478
|
+
cache.set(cacheKey2, "", {
|
|
20479
|
+
reposEntries: Array.from(repos.entries()),
|
|
20480
|
+
monthlyCounts,
|
|
20481
|
+
monthlyOpenedCounts,
|
|
20482
|
+
dailyActivityCounts
|
|
20483
|
+
});
|
|
20435
20484
|
return { repos, monthlyCounts, monthlyOpenedCounts, dailyActivityCounts };
|
|
20436
20485
|
}
|
|
20437
|
-
|
|
20438
|
-
|
|
20439
|
-
|
|
20440
|
-
|
|
20441
|
-
debug(MODULE6, `Fetching closed PR counts for @${githubUsername}...`);
|
|
20442
|
-
const repos = /* @__PURE__ */ new Map();
|
|
20443
|
-
const monthlyCounts = {};
|
|
20444
|
-
const monthlyOpenedCounts = {};
|
|
20445
|
-
const dailyActivityCounts = {};
|
|
20446
|
-
let page = 1;
|
|
20447
|
-
let fetched = 0;
|
|
20448
|
-
while (true) {
|
|
20449
|
-
const { data } = await octokit.search.issuesAndPullRequests({
|
|
20450
|
-
q: `is:pr is:closed is:unmerged author:${githubUsername}`,
|
|
20451
|
-
sort: "updated",
|
|
20452
|
-
order: "desc",
|
|
20453
|
-
per_page: 100,
|
|
20454
|
-
page
|
|
20455
|
-
});
|
|
20456
|
-
for (const item of data.items) {
|
|
20457
|
-
const parsed = extractOwnerRepo(item.html_url);
|
|
20458
|
-
if (!parsed) {
|
|
20459
|
-
warn(MODULE6, `Skipping closed PR with unparseable URL: ${item.html_url}`);
|
|
20460
|
-
continue;
|
|
20461
|
-
}
|
|
20462
|
-
const { owner } = parsed;
|
|
20463
|
-
const repo = `${owner}/${parsed.repo}`;
|
|
20464
|
-
if (owner.toLowerCase() === githubUsername.toLowerCase())
|
|
20465
|
-
continue;
|
|
20466
|
-
repos.set(repo, (repos.get(repo) || 0) + 1);
|
|
20467
|
-
if (item.closed_at) {
|
|
20468
|
-
const closedMonth = item.closed_at.slice(0, 7);
|
|
20469
|
-
monthlyCounts[closedMonth] = (monthlyCounts[closedMonth] || 0) + 1;
|
|
20470
|
-
const closedDay = item.closed_at.slice(0, 10);
|
|
20471
|
-
if (closedDay.length === 10)
|
|
20472
|
-
dailyActivityCounts[closedDay] = (dailyActivityCounts[closedDay] || 0) + 1;
|
|
20473
|
-
}
|
|
20474
|
-
if (item.created_at) {
|
|
20475
|
-
const openedMonth = item.created_at.slice(0, 7);
|
|
20476
|
-
monthlyOpenedCounts[openedMonth] = (monthlyOpenedCounts[openedMonth] || 0) + 1;
|
|
20477
|
-
const openedDay = item.created_at.slice(0, 10);
|
|
20478
|
-
if (openedDay.length === 10)
|
|
20479
|
-
dailyActivityCounts[openedDay] = (dailyActivityCounts[openedDay] || 0) + 1;
|
|
20480
|
-
}
|
|
20486
|
+
function fetchUserMergedPRCounts(octokit, githubUsername) {
|
|
20487
|
+
return fetchUserPRCounts(octokit, githubUsername, "is:merged", "merged", (repos, repo, item) => {
|
|
20488
|
+
if (!item.pull_request?.merged_at) {
|
|
20489
|
+
warn(MODULE6, `merged_at missing for merged PR ${item.html_url}${item.closed_at ? ", falling back to closed_at" : ", no date available"}`);
|
|
20481
20490
|
}
|
|
20482
|
-
|
|
20483
|
-
|
|
20484
|
-
|
|
20491
|
+
const mergedAt = item.pull_request?.merged_at || item.closed_at || "";
|
|
20492
|
+
const existing = repos.get(repo);
|
|
20493
|
+
if (existing) {
|
|
20494
|
+
existing.count += 1;
|
|
20495
|
+
if (mergedAt && mergedAt > existing.lastMergedAt) {
|
|
20496
|
+
existing.lastMergedAt = mergedAt;
|
|
20497
|
+
}
|
|
20498
|
+
} else {
|
|
20499
|
+
repos.set(repo, { count: 1, lastMergedAt: mergedAt });
|
|
20485
20500
|
}
|
|
20486
|
-
|
|
20487
|
-
}
|
|
20488
|
-
|
|
20489
|
-
|
|
20501
|
+
return mergedAt;
|
|
20502
|
+
});
|
|
20503
|
+
}
|
|
20504
|
+
function fetchUserClosedPRCounts(octokit, githubUsername) {
|
|
20505
|
+
return fetchUserPRCounts(octokit, githubUsername, "is:closed is:unmerged", "closed", (repos, repo, item) => {
|
|
20506
|
+
repos.set(repo, (repos.get(repo) || 0) + 1);
|
|
20507
|
+
return item.closed_at || "";
|
|
20508
|
+
});
|
|
20490
20509
|
}
|
|
20491
20510
|
async function fetchRecentPRs(octokit, config2, query, label, days, mapItem) {
|
|
20492
20511
|
if (!config2.githubUsername) {
|
|
@@ -20511,7 +20530,7 @@ async function fetchRecentPRs(octokit, config2, query, label, days, mapItem) {
|
|
|
20511
20530
|
continue;
|
|
20512
20531
|
}
|
|
20513
20532
|
const repo = `${parsed.owner}/${parsed.repo}`;
|
|
20514
|
-
if (parsed.owner
|
|
20533
|
+
if (isOwnRepo(parsed.owner, config2.githubUsername))
|
|
20515
20534
|
continue;
|
|
20516
20535
|
if (config2.excludeRepos.includes(repo))
|
|
20517
20536
|
continue;
|
|
@@ -20546,14 +20565,15 @@ async function fetchRecentlyMergedPRs(octokit, config2, days = 7) {
|
|
|
20546
20565
|
};
|
|
20547
20566
|
});
|
|
20548
20567
|
}
|
|
20549
|
-
var MODULE6;
|
|
20568
|
+
var MODULE6, PR_COUNTS_CACHE_TTL_MS;
|
|
20550
20569
|
var init_github_stats = __esm({
|
|
20551
20570
|
"../core/dist/core/github-stats.js"() {
|
|
20552
20571
|
"use strict";
|
|
20553
20572
|
init_utils();
|
|
20554
|
-
init_errors3();
|
|
20555
20573
|
init_logger();
|
|
20574
|
+
init_http_cache();
|
|
20556
20575
|
MODULE6 = "github-stats";
|
|
20576
|
+
PR_COUNTS_CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
20557
20577
|
}
|
|
20558
20578
|
});
|
|
20559
20579
|
|
|
@@ -20654,9 +20674,9 @@ var init_pr_monitor = __esm({
|
|
|
20654
20674
|
if (pr)
|
|
20655
20675
|
prs.push(pr);
|
|
20656
20676
|
} catch (error2) {
|
|
20657
|
-
const
|
|
20658
|
-
warn("pr-monitor", `Error fetching ${item.html_url}: ${
|
|
20659
|
-
failures.push({ prUrl: item.html_url, error:
|
|
20677
|
+
const errMsg = errorMessage(error2);
|
|
20678
|
+
warn("pr-monitor", `Error fetching ${item.html_url}: ${errMsg}`);
|
|
20679
|
+
failures.push({ prUrl: item.html_url, error: errMsg });
|
|
20660
20680
|
}
|
|
20661
20681
|
}, MAX_CONCURRENT_REQUESTS);
|
|
20662
20682
|
});
|
|
@@ -20697,12 +20717,12 @@ var init_pr_monitor = __esm({
|
|
|
20697
20717
|
paginateAll((page) => this.octokit.issues.listComments({ owner, repo, issue_number: number3, per_page: 100, page })),
|
|
20698
20718
|
this.octokit.pulls.listReviews({ owner, repo, pull_number: number3 }),
|
|
20699
20719
|
paginateAll((page) => this.octokit.pulls.listReviewComments({ owner, repo, pull_number: number3, per_page: 100, page })).catch((err2) => {
|
|
20700
|
-
const status2 = err2
|
|
20720
|
+
const status2 = getHttpStatusCode(err2);
|
|
20701
20721
|
if (status2 === 429) {
|
|
20702
20722
|
throw err2;
|
|
20703
20723
|
}
|
|
20704
20724
|
if (status2 === 403) {
|
|
20705
|
-
const msg = (err2
|
|
20725
|
+
const msg = errorMessage(err2).toLowerCase();
|
|
20706
20726
|
if (msg.includes("rate limit") || msg.includes("abuse detection")) {
|
|
20707
20727
|
throw err2;
|
|
20708
20728
|
}
|
|
@@ -20794,6 +20814,9 @@ var init_pr_monitor = __esm({
|
|
|
20794
20814
|
const { ciStatus, hasMergeConflict, hasUnrespondedComment, hasIncompleteChecklist, reviewDecision, daysSinceActivity, dormantThreshold, approachingThreshold, latestCommitDate, lastMaintainerCommentDate, latestChangesRequestedDate } = input;
|
|
20795
20815
|
if (hasUnrespondedComment) {
|
|
20796
20816
|
if (latestCommitDate && lastMaintainerCommentDate && latestCommitDate > lastMaintainerCommentDate) {
|
|
20817
|
+
if (latestChangesRequestedDate && latestCommitDate < latestChangesRequestedDate) {
|
|
20818
|
+
return "needs_response";
|
|
20819
|
+
}
|
|
20797
20820
|
if (ciStatus === "failing")
|
|
20798
20821
|
return "failing_ci";
|
|
20799
20822
|
return "changes_addressed";
|
|
@@ -20854,7 +20877,7 @@ var init_pr_monitor = __esm({
|
|
|
20854
20877
|
this.octokit.repos.getCombinedStatusForRef({ owner, repo, ref: sha }),
|
|
20855
20878
|
// 404 is expected for repos without check runs configured; log other errors for debugging
|
|
20856
20879
|
this.octokit.checks.listForRef({ owner, repo, ref: sha }).catch((err2) => {
|
|
20857
|
-
const status = err2
|
|
20880
|
+
const status = getHttpStatusCode(err2);
|
|
20858
20881
|
if (status === 404) {
|
|
20859
20882
|
debug("pr-monitor", `Check runs 404 for ${owner}/${repo}@${sha.slice(0, 7)} (no checks configured)`);
|
|
20860
20883
|
} else {
|
|
@@ -20877,8 +20900,8 @@ var init_pr_monitor = __esm({
|
|
|
20877
20900
|
const combinedAnalysis = analyzeCombinedStatus(combinedStatus);
|
|
20878
20901
|
return mergeStatuses(checkRunAnalysis, combinedAnalysis, checkRuns.length);
|
|
20879
20902
|
} catch (error2) {
|
|
20880
|
-
const statusCode = error2
|
|
20881
|
-
const
|
|
20903
|
+
const statusCode = getHttpStatusCode(error2);
|
|
20904
|
+
const errMsg = errorMessage(error2);
|
|
20882
20905
|
if (statusCode === 401) {
|
|
20883
20906
|
warn("pr-monitor", `CI check failed for ${owner}/${repo}: Invalid token`);
|
|
20884
20907
|
} else if (statusCode === 403) {
|
|
@@ -20887,7 +20910,7 @@ var init_pr_monitor = __esm({
|
|
|
20887
20910
|
debug("pr-monitor", `CI check 404 for ${owner}/${repo} (no CI configured)`);
|
|
20888
20911
|
return { status: "unknown", failingCheckNames: [], failingCheckConclusions: /* @__PURE__ */ new Map() };
|
|
20889
20912
|
} else {
|
|
20890
|
-
warn("pr-monitor", `Failed to check CI for ${owner}/${repo}@${sha.slice(0, 7)}: ${
|
|
20913
|
+
warn("pr-monitor", `Failed to check CI for ${owner}/${repo}@${sha.slice(0, 7)}: ${errMsg}`);
|
|
20891
20914
|
}
|
|
20892
20915
|
return { status: "unknown", failingCheckNames: [], failingCheckConclusions: /* @__PURE__ */ new Map() };
|
|
20893
20916
|
}
|
|
@@ -20943,7 +20966,7 @@ var init_pr_monitor = __esm({
|
|
|
20943
20966
|
results.set(result.value.repo, result.value.stars);
|
|
20944
20967
|
} else {
|
|
20945
20968
|
chunkFailures++;
|
|
20946
|
-
warn(MODULE7, `Failed to fetch stars for ${chunk[j]}: ${result.reason
|
|
20969
|
+
warn(MODULE7, `Failed to fetch stars for ${chunk[j]}: ${errorMessage(result.reason)}`);
|
|
20947
20970
|
}
|
|
20948
20971
|
}
|
|
20949
20972
|
if (chunkFailures === chunk.length && chunk.length > 0) {
|
|
@@ -20957,45 +20980,6 @@ var init_pr_monitor = __esm({
|
|
|
20957
20980
|
debug(MODULE7, `Fetched star counts for ${results.size}/${repos.length} repos`);
|
|
20958
20981
|
return results;
|
|
20959
20982
|
}
|
|
20960
|
-
/**
|
|
20961
|
-
* Shared helper: search for recent PRs and filter out own repos, excluded repos/orgs.
|
|
20962
|
-
* Returns parsed search results that pass all filters.
|
|
20963
|
-
*/
|
|
20964
|
-
async fetchRecentPRs(query, label, days, mapItem) {
|
|
20965
|
-
const config2 = this.stateManager.getState().config;
|
|
20966
|
-
if (!config2.githubUsername) {
|
|
20967
|
-
warn(MODULE7, `Skipping recently ${label} PRs fetch: no githubUsername configured. Run /setup-oss to configure.`);
|
|
20968
|
-
return [];
|
|
20969
|
-
}
|
|
20970
|
-
const sinceDate = /* @__PURE__ */ new Date();
|
|
20971
|
-
sinceDate.setDate(sinceDate.getDate() - days);
|
|
20972
|
-
const since = sinceDate.toISOString().split("T")[0];
|
|
20973
|
-
debug(MODULE7, `Fetching recently ${label} PRs for @${config2.githubUsername} (since ${since})...`);
|
|
20974
|
-
const { data } = await this.octokit.search.issuesAndPullRequests({
|
|
20975
|
-
q: query.replace("{username}", config2.githubUsername).replace("{since}", since),
|
|
20976
|
-
sort: "updated",
|
|
20977
|
-
order: "desc",
|
|
20978
|
-
per_page: 100
|
|
20979
|
-
});
|
|
20980
|
-
const results = [];
|
|
20981
|
-
for (const item of data.items) {
|
|
20982
|
-
const parsed = parseGitHubUrl(item.html_url);
|
|
20983
|
-
if (!parsed) {
|
|
20984
|
-
warn(MODULE7, `Could not parse GitHub URL from API response: ${item.html_url}`);
|
|
20985
|
-
continue;
|
|
20986
|
-
}
|
|
20987
|
-
const repo = `${parsed.owner}/${parsed.repo}`;
|
|
20988
|
-
if (parsed.owner.toLowerCase() === config2.githubUsername.toLowerCase())
|
|
20989
|
-
continue;
|
|
20990
|
-
if (config2.excludeRepos.includes(repo))
|
|
20991
|
-
continue;
|
|
20992
|
-
if (config2.excludeOrgs?.some((org) => parsed.owner.toLowerCase() === org.toLowerCase()))
|
|
20993
|
-
continue;
|
|
20994
|
-
results.push(mapItem(item, { owner: parsed.owner, repo, number: parsed.number }));
|
|
20995
|
-
}
|
|
20996
|
-
debug(MODULE7, `Found ${results.length} recently ${label} PRs`);
|
|
20997
|
-
return results;
|
|
20998
|
-
}
|
|
20999
20983
|
/**
|
|
21000
20984
|
* Fetch PRs closed without merge in the last N days.
|
|
21001
20985
|
* Delegates to github-stats module.
|
|
@@ -21432,7 +21416,7 @@ var init_issue_vetting = __esm({
|
|
|
21432
21416
|
if (_IssueVetter.isRateLimitError(error2)) {
|
|
21433
21417
|
rateLimitFailures++;
|
|
21434
21418
|
}
|
|
21435
|
-
warn(MODULE8, `Error vetting issue ${url}:`, error2
|
|
21419
|
+
warn(MODULE8, `Error vetting issue ${url}:`, errorMessage(error2));
|
|
21436
21420
|
});
|
|
21437
21421
|
pending.push(task);
|
|
21438
21422
|
if (pending.length >= MAX_CONCURRENT_REQUESTS2) {
|
|
@@ -21449,11 +21433,11 @@ var init_issue_vetting = __esm({
|
|
|
21449
21433
|
}
|
|
21450
21434
|
/** Check if an error is a GitHub rate limit error (429 or rate-limit 403). */
|
|
21451
21435
|
static isRateLimitError(error2) {
|
|
21452
|
-
const status = error2
|
|
21436
|
+
const status = getHttpStatusCode(error2);
|
|
21453
21437
|
if (status === 429)
|
|
21454
21438
|
return true;
|
|
21455
21439
|
if (status === 403) {
|
|
21456
|
-
const msg =
|
|
21440
|
+
const msg = errorMessage(error2).toLowerCase();
|
|
21457
21441
|
return msg.includes("rate limit");
|
|
21458
21442
|
}
|
|
21459
21443
|
return false;
|
|
@@ -21477,9 +21461,9 @@ var init_issue_vetting = __esm({
|
|
|
21477
21461
|
});
|
|
21478
21462
|
return { passed: data.total_count === 0 && linkedPRs.length === 0 };
|
|
21479
21463
|
} catch (error2) {
|
|
21480
|
-
const
|
|
21481
|
-
warn(MODULE8, `Failed to check for existing PRs on ${owner}/${repo}#${issueNumber}: ${
|
|
21482
|
-
return { passed: true, inconclusive: true, reason:
|
|
21464
|
+
const errMsg = errorMessage(error2);
|
|
21465
|
+
warn(MODULE8, `Failed to check for existing PRs on ${owner}/${repo}#${issueNumber}: ${errMsg}. Assuming no existing PR.`);
|
|
21466
|
+
return { passed: true, inconclusive: true, reason: errMsg };
|
|
21483
21467
|
}
|
|
21484
21468
|
}
|
|
21485
21469
|
/**
|
|
@@ -21495,8 +21479,8 @@ var init_issue_vetting = __esm({
|
|
|
21495
21479
|
});
|
|
21496
21480
|
return data.total_count;
|
|
21497
21481
|
} catch (error2) {
|
|
21498
|
-
const
|
|
21499
|
-
warn(MODULE8, `Could not check merged PRs in ${owner}/${repo}: ${
|
|
21482
|
+
const errMsg = errorMessage(error2);
|
|
21483
|
+
warn(MODULE8, `Could not check merged PRs in ${owner}/${repo}: ${errMsg}. Defaulting to 0.`);
|
|
21500
21484
|
return 0;
|
|
21501
21485
|
}
|
|
21502
21486
|
}
|
|
@@ -21536,9 +21520,9 @@ var init_issue_vetting = __esm({
|
|
|
21536
21520
|
}
|
|
21537
21521
|
return { passed: true };
|
|
21538
21522
|
} catch (error2) {
|
|
21539
|
-
const
|
|
21540
|
-
warn(MODULE8, `Failed to check claim status on ${owner}/${repo}#${issueNumber}: ${
|
|
21541
|
-
return { passed: true, inconclusive: true, reason:
|
|
21523
|
+
const errMsg = errorMessage(error2);
|
|
21524
|
+
warn(MODULE8, `Failed to check claim status on ${owner}/${repo}#${issueNumber}: ${errMsg}. Assuming not claimed.`);
|
|
21525
|
+
return { passed: true, inconclusive: true, reason: errMsg };
|
|
21542
21526
|
}
|
|
21543
21527
|
}
|
|
21544
21528
|
async checkProjectHealth(owner, repo) {
|
|
@@ -21565,8 +21549,8 @@ var init_issue_vetting = __esm({
|
|
|
21565
21549
|
ciStatus = "passing";
|
|
21566
21550
|
}
|
|
21567
21551
|
} catch (error2) {
|
|
21568
|
-
const
|
|
21569
|
-
warn(MODULE8, `Failed to check CI status for ${owner}/${repo}: ${
|
|
21552
|
+
const errMsg = errorMessage(error2);
|
|
21553
|
+
warn(MODULE8, `Failed to check CI status for ${owner}/${repo}: ${errMsg}. Defaulting to unknown.`);
|
|
21570
21554
|
}
|
|
21571
21555
|
return {
|
|
21572
21556
|
repo: `${owner}/${repo}`,
|
|
@@ -21581,8 +21565,8 @@ var init_issue_vetting = __esm({
|
|
|
21581
21565
|
forksCount: repoData.forks_count
|
|
21582
21566
|
};
|
|
21583
21567
|
} catch (error2) {
|
|
21584
|
-
const
|
|
21585
|
-
warn(MODULE8, `Error checking project health for ${owner}/${repo}: ${
|
|
21568
|
+
const errMsg = errorMessage(error2);
|
|
21569
|
+
warn(MODULE8, `Error checking project health for ${owner}/${repo}: ${errMsg}`);
|
|
21586
21570
|
return {
|
|
21587
21571
|
repo: `${owner}/${repo}`,
|
|
21588
21572
|
lastCommitAt: "",
|
|
@@ -21592,7 +21576,7 @@ var init_issue_vetting = __esm({
|
|
|
21592
21576
|
ciStatus: "unknown",
|
|
21593
21577
|
isActive: false,
|
|
21594
21578
|
checkFailed: true,
|
|
21595
|
-
failureReason:
|
|
21579
|
+
failureReason: errMsg
|
|
21596
21580
|
};
|
|
21597
21581
|
}
|
|
21598
21582
|
}
|
|
@@ -21723,7 +21707,7 @@ var init_issue_discovery = __esm({
|
|
|
21723
21707
|
* Updates the state manager with the list and timestamp.
|
|
21724
21708
|
*/
|
|
21725
21709
|
async fetchStarredRepos() {
|
|
21726
|
-
|
|
21710
|
+
info(MODULE9, "Fetching starred repositories...");
|
|
21727
21711
|
const starredRepos = [];
|
|
21728
21712
|
try {
|
|
21729
21713
|
const iterator2 = this.octokit.paginate.iterator(this.octokit.activity.listReposStarredByAuthenticatedUser, {
|
|
@@ -21744,22 +21728,22 @@ var init_issue_discovery = __esm({
|
|
|
21744
21728
|
}
|
|
21745
21729
|
pageCount++;
|
|
21746
21730
|
if (pageCount >= 5) {
|
|
21747
|
-
|
|
21731
|
+
info(MODULE9, "Reached pagination limit for starred repos (500)");
|
|
21748
21732
|
break;
|
|
21749
21733
|
}
|
|
21750
21734
|
}
|
|
21751
|
-
|
|
21735
|
+
info(MODULE9, `Fetched ${starredRepos.length} starred repositories`);
|
|
21752
21736
|
this.stateManager.setStarredRepos(starredRepos);
|
|
21753
21737
|
return starredRepos;
|
|
21754
21738
|
} catch (error2) {
|
|
21755
21739
|
const cachedRepos = this.stateManager.getStarredRepos();
|
|
21756
|
-
const
|
|
21757
|
-
warn(MODULE9, "Error fetching starred repos:",
|
|
21740
|
+
const errMsg = errorMessage(error2);
|
|
21741
|
+
warn(MODULE9, "Error fetching starred repos:", errMsg);
|
|
21758
21742
|
if (cachedRepos.length === 0) {
|
|
21759
|
-
warn(MODULE9, `Failed to fetch starred repositories from GitHub API. No cached repos available. Error: ${
|
|
21743
|
+
warn(MODULE9, `Failed to fetch starred repositories from GitHub API. No cached repos available. Error: ${errMsg}
|
|
21760
21744
|
Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`);
|
|
21761
21745
|
} else {
|
|
21762
|
-
warn(MODULE9, `Failed to fetch starred repositories from GitHub API. Using ${cachedRepos.length} cached repos instead. Error: ${
|
|
21746
|
+
warn(MODULE9, `Failed to fetch starred repositories from GitHub API. Using ${cachedRepos.length} cached repos instead. Error: ${errMsg}`);
|
|
21763
21747
|
}
|
|
21764
21748
|
return cachedRepos;
|
|
21765
21749
|
}
|
|
@@ -21773,6 +21757,39 @@ Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`);
|
|
|
21773
21757
|
}
|
|
21774
21758
|
return this.stateManager.getStarredRepos();
|
|
21775
21759
|
}
|
|
21760
|
+
/**
|
|
21761
|
+
* Shared pipeline for Phases 2 and 3: spam-filter, repo-exclusion, vetting, and star-count filter.
|
|
21762
|
+
* Extracts the common logic so each phase only needs to supply search results and context.
|
|
21763
|
+
*/
|
|
21764
|
+
async filterVetAndScore(items, filterIssues, excludedRepoSets, remainingNeeded, minStars, phaseLabel) {
|
|
21765
|
+
const spamRepos = detectLabelFarmingRepos(items);
|
|
21766
|
+
if (spamRepos.size > 0) {
|
|
21767
|
+
const spamCount = items.filter((i) => spamRepos.has(i.repository_url.split("/").slice(-2).join("/"))).length;
|
|
21768
|
+
debug(MODULE9, `[SPAM_FILTER] Filtered ${spamCount} issues from ${spamRepos.size} label-farming repos: ${[...spamRepos].join(", ")}`);
|
|
21769
|
+
}
|
|
21770
|
+
const itemsToVet = filterIssues(items).filter((item) => {
|
|
21771
|
+
const repoFullName = item.repository_url.split("/").slice(-2).join("/");
|
|
21772
|
+
if (spamRepos.has(repoFullName))
|
|
21773
|
+
return false;
|
|
21774
|
+
return excludedRepoSets.every((s) => !s.has(repoFullName));
|
|
21775
|
+
}).slice(0, remainingNeeded * 2);
|
|
21776
|
+
if (itemsToVet.length === 0) {
|
|
21777
|
+
debug(MODULE9, `[${phaseLabel}] All ${items.length} items filtered before vetting`);
|
|
21778
|
+
return { candidates: [], allVetFailed: false, rateLimitHit: false };
|
|
21779
|
+
}
|
|
21780
|
+
const { candidates: results, allFailed: allVetFailed, rateLimitHit } = await this.vetter.vetIssuesParallel(itemsToVet.map((i) => i.html_url), remainingNeeded, "normal");
|
|
21781
|
+
const starFiltered = results.filter((c) => {
|
|
21782
|
+
if (c.projectHealth.checkFailed)
|
|
21783
|
+
return true;
|
|
21784
|
+
const stars = c.projectHealth.stargazersCount ?? 0;
|
|
21785
|
+
return stars >= minStars;
|
|
21786
|
+
});
|
|
21787
|
+
const starFilteredCount = results.length - starFiltered.length;
|
|
21788
|
+
if (starFilteredCount > 0) {
|
|
21789
|
+
debug(MODULE9, `[STAR_FILTER] Filtered ${starFilteredCount} ${phaseLabel} candidates below ${minStars} stars`);
|
|
21790
|
+
}
|
|
21791
|
+
return { candidates: starFiltered, allVetFailed, rateLimitHit };
|
|
21792
|
+
}
|
|
21776
21793
|
/**
|
|
21777
21794
|
* Search for issues matching our criteria.
|
|
21778
21795
|
* Searches in priority order: merged-PR repos first (no label filter), then starred repos,
|
|
@@ -21784,6 +21801,7 @@ Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`);
|
|
|
21784
21801
|
const languages = options.languages || config2.languages;
|
|
21785
21802
|
const labels = options.labels || config2.labels;
|
|
21786
21803
|
const maxResults = options.maxResults || 10;
|
|
21804
|
+
const minStars = config2.minStars ?? 50;
|
|
21787
21805
|
const allCandidates = [];
|
|
21788
21806
|
let phase0Error = null;
|
|
21789
21807
|
let phase1Error = null;
|
|
@@ -21797,10 +21815,10 @@ Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`);
|
|
|
21797
21815
|
warn(MODULE9, this.rateLimitWarning);
|
|
21798
21816
|
}
|
|
21799
21817
|
} catch (error2) {
|
|
21800
|
-
if (error2
|
|
21818
|
+
if (getHttpStatusCode(error2) === 401) {
|
|
21801
21819
|
throw error2;
|
|
21802
21820
|
}
|
|
21803
|
-
warn(MODULE9, "Could not check rate limit:", error2
|
|
21821
|
+
warn(MODULE9, "Could not check rate limit:", errorMessage(error2));
|
|
21804
21822
|
}
|
|
21805
21823
|
const mergedPRRepos = this.stateManager.getReposWithMergedPRs();
|
|
21806
21824
|
const mergedPRRepoSet = new Set(mergedPRRepos);
|
|
@@ -21820,7 +21838,7 @@ Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`);
|
|
|
21820
21838
|
const includeDocIssues = config2.includeDocIssues ?? true;
|
|
21821
21839
|
const aiBlocklisted = new Set(config2.aiPolicyBlocklist ?? DEFAULT_CONFIG.aiPolicyBlocklist ?? []);
|
|
21822
21840
|
if (aiBlocklisted.size > 0) {
|
|
21823
|
-
|
|
21841
|
+
debug(MODULE9, `[AI_POLICY_FILTER] Filtering issues from ${aiBlocklisted.size} blocklisted repo(s): ${[...aiBlocklisted].join(", ")}`);
|
|
21824
21842
|
}
|
|
21825
21843
|
const filterIssues = (items) => {
|
|
21826
21844
|
return items.filter((item) => {
|
|
@@ -21847,7 +21865,7 @@ Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`);
|
|
|
21847
21865
|
if (phase0Repos.length > 0) {
|
|
21848
21866
|
const mergedInPhase0 = Math.min(mergedPRRepos.length, phase0Repos.length);
|
|
21849
21867
|
const openInPhase0 = phase0Repos.length - mergedInPhase0;
|
|
21850
|
-
|
|
21868
|
+
info(MODULE9, `Phase 0: Searching issues in ${phase0Repos.length} repos (${mergedInPhase0} merged-PR, ${openInPhase0} open-PR, no label filter)...`);
|
|
21851
21869
|
const mergedPhase0Repos = phase0Repos.slice(0, mergedInPhase0);
|
|
21852
21870
|
if (mergedPhase0Repos.length > 0) {
|
|
21853
21871
|
const remainingNeeded = maxResults - allCandidates.length;
|
|
@@ -21860,7 +21878,7 @@ Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`);
|
|
|
21860
21878
|
if (rateLimitHit) {
|
|
21861
21879
|
rateLimitHitDuringSearch = true;
|
|
21862
21880
|
}
|
|
21863
|
-
|
|
21881
|
+
info(MODULE9, `Found ${mergedCandidates.length} candidates from merged-PR repos`);
|
|
21864
21882
|
}
|
|
21865
21883
|
}
|
|
21866
21884
|
const openPhase0Repos = phase0Repos.slice(mergedInPhase0);
|
|
@@ -21876,14 +21894,14 @@ Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`);
|
|
|
21876
21894
|
if (rateLimitHit) {
|
|
21877
21895
|
rateLimitHitDuringSearch = true;
|
|
21878
21896
|
}
|
|
21879
|
-
|
|
21897
|
+
info(MODULE9, `Found ${openCandidates.length} candidates from open-PR repos`);
|
|
21880
21898
|
}
|
|
21881
21899
|
}
|
|
21882
21900
|
}
|
|
21883
21901
|
if (allCandidates.length < maxResults && starredRepos.length > 0) {
|
|
21884
21902
|
const reposToSearch = starredRepos.filter((r) => !phase0RepoSet.has(r));
|
|
21885
21903
|
if (reposToSearch.length > 0) {
|
|
21886
|
-
|
|
21904
|
+
info(MODULE9, `Phase 1: Searching issues in ${reposToSearch.length} starred repos...`);
|
|
21887
21905
|
const remainingNeeded = maxResults - allCandidates.length;
|
|
21888
21906
|
if (remainingNeeded > 0) {
|
|
21889
21907
|
const { candidates: starredCandidates, allBatchesFailed, rateLimitHit } = await this.searchInRepos(reposToSearch.slice(0, 10), baseQuery, remainingNeeded, "starred", filterIssues);
|
|
@@ -21894,13 +21912,13 @@ Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`);
|
|
|
21894
21912
|
if (rateLimitHit) {
|
|
21895
21913
|
rateLimitHitDuringSearch = true;
|
|
21896
21914
|
}
|
|
21897
|
-
|
|
21915
|
+
info(MODULE9, `Found ${starredCandidates.length} candidates from starred repos`);
|
|
21898
21916
|
}
|
|
21899
21917
|
}
|
|
21900
21918
|
}
|
|
21901
21919
|
let phase2Error = null;
|
|
21902
21920
|
if (allCandidates.length < maxResults) {
|
|
21903
|
-
|
|
21921
|
+
info(MODULE9, "Phase 2: General issue search...");
|
|
21904
21922
|
const remainingNeeded = maxResults - allCandidates.length;
|
|
21905
21923
|
try {
|
|
21906
21924
|
const { data } = await this.octokit.search.issuesAndPullRequests({
|
|
@@ -21910,32 +21928,9 @@ Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`);
|
|
|
21910
21928
|
per_page: remainingNeeded * 3
|
|
21911
21929
|
// Fetch extra since some will be filtered
|
|
21912
21930
|
});
|
|
21913
|
-
|
|
21914
|
-
const spamRepos = detectLabelFarmingRepos(data.items);
|
|
21915
|
-
if (spamRepos.size > 0) {
|
|
21916
|
-
const spamCount = data.items.filter((i) => spamRepos.has(i.repository_url.split("/").slice(-2).join("/"))).length;
|
|
21917
|
-
console.log(`[SPAM_FILTER] Filtered ${spamCount} issues from ${spamRepos.size} label-farming repos: ${[...spamRepos].join(", ")}`);
|
|
21918
|
-
}
|
|
21931
|
+
info(MODULE9, `Found ${data.total_count} issues in general search, processing top ${data.items.length}...`);
|
|
21919
21932
|
const seenRepos = new Set(allCandidates.map((c) => c.issue.repo));
|
|
21920
|
-
const
|
|
21921
|
-
const repoFullName = item.repository_url.split("/").slice(-2).join("/");
|
|
21922
|
-
return !spamRepos.has(repoFullName);
|
|
21923
|
-
}).filter((item) => {
|
|
21924
|
-
const repoFullName = item.repository_url.split("/").slice(-2).join("/");
|
|
21925
|
-
return !phase0RepoSet.has(repoFullName) && !starredRepoSet.has(repoFullName) && !seenRepos.has(repoFullName);
|
|
21926
|
-
}).slice(0, remainingNeeded * 2);
|
|
21927
|
-
const { candidates: results, allFailed: allVetFailed, rateLimitHit: vetRateLimitHit } = await this.vetter.vetIssuesParallel(itemsToVet.map((i) => i.html_url), remainingNeeded, "normal");
|
|
21928
|
-
const minStars = config2.minStars ?? 50;
|
|
21929
|
-
const starFiltered = results.filter((c) => {
|
|
21930
|
-
if (c.projectHealth.checkFailed)
|
|
21931
|
-
return true;
|
|
21932
|
-
const stars = c.projectHealth.stargazersCount ?? 0;
|
|
21933
|
-
return stars >= minStars;
|
|
21934
|
-
});
|
|
21935
|
-
const starFilteredCount = results.length - starFiltered.length;
|
|
21936
|
-
if (starFilteredCount > 0) {
|
|
21937
|
-
console.log(`[STAR_FILTER] Filtered ${starFilteredCount} candidates below ${minStars} stars`);
|
|
21938
|
-
}
|
|
21933
|
+
const { candidates: starFiltered, allVetFailed, rateLimitHit: vetRateLimitHit } = await this.filterVetAndScore(data.items, filterIssues, [phase0RepoSet, starredRepoSet, seenRepos], remainingNeeded, minStars, "Phase 2");
|
|
21939
21934
|
allCandidates.push(...starFiltered);
|
|
21940
21935
|
if (allVetFailed) {
|
|
21941
21936
|
phase2Error = (phase2Error ? phase2Error + "; " : "") + "all vetting failed";
|
|
@@ -21943,25 +21938,24 @@ Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`);
|
|
|
21943
21938
|
if (vetRateLimitHit) {
|
|
21944
21939
|
rateLimitHitDuringSearch = true;
|
|
21945
21940
|
}
|
|
21946
|
-
|
|
21941
|
+
info(MODULE9, `Found ${starFiltered.length} candidates from general search`);
|
|
21947
21942
|
} catch (error2) {
|
|
21948
|
-
const
|
|
21949
|
-
phase2Error =
|
|
21943
|
+
const errMsg = errorMessage(error2);
|
|
21944
|
+
phase2Error = errMsg;
|
|
21950
21945
|
if (IssueVetter.isRateLimitError(error2)) {
|
|
21951
21946
|
rateLimitHitDuringSearch = true;
|
|
21952
21947
|
}
|
|
21953
|
-
warn(MODULE9, `Error in general issue search: ${
|
|
21948
|
+
warn(MODULE9, `Error in general issue search: ${errMsg}`);
|
|
21954
21949
|
}
|
|
21955
21950
|
}
|
|
21956
21951
|
let phase3Error = null;
|
|
21957
21952
|
if (allCandidates.length < maxResults) {
|
|
21958
|
-
|
|
21953
|
+
info(MODULE9, "Phase 3: Searching actively maintained repos...");
|
|
21959
21954
|
const remainingNeeded = maxResults - allCandidates.length;
|
|
21960
21955
|
const thirtyDaysAgo = /* @__PURE__ */ new Date();
|
|
21961
21956
|
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
|
|
21962
21957
|
const pushedSince = thirtyDaysAgo.toISOString().split("T")[0];
|
|
21963
|
-
const
|
|
21964
|
-
const phase3Query = `is:issue is:open no:assignee ${langQuery} stars:>=${phase3MinStars} pushed:>=${pushedSince} archived:false`.replace(/ +/g, " ").trim();
|
|
21958
|
+
const phase3Query = `is:issue is:open no:assignee ${langQuery} stars:>=${minStars} pushed:>=${pushedSince} archived:false`.replace(/ +/g, " ").trim();
|
|
21965
21959
|
try {
|
|
21966
21960
|
const { data } = await this.octokit.search.issuesAndPullRequests({
|
|
21967
21961
|
q: phase3Query,
|
|
@@ -21969,29 +21963,9 @@ Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`);
|
|
|
21969
21963
|
order: "desc",
|
|
21970
21964
|
per_page: remainingNeeded * 3
|
|
21971
21965
|
});
|
|
21972
|
-
|
|
21973
|
-
const spamRepos = detectLabelFarmingRepos(data.items);
|
|
21974
|
-
if (spamRepos.size > 0) {
|
|
21975
|
-
const spamCount = data.items.filter((i) => spamRepos.has(i.repository_url.split("/").slice(-2).join("/"))).length;
|
|
21976
|
-
console.log(`[SPAM_FILTER] Filtered ${spamCount} issues from ${spamRepos.size} label-farming repos: ${[...spamRepos].join(", ")}`);
|
|
21977
|
-
}
|
|
21966
|
+
info(MODULE9, `Found ${data.total_count} issues in maintained-repo search, processing top ${data.items.length}...`);
|
|
21978
21967
|
const seenRepos = new Set(allCandidates.map((c) => c.issue.repo));
|
|
21979
|
-
const
|
|
21980
|
-
const repoFullName = item.repository_url.split("/").slice(-2).join("/");
|
|
21981
|
-
return !spamRepos.has(repoFullName) && !phase0RepoSet.has(repoFullName) && !starredRepoSet.has(repoFullName) && !seenRepos.has(repoFullName);
|
|
21982
|
-
}).slice(0, remainingNeeded * 2);
|
|
21983
|
-
const { candidates: results, allFailed: allVetFailed, rateLimitHit: vetRateLimitHit } = await this.vetter.vetIssuesParallel(itemsToVet.map((i) => i.html_url), remainingNeeded, "normal");
|
|
21984
|
-
const minStars = config2.minStars ?? 50;
|
|
21985
|
-
const starFiltered = results.filter((c) => {
|
|
21986
|
-
if (c.projectHealth.checkFailed)
|
|
21987
|
-
return true;
|
|
21988
|
-
const stars = c.projectHealth.stargazersCount ?? 0;
|
|
21989
|
-
return stars >= minStars;
|
|
21990
|
-
});
|
|
21991
|
-
const starFilteredCount = results.length - starFiltered.length;
|
|
21992
|
-
if (starFilteredCount > 0) {
|
|
21993
|
-
console.log(`[STAR_FILTER] Filtered ${starFilteredCount} Phase 3 candidates below ${minStars} stars`);
|
|
21994
|
-
}
|
|
21968
|
+
const { candidates: starFiltered, allVetFailed, rateLimitHit: vetRateLimitHit } = await this.filterVetAndScore(data.items, filterIssues, [phase0RepoSet, starredRepoSet, seenRepos], remainingNeeded, minStars, "Phase 3");
|
|
21995
21969
|
allCandidates.push(...starFiltered);
|
|
21996
21970
|
if (allVetFailed) {
|
|
21997
21971
|
phase3Error = "all vetting failed";
|
|
@@ -21999,14 +21973,14 @@ Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`);
|
|
|
21999
21973
|
if (vetRateLimitHit) {
|
|
22000
21974
|
rateLimitHitDuringSearch = true;
|
|
22001
21975
|
}
|
|
22002
|
-
|
|
21976
|
+
info(MODULE9, `Found ${starFiltered.length} candidates from maintained-repo search`);
|
|
22003
21977
|
} catch (error2) {
|
|
22004
|
-
const
|
|
22005
|
-
phase3Error =
|
|
21978
|
+
const errMsg = errorMessage(error2);
|
|
21979
|
+
phase3Error = errMsg;
|
|
22006
21980
|
if (IssueVetter.isRateLimitError(error2)) {
|
|
22007
21981
|
rateLimitHitDuringSearch = true;
|
|
22008
21982
|
}
|
|
22009
|
-
warn(MODULE9, `Error in maintained-repo search: ${
|
|
21983
|
+
warn(MODULE9, `Error in maintained-repo search: ${errMsg}`);
|
|
22010
21984
|
}
|
|
22011
21985
|
}
|
|
22012
21986
|
if (allCandidates.length === 0) {
|
|
@@ -22079,7 +22053,7 @@ Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`);
|
|
|
22079
22053
|
rateLimitFailures++;
|
|
22080
22054
|
}
|
|
22081
22055
|
const batchRepos = batch.join(", ");
|
|
22082
|
-
warn(MODULE9, `Error searching issues in batch [${batchRepos}]:`, error2
|
|
22056
|
+
warn(MODULE9, `Error searching issues in batch [${batchRepos}]:`, errorMessage(error2));
|
|
22083
22057
|
}
|
|
22084
22058
|
}
|
|
22085
22059
|
const allBatchesFailed = failedBatches === batches.length && batches.length > 0;
|
|
@@ -22164,7 +22138,7 @@ Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`);
|
|
|
22164
22138
|
content += `- **Recommendation**: Y = approve, N = skip, ? = needs_review
|
|
22165
22139
|
`;
|
|
22166
22140
|
fs4.writeFileSync(outputFile, content, "utf-8");
|
|
22167
|
-
|
|
22141
|
+
info(MODULE9, `Saved ${sorted.length} issues to ${outputFile}`);
|
|
22168
22142
|
return outputFile;
|
|
22169
22143
|
}
|
|
22170
22144
|
/**
|
|
@@ -22266,7 +22240,7 @@ var init_issue_conversation = __esm({
|
|
|
22266
22240
|
}
|
|
22267
22241
|
const { owner, repo } = parsed;
|
|
22268
22242
|
const repoFullName = `${owner}/${repo}`;
|
|
22269
|
-
if (owner
|
|
22243
|
+
if (isOwnRepo(owner, username))
|
|
22270
22244
|
continue;
|
|
22271
22245
|
if (item.user?.login?.toLowerCase() === username.toLowerCase())
|
|
22272
22246
|
continue;
|
|
@@ -22295,7 +22269,7 @@ var init_issue_conversation = __esm({
|
|
|
22295
22269
|
});
|
|
22296
22270
|
}
|
|
22297
22271
|
} catch (error2) {
|
|
22298
|
-
const msg =
|
|
22272
|
+
const msg = errorMessage(error2);
|
|
22299
22273
|
failures.push({ issueUrl: item.html_url, error: msg });
|
|
22300
22274
|
warn(MODULE10, `Error analyzing issue ${item.html_url}: ${msg}`);
|
|
22301
22275
|
}
|
|
@@ -22725,6 +22699,7 @@ var init_core3 = __esm({
|
|
|
22725
22699
|
init_comment_utils();
|
|
22726
22700
|
init_github();
|
|
22727
22701
|
init_utils();
|
|
22702
|
+
init_errors3();
|
|
22728
22703
|
init_logger();
|
|
22729
22704
|
init_http_cache();
|
|
22730
22705
|
init_daily_logic();
|
|
@@ -22789,15 +22764,15 @@ async function fetchPRData(prMonitor, token) {
|
|
|
22789
22764
|
prMonitor.fetchUserMergedPRCounts(),
|
|
22790
22765
|
prMonitor.fetchUserClosedPRCounts(),
|
|
22791
22766
|
prMonitor.fetchRecentlyClosedPRs().catch((err2) => {
|
|
22792
|
-
console.error(`Warning: Failed to fetch recently closed PRs: ${err2
|
|
22767
|
+
console.error(`Warning: Failed to fetch recently closed PRs: ${errorMessage(err2)}`);
|
|
22793
22768
|
return [];
|
|
22794
22769
|
}),
|
|
22795
22770
|
prMonitor.fetchRecentlyMergedPRs().catch((err2) => {
|
|
22796
|
-
console.error(`Warning: Failed to fetch recently merged PRs: ${err2
|
|
22771
|
+
console.error(`Warning: Failed to fetch recently merged PRs: ${errorMessage(err2)}`);
|
|
22797
22772
|
return [];
|
|
22798
22773
|
}),
|
|
22799
22774
|
issueMonitor.fetchCommentedIssues().catch((error2) => {
|
|
22800
|
-
const msg =
|
|
22775
|
+
const msg = errorMessage(error2);
|
|
22801
22776
|
if (msg.includes("No GitHub username configured")) {
|
|
22802
22777
|
console.error(`[DAILY] Issue conversation tracking requires setup: ${msg}`);
|
|
22803
22778
|
} else {
|
|
@@ -22847,7 +22822,7 @@ async function updateRepoScores(prMonitor, prs, mergedCounts, closedCounts) {
|
|
|
22847
22822
|
stateManager2.updateRepoScore(repo, { mergedPRCount: count, lastMergedAt: lastMergedAt || void 0 });
|
|
22848
22823
|
} catch (error2) {
|
|
22849
22824
|
mergedCountFailures++;
|
|
22850
|
-
console.error(`[DAILY] Failed to update merged count for ${repo}:`, error2
|
|
22825
|
+
console.error(`[DAILY] Failed to update merged count for ${repo}:`, errorMessage(error2));
|
|
22851
22826
|
}
|
|
22852
22827
|
}
|
|
22853
22828
|
if (mergedCountFailures === mergedCounts.size && mergedCounts.size > 0) {
|
|
@@ -22863,7 +22838,7 @@ async function updateRepoScores(prMonitor, prs, mergedCounts, closedCounts) {
|
|
|
22863
22838
|
stateManager2.updateRepoScore(repo, { closedWithoutMergeCount: count });
|
|
22864
22839
|
} catch (error2) {
|
|
22865
22840
|
closedCountFailures++;
|
|
22866
|
-
console.error(`[DAILY] Failed to update closed count for ${repo}:`, error2
|
|
22841
|
+
console.error(`[DAILY] Failed to update closed count for ${repo}:`, errorMessage(error2));
|
|
22867
22842
|
}
|
|
22868
22843
|
}
|
|
22869
22844
|
if (closedCountFailures === closedCounts.size && closedCounts.size > 0) {
|
|
@@ -22876,7 +22851,7 @@ async function updateRepoScores(prMonitor, prs, mergedCounts, closedCounts) {
|
|
|
22876
22851
|
stateManager2.updateRepoScore(repo, { signals });
|
|
22877
22852
|
} catch (error2) {
|
|
22878
22853
|
signalUpdateFailures++;
|
|
22879
|
-
console.error(`[DAILY] Failed to update signals for ${repo}:`, error2
|
|
22854
|
+
console.error(`[DAILY] Failed to update signals for ${repo}:`, errorMessage(error2));
|
|
22880
22855
|
}
|
|
22881
22856
|
}
|
|
22882
22857
|
if (signalUpdateFailures === repoSignals.size && repoSignals.size > 0) {
|
|
@@ -22887,7 +22862,7 @@ async function updateRepoScores(prMonitor, prs, mergedCounts, closedCounts) {
|
|
|
22887
22862
|
try {
|
|
22888
22863
|
starCounts = await prMonitor.fetchRepoStarCounts(allRepos);
|
|
22889
22864
|
} catch (error2) {
|
|
22890
|
-
console.error("[DAILY] Failed to fetch repo star counts:", error2
|
|
22865
|
+
console.error("[DAILY] Failed to fetch repo star counts:", errorMessage(error2));
|
|
22891
22866
|
console.error("[DAILY] Dashboard minStars filter will use cached star counts (or be skipped for repos without cached data).");
|
|
22892
22867
|
starCounts = /* @__PURE__ */ new Map();
|
|
22893
22868
|
}
|
|
@@ -22897,7 +22872,7 @@ async function updateRepoScores(prMonitor, prs, mergedCounts, closedCounts) {
|
|
|
22897
22872
|
stateManager2.updateRepoScore(repo, { stargazersCount: stars });
|
|
22898
22873
|
} catch (error2) {
|
|
22899
22874
|
starUpdateFailures++;
|
|
22900
|
-
console.error(`[DAILY] Failed to update star count for ${repo}:`, error2
|
|
22875
|
+
console.error(`[DAILY] Failed to update star count for ${repo}:`, errorMessage(error2));
|
|
22901
22876
|
}
|
|
22902
22877
|
}
|
|
22903
22878
|
if (starUpdateFailures === starCounts.size && starCounts.size > 0) {
|
|
@@ -22909,7 +22884,7 @@ async function updateRepoScores(prMonitor, prs, mergedCounts, closedCounts) {
|
|
|
22909
22884
|
stateManager2.addTrustedProject(repo);
|
|
22910
22885
|
} catch (error2) {
|
|
22911
22886
|
trustSyncFailures++;
|
|
22912
|
-
console.error(`[DAILY] Failed to sync trusted project ${repo}:`, error2
|
|
22887
|
+
console.error(`[DAILY] Failed to sync trusted project ${repo}:`, errorMessage(error2));
|
|
22913
22888
|
}
|
|
22914
22889
|
}
|
|
22915
22890
|
if (trustSyncFailures === mergedCounts.size && mergedCounts.size > 0) {
|
|
@@ -22921,12 +22896,12 @@ function updateAnalytics(prs, monthlyCounts, monthlyClosedCounts, openedFromMerg
|
|
|
22921
22896
|
try {
|
|
22922
22897
|
stateManager2.setMonthlyMergedCounts(monthlyCounts);
|
|
22923
22898
|
} catch (error2) {
|
|
22924
|
-
console.error("[DAILY] Failed to store monthly merged counts:", error2
|
|
22899
|
+
console.error("[DAILY] Failed to store monthly merged counts:", errorMessage(error2));
|
|
22925
22900
|
}
|
|
22926
22901
|
try {
|
|
22927
22902
|
stateManager2.setMonthlyClosedCounts(monthlyClosedCounts);
|
|
22928
22903
|
} catch (error2) {
|
|
22929
|
-
console.error("[DAILY] Failed to store monthly closed counts:", error2
|
|
22904
|
+
console.error("[DAILY] Failed to store monthly closed counts:", errorMessage(error2));
|
|
22930
22905
|
}
|
|
22931
22906
|
try {
|
|
22932
22907
|
const combinedOpenedCounts = { ...openedFromMerged };
|
|
@@ -22941,7 +22916,7 @@ function updateAnalytics(prs, monthlyCounts, monthlyClosedCounts, openedFromMerg
|
|
|
22941
22916
|
}
|
|
22942
22917
|
stateManager2.setMonthlyOpenedCounts(combinedOpenedCounts);
|
|
22943
22918
|
} catch (error2) {
|
|
22944
|
-
console.error("[DAILY] Failed to compute/store monthly opened counts:", error2
|
|
22919
|
+
console.error("[DAILY] Failed to compute/store monthly opened counts:", errorMessage(error2));
|
|
22945
22920
|
}
|
|
22946
22921
|
}
|
|
22947
22922
|
function partitionPRs(prMonitor, prs, recentlyClosedPRs, recentlyMergedPRs) {
|
|
@@ -22956,7 +22931,7 @@ function partitionPRs(prMonitor, prs, recentlyClosedPRs, recentlyMergedPRs) {
|
|
|
22956
22931
|
stateManager2.save();
|
|
22957
22932
|
}
|
|
22958
22933
|
} catch (error2) {
|
|
22959
|
-
console.error("[DAILY] Failed to expire/persist snoozes:", error2
|
|
22934
|
+
console.error("[DAILY] Failed to expire/persist snoozes:", errorMessage(error2));
|
|
22960
22935
|
}
|
|
22961
22936
|
const shelvedPRs = [];
|
|
22962
22937
|
const autoUnshelvedPRs = [];
|
|
@@ -23015,14 +22990,15 @@ function generateDigestOutput(digest, activePRs, shelvedPRs, commentedIssues, fa
|
|
|
23015
22990
|
const issueResponses = filteredCommentedIssues.filter((i) => i.status === "new_response");
|
|
23016
22991
|
const summary = formatSummary(digest, capacity, issueResponses);
|
|
23017
22992
|
const snoozedUrls = new Set(Object.keys(stateManager2.getState().config.snoozedPRs ?? {}).filter((url) => stateManager2.isSnoozed(url)));
|
|
23018
|
-
const
|
|
22993
|
+
const dismissedUrls = new Set(Object.keys(stateManager2.getState().config.dismissedIssues ?? {}));
|
|
22994
|
+
const nonDismissedPRs = activePRs.filter((pr) => !dismissedUrls.has(pr.url));
|
|
22995
|
+
const actionableIssues = collectActionableIssues(nonDismissedPRs, snoozedUrls);
|
|
23019
22996
|
digest.summary.totalNeedingAttention = actionableIssues.length;
|
|
23020
22997
|
const briefSummary = formatBriefSummary(digest, actionableIssues.length, issueResponses.length);
|
|
23021
22998
|
const actionMenu = computeActionMenu(actionableIssues, capacity, filteredCommentedIssues);
|
|
23022
22999
|
const repoGroups = groupPRsByRepo(activePRs);
|
|
23023
23000
|
return {
|
|
23024
23001
|
digest,
|
|
23025
|
-
updates: [],
|
|
23026
23002
|
capacity,
|
|
23027
23003
|
summary,
|
|
23028
23004
|
briefSummary,
|
|
@@ -23036,7 +23012,6 @@ function generateDigestOutput(digest, activePRs, shelvedPRs, commentedIssues, fa
|
|
|
23036
23012
|
function toDailyOutput(result) {
|
|
23037
23013
|
return {
|
|
23038
23014
|
digest: deduplicateDigest(result.digest),
|
|
23039
|
-
updates: result.updates,
|
|
23040
23015
|
capacity: result.capacity,
|
|
23041
23016
|
summary: result.summary,
|
|
23042
23017
|
briefSummary: result.briefSummary,
|
|
@@ -23067,6 +23042,7 @@ var init_daily = __esm({
|
|
|
23067
23042
|
"../core/dist/commands/daily.js"() {
|
|
23068
23043
|
"use strict";
|
|
23069
23044
|
init_core3();
|
|
23045
|
+
init_errors3();
|
|
23070
23046
|
init_json();
|
|
23071
23047
|
init_core3();
|
|
23072
23048
|
}
|
|
@@ -23084,11 +23060,12 @@ var init_dashboard_data = __esm({
|
|
|
23084
23060
|
"../core/dist/commands/dashboard-data.js"() {
|
|
23085
23061
|
"use strict";
|
|
23086
23062
|
init_core3();
|
|
23063
|
+
init_errors3();
|
|
23087
23064
|
init_daily();
|
|
23088
23065
|
}
|
|
23089
23066
|
});
|
|
23090
23067
|
|
|
23091
|
-
// ../core/dist/commands/dashboard-
|
|
23068
|
+
// ../core/dist/commands/dashboard-formatters.js
|
|
23092
23069
|
function escapeHtml(text) {
|
|
23093
23070
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
23094
23071
|
}
|
|
@@ -23107,78 +23084,18 @@ function buildDashboardStats(digest, state) {
|
|
|
23107
23084
|
mergeRate: `${(summary.mergeRate ?? 0).toFixed(1)}%`
|
|
23108
23085
|
};
|
|
23109
23086
|
}
|
|
23110
|
-
|
|
23111
|
-
|
|
23112
|
-
|
|
23113
|
-
|
|
23114
|
-
|
|
23115
|
-
|
|
23116
|
-
|
|
23117
|
-
|
|
23118
|
-
|
|
23119
|
-
|
|
23120
|
-
|
|
23121
|
-
|
|
23122
|
-
...digest.incompleteChecklistPRs || [],
|
|
23123
|
-
...digest.missingRequiredFilesPRs || [],
|
|
23124
|
-
...digest.needsRebasePRs || []
|
|
23125
|
-
];
|
|
23126
|
-
const waitingOnOthers = [
|
|
23127
|
-
...digest.changesAddressedPRs || [],
|
|
23128
|
-
...digest.waitingOnMaintainerPRs || [],
|
|
23129
|
-
...digest.ciBlockedPRs || [],
|
|
23130
|
-
...digest.ciNotRunningPRs || []
|
|
23131
|
-
];
|
|
23132
|
-
function truncateTitle(title, max = 50) {
|
|
23133
|
-
const truncated = title.length <= max ? title : title.slice(0, max) + "...";
|
|
23134
|
-
return escapeHtml(truncated);
|
|
23135
|
-
}
|
|
23136
|
-
function renderHealthItems(prs, cssClass, svgPaths, labelFn, metaFn) {
|
|
23137
|
-
return prs.map((pr) => {
|
|
23138
|
-
const rawLabel = typeof labelFn === "string" ? labelFn : labelFn(pr);
|
|
23139
|
-
const label = escapeHtml(rawLabel);
|
|
23140
|
-
return `
|
|
23141
|
-
<div class="health-item ${cssClass}" data-status="${cssClass}" data-repo="${escapeHtml(pr.repo)}" data-title="${escapeHtml(pr.title.toLowerCase())}">
|
|
23142
|
-
<div class="health-icon">
|
|
23143
|
-
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
23144
|
-
${svgPaths}
|
|
23145
|
-
</svg>
|
|
23146
|
-
</div>
|
|
23147
|
-
<div class="health-content">
|
|
23148
|
-
<div class="health-title"><a href="${escapeHtml(pr.url)}" target="_blank">${escapeHtml(pr.repo)}#${pr.number}</a> - ${label}</div>
|
|
23149
|
-
<div class="health-meta">${metaFn(pr)}</div>
|
|
23150
|
-
</div>
|
|
23151
|
-
</div>`;
|
|
23152
|
-
}).join("");
|
|
23153
|
-
}
|
|
23154
|
-
const SVG = {
|
|
23155
|
-
comment: '<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>',
|
|
23156
|
-
edit: '<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>',
|
|
23157
|
-
xCircle: '<circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/>',
|
|
23158
|
-
conflict: '<path d="M8 3v3a2 2 0 0 1-2 2H3"/><path d="M21 8h-3a2 2 0 0 1-2-2V3"/><path d="M3 16h3a2 2 0 0 1 2 2v3"/><path d="M16 21v-3a2 2 0 0 1 2-2h3"/>',
|
|
23159
|
-
checklist: '<path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>',
|
|
23160
|
-
file: '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="12" y1="18" x2="12" y2="12"/><line x1="9" y1="15" x2="15" y2="15"/>',
|
|
23161
|
-
checkCircle: '<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/>',
|
|
23162
|
-
clock: '<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>',
|
|
23163
|
-
lock: '<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
|
|
23164
|
-
infoCircle: '<circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/>',
|
|
23165
|
-
refresh: '<polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/>',
|
|
23166
|
-
box: '<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/>',
|
|
23167
|
-
bell: '<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/>',
|
|
23168
|
-
gitMerge: '<circle cx="7" cy="18" r="3"/><circle cx="7" cy="6" r="3"/><circle cx="17" cy="12" r="3"/><line x1="7" y1="9" x2="7" y2="15"/><path d="M7 9c0 4 10 3 10 3"/>'
|
|
23169
|
-
};
|
|
23170
|
-
const titleMeta = (pr) => truncateTitle(pr.title);
|
|
23171
|
-
return `<!DOCTYPE html>
|
|
23172
|
-
<html lang="en">
|
|
23173
|
-
<head>
|
|
23174
|
-
<meta charset="UTF-8">
|
|
23175
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
23176
|
-
<title>OSS Autopilot - Mission Control</title>
|
|
23177
|
-
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
23178
|
-
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
23179
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
23180
|
-
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
23181
|
-
<style>
|
|
23087
|
+
var init_dashboard_formatters = __esm({
|
|
23088
|
+
"../core/dist/commands/dashboard-formatters.js"() {
|
|
23089
|
+
"use strict";
|
|
23090
|
+
}
|
|
23091
|
+
});
|
|
23092
|
+
|
|
23093
|
+
// ../core/dist/commands/dashboard-styles.js
|
|
23094
|
+
var DASHBOARD_CSS;
|
|
23095
|
+
var init_dashboard_styles = __esm({
|
|
23096
|
+
"../core/dist/commands/dashboard-styles.js"() {
|
|
23097
|
+
"use strict";
|
|
23098
|
+
DASHBOARD_CSS = `
|
|
23182
23099
|
:root, [data-theme="dark"] {
|
|
23183
23100
|
--bg-base: #080b10;
|
|
23184
23101
|
--bg-surface: rgba(22, 27, 34, 0.65);
|
|
@@ -23938,6 +23855,369 @@ function generateDashboardHtml(stats, monthlyMerged, monthlyClosed, monthlyOpene
|
|
|
23938
23855
|
.health-item[data-hidden="true"] {
|
|
23939
23856
|
display: none;
|
|
23940
23857
|
}
|
|
23858
|
+
`;
|
|
23859
|
+
}
|
|
23860
|
+
});
|
|
23861
|
+
|
|
23862
|
+
// ../core/dist/commands/dashboard-components.js
|
|
23863
|
+
function truncateTitle(title, max = 50) {
|
|
23864
|
+
const truncated = title.length <= max ? title : title.slice(0, max) + "...";
|
|
23865
|
+
return escapeHtml(truncated);
|
|
23866
|
+
}
|
|
23867
|
+
function renderHealthItems(prs, cssClass, svgPaths, labelFn, metaFn) {
|
|
23868
|
+
return prs.map((pr) => {
|
|
23869
|
+
const rawLabel = typeof labelFn === "string" ? labelFn : labelFn(pr);
|
|
23870
|
+
const label = escapeHtml(rawLabel);
|
|
23871
|
+
return `
|
|
23872
|
+
<div class="health-item ${cssClass}" data-status="${cssClass}" data-repo="${escapeHtml(pr.repo)}" data-title="${escapeHtml(pr.title.toLowerCase())}">
|
|
23873
|
+
<div class="health-icon">
|
|
23874
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
23875
|
+
${svgPaths}
|
|
23876
|
+
</svg>
|
|
23877
|
+
</div>
|
|
23878
|
+
<div class="health-content">
|
|
23879
|
+
<div class="health-title"><a href="${escapeHtml(pr.url)}" target="_blank">${escapeHtml(pr.repo)}#${pr.number}</a> - ${label}</div>
|
|
23880
|
+
<div class="health-meta">${metaFn(pr)}</div>
|
|
23881
|
+
</div>
|
|
23882
|
+
</div>`;
|
|
23883
|
+
}).join("");
|
|
23884
|
+
}
|
|
23885
|
+
function titleMeta(pr) {
|
|
23886
|
+
return truncateTitle(pr.title);
|
|
23887
|
+
}
|
|
23888
|
+
var SVG_ICONS;
|
|
23889
|
+
var init_dashboard_components = __esm({
|
|
23890
|
+
"../core/dist/commands/dashboard-components.js"() {
|
|
23891
|
+
"use strict";
|
|
23892
|
+
init_dashboard_formatters();
|
|
23893
|
+
SVG_ICONS = {
|
|
23894
|
+
comment: '<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>',
|
|
23895
|
+
edit: '<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>',
|
|
23896
|
+
xCircle: '<circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/>',
|
|
23897
|
+
conflict: '<path d="M8 3v3a2 2 0 0 1-2 2H3"/><path d="M21 8h-3a2 2 0 0 1-2-2V3"/><path d="M3 16h3a2 2 0 0 1 2 2v3"/><path d="M16 21v-3a2 2 0 0 1 2-2h3"/>',
|
|
23898
|
+
checklist: '<path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>',
|
|
23899
|
+
file: '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="12" y1="18" x2="12" y2="12"/><line x1="9" y1="15" x2="15" y2="15"/>',
|
|
23900
|
+
checkCircle: '<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/>',
|
|
23901
|
+
clock: '<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>',
|
|
23902
|
+
lock: '<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
|
|
23903
|
+
infoCircle: '<circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/>',
|
|
23904
|
+
refresh: '<polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/>',
|
|
23905
|
+
box: '<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/>',
|
|
23906
|
+
bell: '<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/>',
|
|
23907
|
+
gitMerge: '<circle cx="7" cy="18" r="3"/><circle cx="7" cy="6" r="3"/><circle cx="17" cy="12" r="3"/><line x1="7" y1="9" x2="7" y2="15"/><path d="M7 9c0 4 10 3 10 3"/>'
|
|
23908
|
+
};
|
|
23909
|
+
}
|
|
23910
|
+
});
|
|
23911
|
+
|
|
23912
|
+
// ../core/dist/commands/dashboard-scripts.js
|
|
23913
|
+
function generateDashboardScripts(stats, monthlyMerged, monthlyClosed, monthlyOpened, digest, state) {
|
|
23914
|
+
const statusChart = `
|
|
23915
|
+
Chart.defaults.color = '#6e7681';
|
|
23916
|
+
Chart.defaults.borderColor = 'rgba(48, 54, 61, 0.4)';
|
|
23917
|
+
Chart.defaults.font.family = "'Geist', sans-serif";
|
|
23918
|
+
Chart.defaults.font.size = 11;
|
|
23919
|
+
|
|
23920
|
+
// === Status Doughnut ===
|
|
23921
|
+
new Chart(document.getElementById('statusChart'), {
|
|
23922
|
+
type: 'doughnut',
|
|
23923
|
+
data: {
|
|
23924
|
+
labels: ['Active', 'Shelved', 'Merged', 'Closed'],
|
|
23925
|
+
datasets: [{
|
|
23926
|
+
data: [${stats.activePRs}, ${stats.shelvedPRs}, ${stats.mergedPRs}, ${stats.closedPRs}],
|
|
23927
|
+
backgroundColor: ['#3fb950', '#6e7681', '#a855f7', '#484f58'],
|
|
23928
|
+
borderColor: 'rgba(8, 11, 16, 0.8)',
|
|
23929
|
+
borderWidth: 2,
|
|
23930
|
+
hoverOffset: 8
|
|
23931
|
+
}]
|
|
23932
|
+
},
|
|
23933
|
+
options: {
|
|
23934
|
+
responsive: true,
|
|
23935
|
+
maintainAspectRatio: false,
|
|
23936
|
+
cutout: '65%',
|
|
23937
|
+
plugins: {
|
|
23938
|
+
legend: {
|
|
23939
|
+
position: 'bottom',
|
|
23940
|
+
labels: { padding: 16, usePointStyle: true, pointStyle: 'circle', font: { size: 11 } }
|
|
23941
|
+
}
|
|
23942
|
+
}
|
|
23943
|
+
}
|
|
23944
|
+
});`;
|
|
23945
|
+
const repoChart = (() => {
|
|
23946
|
+
const { excludeRepos: exRepos = [], excludeOrgs: exOrgs, minStars } = state.config;
|
|
23947
|
+
const starThreshold = minStars ?? 50;
|
|
23948
|
+
const shouldExcludeRepo = (repo) => {
|
|
23949
|
+
const repoLower = repo.toLowerCase();
|
|
23950
|
+
if (exRepos.some((r) => r.toLowerCase() === repoLower))
|
|
23951
|
+
return true;
|
|
23952
|
+
if (exOrgs?.some((o) => o.toLowerCase() === repoLower.split("/")[0]))
|
|
23953
|
+
return true;
|
|
23954
|
+
const score = (state.repoScores || {})[repo];
|
|
23955
|
+
if (score?.stargazersCount !== void 0 && score.stargazersCount < starThreshold)
|
|
23956
|
+
return true;
|
|
23957
|
+
return false;
|
|
23958
|
+
};
|
|
23959
|
+
const allRepoEntries = Object.entries(
|
|
23960
|
+
// Rebuild from full prsByRepo to get all repos, not just top 10
|
|
23961
|
+
(() => {
|
|
23962
|
+
const all = {};
|
|
23963
|
+
for (const pr of digest.openPRs || []) {
|
|
23964
|
+
if (shouldExcludeRepo(pr.repo))
|
|
23965
|
+
continue;
|
|
23966
|
+
if (!all[pr.repo])
|
|
23967
|
+
all[pr.repo] = { active: 0, merged: 0, closed: 0 };
|
|
23968
|
+
all[pr.repo].active++;
|
|
23969
|
+
}
|
|
23970
|
+
for (const [repo, score] of Object.entries(state.repoScores || {})) {
|
|
23971
|
+
if (shouldExcludeRepo(repo))
|
|
23972
|
+
continue;
|
|
23973
|
+
if (!all[repo])
|
|
23974
|
+
all[repo] = { active: 0, merged: 0, closed: 0 };
|
|
23975
|
+
all[repo].merged = score.mergedPRCount;
|
|
23976
|
+
all[repo].closed = score.closedWithoutMergeCount;
|
|
23977
|
+
}
|
|
23978
|
+
return all;
|
|
23979
|
+
})()
|
|
23980
|
+
).sort((a, b) => {
|
|
23981
|
+
const totalA = a[1].merged + a[1].active + a[1].closed;
|
|
23982
|
+
const totalB = b[1].merged + b[1].active + b[1].closed;
|
|
23983
|
+
return totalB - totalA;
|
|
23984
|
+
});
|
|
23985
|
+
const displayRepos = allRepoEntries.slice(0, 10);
|
|
23986
|
+
const otherRepos = allRepoEntries.slice(10);
|
|
23987
|
+
const grandTotal = allRepoEntries.reduce((sum, [, d]) => sum + d.merged + d.active + d.closed, 0);
|
|
23988
|
+
if (otherRepos.length > 0) {
|
|
23989
|
+
const otherData = otherRepos.reduce((acc, [, d]) => ({
|
|
23990
|
+
active: acc.active + d.active,
|
|
23991
|
+
merged: acc.merged + d.merged,
|
|
23992
|
+
closed: acc.closed + d.closed
|
|
23993
|
+
}), { active: 0, merged: 0, closed: 0 });
|
|
23994
|
+
displayRepos.push(["Other", otherData]);
|
|
23995
|
+
}
|
|
23996
|
+
const repoLabels = displayRepos.map(([repo]) => repo === "Other" ? "Other" : repo.split("/")[1] || repo);
|
|
23997
|
+
const mergedData = displayRepos.map(([, d]) => d.merged);
|
|
23998
|
+
const activeData = displayRepos.map(([, d]) => d.active);
|
|
23999
|
+
const closedData = displayRepos.map(([, d]) => d.closed);
|
|
24000
|
+
return `
|
|
24001
|
+
new Chart(document.getElementById('reposChart'), {
|
|
24002
|
+
type: 'bar',
|
|
24003
|
+
data: {
|
|
24004
|
+
labels: ${JSON.stringify(repoLabels)},
|
|
24005
|
+
datasets: [
|
|
24006
|
+
{ label: 'Merged', data: ${JSON.stringify(mergedData)}, backgroundColor: '#a855f7', borderRadius: 3 },
|
|
24007
|
+
{ label: 'Active', data: ${JSON.stringify(activeData)}, backgroundColor: '#3fb950', borderRadius: 3 },
|
|
24008
|
+
{ label: 'Closed', data: ${JSON.stringify(closedData)}, backgroundColor: '#484f58', borderRadius: 3 }
|
|
24009
|
+
]
|
|
24010
|
+
},
|
|
24011
|
+
options: {
|
|
24012
|
+
responsive: true,
|
|
24013
|
+
maintainAspectRatio: false,
|
|
24014
|
+
scales: {
|
|
24015
|
+
x: { stacked: true, grid: { display: false }, ticks: { font: { size: 10 } } },
|
|
24016
|
+
y: { stacked: true, grid: { color: 'rgba(48, 54, 61, 0.3)' }, ticks: { stepSize: 1 } }
|
|
24017
|
+
},
|
|
24018
|
+
plugins: {
|
|
24019
|
+
legend: { position: 'bottom', labels: { padding: 16, usePointStyle: true, pointStyle: 'circle', font: { size: 11 } } },
|
|
24020
|
+
tooltip: {
|
|
24021
|
+
callbacks: {
|
|
24022
|
+
afterBody: function(context) {
|
|
24023
|
+
const idx = context[0].dataIndex;
|
|
24024
|
+
const total = ${JSON.stringify(mergedData)}[idx] + ${JSON.stringify(activeData)}[idx] + ${JSON.stringify(closedData)}[idx];
|
|
24025
|
+
const pct = ${grandTotal} > 0 ? ((total / ${grandTotal}) * 100).toFixed(1) : '0.0';
|
|
24026
|
+
return pct + '% of all PRs';
|
|
24027
|
+
}
|
|
24028
|
+
}
|
|
24029
|
+
}
|
|
24030
|
+
}
|
|
24031
|
+
}
|
|
24032
|
+
});`;
|
|
24033
|
+
})();
|
|
24034
|
+
const timelineChart = (() => {
|
|
24035
|
+
const now = /* @__PURE__ */ new Date();
|
|
24036
|
+
const allMonths = [];
|
|
24037
|
+
for (let offset = 5; offset >= 0; offset--) {
|
|
24038
|
+
const d = new Date(now.getFullYear(), now.getMonth() - offset, 1);
|
|
24039
|
+
allMonths.push(`${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`);
|
|
24040
|
+
}
|
|
24041
|
+
return `
|
|
24042
|
+
const timelineMonths = ${JSON.stringify(allMonths)};
|
|
24043
|
+
const openedData = ${JSON.stringify(monthlyOpened)};
|
|
24044
|
+
const mergedData = ${JSON.stringify(monthlyMerged)};
|
|
24045
|
+
const closedData = ${JSON.stringify(monthlyClosed)};
|
|
24046
|
+
new Chart(document.getElementById('monthlyChart'), {
|
|
24047
|
+
type: 'bar',
|
|
24048
|
+
data: {
|
|
24049
|
+
labels: timelineMonths,
|
|
24050
|
+
datasets: [
|
|
24051
|
+
{
|
|
24052
|
+
label: 'Opened',
|
|
24053
|
+
data: timelineMonths.map(m => openedData[m] || 0),
|
|
24054
|
+
backgroundColor: '#58a6ff',
|
|
24055
|
+
borderRadius: 3
|
|
24056
|
+
},
|
|
24057
|
+
{
|
|
24058
|
+
label: 'Merged',
|
|
24059
|
+
data: timelineMonths.map(m => mergedData[m] || 0),
|
|
24060
|
+
backgroundColor: '#a855f7',
|
|
24061
|
+
borderRadius: 3
|
|
24062
|
+
},
|
|
24063
|
+
{
|
|
24064
|
+
label: 'Closed',
|
|
24065
|
+
data: timelineMonths.map(m => closedData[m] || 0),
|
|
24066
|
+
backgroundColor: '#484f58',
|
|
24067
|
+
borderRadius: 3
|
|
24068
|
+
}
|
|
24069
|
+
]
|
|
24070
|
+
},
|
|
24071
|
+
options: {
|
|
24072
|
+
responsive: true,
|
|
24073
|
+
maintainAspectRatio: false,
|
|
24074
|
+
scales: {
|
|
24075
|
+
x: { grid: { display: false } },
|
|
24076
|
+
y: { grid: { color: 'rgba(48, 54, 61, 0.3)' }, beginAtZero: true, ticks: { stepSize: 1 } }
|
|
24077
|
+
},
|
|
24078
|
+
plugins: {
|
|
24079
|
+
legend: { position: 'bottom', labels: { padding: 16, usePointStyle: true, pointStyle: 'circle', font: { size: 11 } } }
|
|
24080
|
+
},
|
|
24081
|
+
interaction: { intersect: false, mode: 'index' }
|
|
24082
|
+
}
|
|
24083
|
+
});`;
|
|
24084
|
+
})();
|
|
24085
|
+
return THEME_AND_FILTER_SCRIPT + statusChart + "\n" + repoChart + "\n" + timelineChart;
|
|
24086
|
+
}
|
|
24087
|
+
var THEME_AND_FILTER_SCRIPT;
|
|
24088
|
+
var init_dashboard_scripts = __esm({
|
|
24089
|
+
"../core/dist/commands/dashboard-scripts.js"() {
|
|
24090
|
+
"use strict";
|
|
24091
|
+
THEME_AND_FILTER_SCRIPT = `
|
|
24092
|
+
// === Theme Toggle ===
|
|
24093
|
+
(function() {
|
|
24094
|
+
var html = document.documentElement;
|
|
24095
|
+
var toggle = document.getElementById('themeToggle');
|
|
24096
|
+
var sunIcon = document.getElementById('themeIconSun');
|
|
24097
|
+
var moonIcon = document.getElementById('themeIconMoon');
|
|
24098
|
+
var label = document.getElementById('themeLabel');
|
|
24099
|
+
|
|
24100
|
+
function getEffectiveTheme() {
|
|
24101
|
+
try {
|
|
24102
|
+
var stored = localStorage.getItem('oss-dashboard-theme');
|
|
24103
|
+
if (stored === 'light' || stored === 'dark') return stored;
|
|
24104
|
+
} catch (e) { /* localStorage unavailable (private browsing) */ }
|
|
24105
|
+
return window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
|
|
24106
|
+
}
|
|
24107
|
+
|
|
24108
|
+
function applyTheme(theme) {
|
|
24109
|
+
html.setAttribute('data-theme', theme);
|
|
24110
|
+
if (theme === 'light') {
|
|
24111
|
+
sunIcon.style.display = 'none';
|
|
24112
|
+
moonIcon.style.display = 'block';
|
|
24113
|
+
label.textContent = 'Dark';
|
|
24114
|
+
} else {
|
|
24115
|
+
sunIcon.style.display = 'block';
|
|
24116
|
+
moonIcon.style.display = 'none';
|
|
24117
|
+
label.textContent = 'Light';
|
|
24118
|
+
}
|
|
24119
|
+
}
|
|
24120
|
+
|
|
24121
|
+
applyTheme(getEffectiveTheme());
|
|
24122
|
+
|
|
24123
|
+
toggle.addEventListener('click', function() {
|
|
24124
|
+
var current = html.getAttribute('data-theme');
|
|
24125
|
+
var next = current === 'dark' ? 'light' : 'dark';
|
|
24126
|
+
try { localStorage.setItem('oss-dashboard-theme', next); } catch (e) { /* private browsing */ }
|
|
24127
|
+
applyTheme(next);
|
|
24128
|
+
});
|
|
24129
|
+
})();
|
|
24130
|
+
|
|
24131
|
+
// === Filtering & Search ===
|
|
24132
|
+
(function() {
|
|
24133
|
+
var searchInput = document.getElementById('searchInput');
|
|
24134
|
+
var statusFilter = document.getElementById('statusFilter');
|
|
24135
|
+
var repoFilter = document.getElementById('repoFilter');
|
|
24136
|
+
var filterCount = document.getElementById('filterCount');
|
|
24137
|
+
|
|
24138
|
+
function applyFilters() {
|
|
24139
|
+
var query = searchInput.value.toLowerCase().trim();
|
|
24140
|
+
var status = statusFilter.value;
|
|
24141
|
+
var repo = repoFilter.value;
|
|
24142
|
+
var allItems = document.querySelectorAll('.health-item[data-status], .pr-item[data-status]');
|
|
24143
|
+
var visible = 0;
|
|
24144
|
+
var total = allItems.length;
|
|
24145
|
+
|
|
24146
|
+
allItems.forEach(function(item) {
|
|
24147
|
+
var itemStatus = item.getAttribute('data-status') || '';
|
|
24148
|
+
var itemRepo = item.getAttribute('data-repo') || '';
|
|
24149
|
+
var itemTitle = item.getAttribute('data-title') || '';
|
|
24150
|
+
|
|
24151
|
+
var matchesStatus = (status === 'all') || (itemStatus === status);
|
|
24152
|
+
var matchesRepo = (repo === 'all') || (itemRepo === repo);
|
|
24153
|
+
var matchesSearch = !query || itemTitle.indexOf(query) !== -1;
|
|
24154
|
+
|
|
24155
|
+
if (matchesStatus && matchesRepo && matchesSearch) {
|
|
24156
|
+
item.setAttribute('data-hidden', 'false');
|
|
24157
|
+
visible++;
|
|
24158
|
+
} else {
|
|
24159
|
+
item.setAttribute('data-hidden', 'true');
|
|
24160
|
+
}
|
|
24161
|
+
});
|
|
24162
|
+
|
|
24163
|
+
// Show/hide parent sections if all children are hidden
|
|
24164
|
+
var sections = document.querySelectorAll('.health-section, .pr-list-section');
|
|
24165
|
+
sections.forEach(function(section) {
|
|
24166
|
+
var items = section.querySelectorAll('.health-item[data-status], .pr-item[data-status]');
|
|
24167
|
+
if (items.length === 0) return; // sections without filterable items (e.g. empty state)
|
|
24168
|
+
var anyVisible = false;
|
|
24169
|
+
items.forEach(function(item) {
|
|
24170
|
+
if (item.getAttribute('data-hidden') !== 'true') anyVisible = true;
|
|
24171
|
+
});
|
|
24172
|
+
section.style.display = anyVisible ? '' : 'none';
|
|
24173
|
+
});
|
|
24174
|
+
|
|
24175
|
+
var isFiltering = (status !== 'all' || repo !== 'all' || query.length > 0);
|
|
24176
|
+
filterCount.textContent = isFiltering ? (visible + ' of ' + total + ' items') : '';
|
|
24177
|
+
}
|
|
24178
|
+
|
|
24179
|
+
searchInput.addEventListener('input', applyFilters);
|
|
24180
|
+
statusFilter.addEventListener('change', applyFilters);
|
|
24181
|
+
repoFilter.addEventListener('change', applyFilters);
|
|
24182
|
+
})();
|
|
24183
|
+
`;
|
|
24184
|
+
}
|
|
24185
|
+
});
|
|
24186
|
+
|
|
24187
|
+
// ../core/dist/commands/dashboard-templates.js
|
|
24188
|
+
function generateDashboardHtml(stats, monthlyMerged, monthlyClosed, monthlyOpened, digest, state, issueResponses = []) {
|
|
24189
|
+
const approachingDormantDays = state.config?.approachingDormantDays ?? 25;
|
|
24190
|
+
const shelvedPRs = digest.shelvedPRs || [];
|
|
24191
|
+
const autoUnshelvedPRs = digest.autoUnshelvedPRs || [];
|
|
24192
|
+
const recentlyMerged = digest.recentlyMergedPRs || [];
|
|
24193
|
+
const shelvedUrls = new Set(shelvedPRs.map((pr) => pr.url));
|
|
24194
|
+
const activePRList = (digest.openPRs || []).filter((pr) => !shelvedUrls.has(pr.url));
|
|
24195
|
+
const actionRequired = [
|
|
24196
|
+
...digest.prsNeedingResponse || [],
|
|
24197
|
+
...digest.needsChangesPRs || [],
|
|
24198
|
+
...digest.ciFailingPRs || [],
|
|
24199
|
+
...digest.mergeConflictPRs || [],
|
|
24200
|
+
...digest.incompleteChecklistPRs || [],
|
|
24201
|
+
...digest.missingRequiredFilesPRs || [],
|
|
24202
|
+
...digest.needsRebasePRs || []
|
|
24203
|
+
];
|
|
24204
|
+
const waitingOnOthers = [
|
|
24205
|
+
...digest.changesAddressedPRs || [],
|
|
24206
|
+
...digest.waitingOnMaintainerPRs || [],
|
|
24207
|
+
...digest.ciBlockedPRs || [],
|
|
24208
|
+
...digest.ciNotRunningPRs || []
|
|
24209
|
+
];
|
|
24210
|
+
return `<!DOCTYPE html>
|
|
24211
|
+
<html lang="en">
|
|
24212
|
+
<head>
|
|
24213
|
+
<meta charset="UTF-8">
|
|
24214
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
24215
|
+
<title>OSS Autopilot - Mission Control</title>
|
|
24216
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
24217
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
24218
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
24219
|
+
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
24220
|
+
<style>${DASHBOARD_CSS}
|
|
23941
24221
|
</style>
|
|
23942
24222
|
</head>
|
|
23943
24223
|
<body>
|
|
@@ -24069,13 +24349,13 @@ function generateDashboardHtml(stats, monthlyMerged, monthlyClosed, monthlyOpene
|
|
|
24069
24349
|
<span class="health-badge">${actionRequired.length} issue${actionRequired.length !== 1 ? "s" : ""}</span>
|
|
24070
24350
|
</div>
|
|
24071
24351
|
<div class="health-items">
|
|
24072
|
-
${renderHealthItems(digest.prsNeedingResponse || [], "needs-response",
|
|
24073
|
-
${renderHealthItems(digest.needsChangesPRs || [], "needs-changes",
|
|
24074
|
-
${renderHealthItems(digest.ciFailingPRs || [], "ci-failing",
|
|
24075
|
-
${renderHealthItems(digest.mergeConflictPRs || [], "conflict",
|
|
24076
|
-
${renderHealthItems(digest.incompleteChecklistPRs || [], "incomplete-checklist",
|
|
24077
|
-
${renderHealthItems(digest.missingRequiredFilesPRs || [], "missing-files",
|
|
24078
|
-
${renderHealthItems(digest.needsRebasePRs || [], "needs-rebase",
|
|
24352
|
+
${renderHealthItems(digest.prsNeedingResponse || [], "needs-response", SVG_ICONS.comment, "Needs Response", (pr) => pr.lastMaintainerComment ? `@${escapeHtml(pr.lastMaintainerComment.author)}: ${truncateTitle(pr.lastMaintainerComment.body, 40)}` : truncateTitle(pr.title))}
|
|
24353
|
+
${renderHealthItems(digest.needsChangesPRs || [], "needs-changes", SVG_ICONS.edit, "Needs Changes", titleMeta)}
|
|
24354
|
+
${renderHealthItems(digest.ciFailingPRs || [], "ci-failing", SVG_ICONS.xCircle, "CI Failing", titleMeta)}
|
|
24355
|
+
${renderHealthItems(digest.mergeConflictPRs || [], "conflict", SVG_ICONS.conflict, "Merge Conflict", titleMeta)}
|
|
24356
|
+
${renderHealthItems(digest.incompleteChecklistPRs || [], "incomplete-checklist", SVG_ICONS.checklist, (pr) => `Incomplete Checklist${pr.checklistStats ? ` (${pr.checklistStats.checked}/${pr.checklistStats.total})` : ""}`, titleMeta)}
|
|
24357
|
+
${renderHealthItems(digest.missingRequiredFilesPRs || [], "missing-files", SVG_ICONS.file, "Missing Required Files", (pr) => pr.missingRequiredFiles ? escapeHtml(pr.missingRequiredFiles.join(", ")) : truncateTitle(pr.title))}
|
|
24358
|
+
${renderHealthItems(digest.needsRebasePRs || [], "needs-rebase", SVG_ICONS.refresh, (pr) => `Needs Rebase${pr.commitsBehindUpstream ? ` (${pr.commitsBehindUpstream} behind)` : ""}`, titleMeta)}
|
|
24079
24359
|
</div>
|
|
24080
24360
|
</section>
|
|
24081
24361
|
` : ""}
|
|
@@ -24091,10 +24371,10 @@ function generateDashboardHtml(stats, monthlyMerged, monthlyClosed, monthlyOpene
|
|
|
24091
24371
|
<span class="health-badge" style="background: var(--accent-info-dim); color: var(--accent-info);">${waitingOnOthers.length} PR${waitingOnOthers.length !== 1 ? "s" : ""}</span>
|
|
24092
24372
|
</div>
|
|
24093
24373
|
<div class="health-items">
|
|
24094
|
-
${renderHealthItems(digest.changesAddressedPRs || [], "changes-addressed",
|
|
24095
|
-
${renderHealthItems(digest.waitingOnMaintainerPRs || [], "waiting-maintainer",
|
|
24096
|
-
${renderHealthItems(digest.ciBlockedPRs || [], "ci-blocked",
|
|
24097
|
-
${renderHealthItems(digest.ciNotRunningPRs || [], "ci-not-running",
|
|
24374
|
+
${renderHealthItems(digest.changesAddressedPRs || [], "changes-addressed", SVG_ICONS.checkCircle, "Changes Addressed", (pr) => `Awaiting re-review${pr.lastMaintainerComment ? ` from @${escapeHtml(pr.lastMaintainerComment.author)}` : ""}`)}
|
|
24375
|
+
${renderHealthItems(digest.waitingOnMaintainerPRs || [], "waiting-maintainer", SVG_ICONS.clock, "Waiting on Maintainer", titleMeta)}
|
|
24376
|
+
${renderHealthItems(digest.ciBlockedPRs || [], "ci-blocked", SVG_ICONS.lock, "CI Blocked", titleMeta)}
|
|
24377
|
+
${renderHealthItems(digest.ciNotRunningPRs || [], "ci-not-running", SVG_ICONS.infoCircle, "CI Not Running", titleMeta)}
|
|
24098
24378
|
</div>
|
|
24099
24379
|
</section>
|
|
24100
24380
|
` : ""}
|
|
@@ -24118,7 +24398,7 @@ function generateDashboardHtml(stats, monthlyMerged, monthlyClosed, monthlyOpene
|
|
|
24118
24398
|
<section class="health-section" style="animation-delay: 0.15s;">
|
|
24119
24399
|
<div class="health-header">
|
|
24120
24400
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--accent-merged)" stroke-width="2">
|
|
24121
|
-
${
|
|
24401
|
+
${SVG_ICONS.gitMerge}
|
|
24122
24402
|
</svg>
|
|
24123
24403
|
<h2>Recently Merged</h2>
|
|
24124
24404
|
<span class="health-badge" style="background: var(--accent-merged-dim); color: var(--accent-merged);">${recentlyMerged.length} merged</span>
|
|
@@ -24128,7 +24408,7 @@ function generateDashboardHtml(stats, monthlyMerged, monthlyClosed, monthlyOpene
|
|
|
24128
24408
|
<div class="health-item" style="border-left-color: var(--accent-merged);" data-status="merged" data-repo="${escapeHtml(pr.repo)}" data-title="${escapeHtml(pr.title.toLowerCase())}">
|
|
24129
24409
|
<div class="health-icon" style="background: var(--accent-merged-dim); color: var(--accent-merged);">
|
|
24130
24410
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
24131
|
-
${
|
|
24411
|
+
${SVG_ICONS.gitMerge}
|
|
24132
24412
|
</svg>
|
|
24133
24413
|
</div>
|
|
24134
24414
|
<div class="health-content">
|
|
@@ -24176,13 +24456,13 @@ function generateDashboardHtml(stats, monthlyMerged, monthlyClosed, monthlyOpene
|
|
|
24176
24456
|
<section class="health-section" style="animation-delay: 0.25s;">
|
|
24177
24457
|
<div class="health-header">
|
|
24178
24458
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--accent-info)" stroke-width="2">
|
|
24179
|
-
${
|
|
24459
|
+
${SVG_ICONS.bell}
|
|
24180
24460
|
</svg>
|
|
24181
24461
|
<h2>Auto-Unshelved</h2>
|
|
24182
24462
|
<span class="health-badge" style="background: var(--accent-info-dim); color: var(--accent-info);">${autoUnshelvedPRs.length} unshelved</span>
|
|
24183
24463
|
</div>
|
|
24184
24464
|
<div class="health-items">
|
|
24185
|
-
${renderHealthItems(autoUnshelvedPRs, "auto-unshelved",
|
|
24465
|
+
${renderHealthItems(autoUnshelvedPRs, "auto-unshelved", SVG_ICONS.bell, (pr) => "Auto-Unshelved (" + pr.status.replace(/_/g, " ") + ")", titleMeta)}
|
|
24186
24466
|
</div>
|
|
24187
24467
|
</section>
|
|
24188
24468
|
` : ""}
|
|
@@ -24191,7 +24471,7 @@ function generateDashboardHtml(stats, monthlyMerged, monthlyClosed, monthlyOpene
|
|
|
24191
24471
|
<section class="health-section" style="animation-delay: 0.3s;">
|
|
24192
24472
|
<div class="health-header">
|
|
24193
24473
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--accent-info)" stroke-width="2">
|
|
24194
|
-
${
|
|
24474
|
+
${SVG_ICONS.comment}
|
|
24195
24475
|
</svg>
|
|
24196
24476
|
<h2>Issue Conversations</h2>
|
|
24197
24477
|
<span class="health-badge" style="background: var(--accent-info-dim); color: var(--accent-info);">${issueResponses.length} repl${issueResponses.length !== 1 ? "ies" : "y"}</span>
|
|
@@ -24201,7 +24481,7 @@ function generateDashboardHtml(stats, monthlyMerged, monthlyClosed, monthlyOpene
|
|
|
24201
24481
|
<div class="health-item changes-addressed">
|
|
24202
24482
|
<div class="health-icon" style="background: var(--accent-info-dim); color: var(--accent-info);">
|
|
24203
24483
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
24204
|
-
${
|
|
24484
|
+
${SVG_ICONS.comment}
|
|
24205
24485
|
</svg>
|
|
24206
24486
|
</div>
|
|
24207
24487
|
<div class="health-content">
|
|
@@ -24331,7 +24611,7 @@ function generateDashboardHtml(stats, monthlyMerged, monthlyClosed, monthlyOpene
|
|
|
24331
24611
|
<div class="pr-item" data-status="shelved" data-repo="${escapeHtml(pr.repo)}" data-title="${escapeHtml(pr.title.toLowerCase())}">
|
|
24332
24612
|
<div class="pr-status-indicator" style="background: rgba(110, 118, 129, 0.1); color: var(--text-muted);">
|
|
24333
24613
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
24334
|
-
${
|
|
24614
|
+
${SVG_ICONS.box}
|
|
24335
24615
|
</svg>
|
|
24336
24616
|
</div>
|
|
24337
24617
|
<div class="pr-content">
|
|
@@ -24358,274 +24638,7 @@ function generateDashboardHtml(stats, monthlyMerged, monthlyClosed, monthlyOpene
|
|
|
24358
24638
|
</div>
|
|
24359
24639
|
|
|
24360
24640
|
<script>
|
|
24361
|
-
|
|
24362
|
-
(function() {
|
|
24363
|
-
var html = document.documentElement;
|
|
24364
|
-
var toggle = document.getElementById('themeToggle');
|
|
24365
|
-
var sunIcon = document.getElementById('themeIconSun');
|
|
24366
|
-
var moonIcon = document.getElementById('themeIconMoon');
|
|
24367
|
-
var label = document.getElementById('themeLabel');
|
|
24368
|
-
|
|
24369
|
-
function getEffectiveTheme() {
|
|
24370
|
-
try {
|
|
24371
|
-
var stored = localStorage.getItem('oss-dashboard-theme');
|
|
24372
|
-
if (stored === 'light' || stored === 'dark') return stored;
|
|
24373
|
-
} catch (e) { /* localStorage unavailable (private browsing) */ }
|
|
24374
|
-
return window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
|
|
24375
|
-
}
|
|
24376
|
-
|
|
24377
|
-
function applyTheme(theme) {
|
|
24378
|
-
html.setAttribute('data-theme', theme);
|
|
24379
|
-
if (theme === 'light') {
|
|
24380
|
-
sunIcon.style.display = 'none';
|
|
24381
|
-
moonIcon.style.display = 'block';
|
|
24382
|
-
label.textContent = 'Dark';
|
|
24383
|
-
} else {
|
|
24384
|
-
sunIcon.style.display = 'block';
|
|
24385
|
-
moonIcon.style.display = 'none';
|
|
24386
|
-
label.textContent = 'Light';
|
|
24387
|
-
}
|
|
24388
|
-
}
|
|
24389
|
-
|
|
24390
|
-
applyTheme(getEffectiveTheme());
|
|
24391
|
-
|
|
24392
|
-
toggle.addEventListener('click', function() {
|
|
24393
|
-
var current = html.getAttribute('data-theme');
|
|
24394
|
-
var next = current === 'dark' ? 'light' : 'dark';
|
|
24395
|
-
try { localStorage.setItem('oss-dashboard-theme', next); } catch (e) { /* private browsing */ }
|
|
24396
|
-
applyTheme(next);
|
|
24397
|
-
});
|
|
24398
|
-
})();
|
|
24399
|
-
|
|
24400
|
-
// === Filtering & Search ===
|
|
24401
|
-
(function() {
|
|
24402
|
-
var searchInput = document.getElementById('searchInput');
|
|
24403
|
-
var statusFilter = document.getElementById('statusFilter');
|
|
24404
|
-
var repoFilter = document.getElementById('repoFilter');
|
|
24405
|
-
var filterCount = document.getElementById('filterCount');
|
|
24406
|
-
|
|
24407
|
-
function applyFilters() {
|
|
24408
|
-
var query = searchInput.value.toLowerCase().trim();
|
|
24409
|
-
var status = statusFilter.value;
|
|
24410
|
-
var repo = repoFilter.value;
|
|
24411
|
-
var allItems = document.querySelectorAll('.health-item[data-status], .pr-item[data-status]');
|
|
24412
|
-
var visible = 0;
|
|
24413
|
-
var total = allItems.length;
|
|
24414
|
-
|
|
24415
|
-
allItems.forEach(function(item) {
|
|
24416
|
-
var itemStatus = item.getAttribute('data-status') || '';
|
|
24417
|
-
var itemRepo = item.getAttribute('data-repo') || '';
|
|
24418
|
-
var itemTitle = item.getAttribute('data-title') || '';
|
|
24419
|
-
|
|
24420
|
-
var matchesStatus = (status === 'all') || (itemStatus === status);
|
|
24421
|
-
var matchesRepo = (repo === 'all') || (itemRepo === repo);
|
|
24422
|
-
var matchesSearch = !query || itemTitle.indexOf(query) !== -1;
|
|
24423
|
-
|
|
24424
|
-
if (matchesStatus && matchesRepo && matchesSearch) {
|
|
24425
|
-
item.setAttribute('data-hidden', 'false');
|
|
24426
|
-
visible++;
|
|
24427
|
-
} else {
|
|
24428
|
-
item.setAttribute('data-hidden', 'true');
|
|
24429
|
-
}
|
|
24430
|
-
});
|
|
24431
|
-
|
|
24432
|
-
// Show/hide parent sections if all children are hidden
|
|
24433
|
-
var sections = document.querySelectorAll('.health-section, .pr-list-section');
|
|
24434
|
-
sections.forEach(function(section) {
|
|
24435
|
-
var items = section.querySelectorAll('.health-item[data-status], .pr-item[data-status]');
|
|
24436
|
-
if (items.length === 0) return; // sections without filterable items (e.g. empty state)
|
|
24437
|
-
var anyVisible = false;
|
|
24438
|
-
items.forEach(function(item) {
|
|
24439
|
-
if (item.getAttribute('data-hidden') !== 'true') anyVisible = true;
|
|
24440
|
-
});
|
|
24441
|
-
section.style.display = anyVisible ? '' : 'none';
|
|
24442
|
-
});
|
|
24443
|
-
|
|
24444
|
-
var isFiltering = (status !== 'all' || repo !== 'all' || query.length > 0);
|
|
24445
|
-
filterCount.textContent = isFiltering ? (visible + ' of ' + total + ' items') : '';
|
|
24446
|
-
}
|
|
24447
|
-
|
|
24448
|
-
searchInput.addEventListener('input', applyFilters);
|
|
24449
|
-
statusFilter.addEventListener('change', applyFilters);
|
|
24450
|
-
repoFilter.addEventListener('change', applyFilters);
|
|
24451
|
-
})();
|
|
24452
|
-
|
|
24453
|
-
// === Chart.js Configuration ===
|
|
24454
|
-
Chart.defaults.color = '#6e7681';
|
|
24455
|
-
Chart.defaults.borderColor = 'rgba(48, 54, 61, 0.4)';
|
|
24456
|
-
Chart.defaults.font.family = "'Geist', sans-serif";
|
|
24457
|
-
Chart.defaults.font.size = 11;
|
|
24458
|
-
|
|
24459
|
-
// === Status Doughnut ===
|
|
24460
|
-
new Chart(document.getElementById('statusChart'), {
|
|
24461
|
-
type: 'doughnut',
|
|
24462
|
-
data: {
|
|
24463
|
-
labels: ['Active', 'Shelved', 'Merged', 'Closed'],
|
|
24464
|
-
datasets: [{
|
|
24465
|
-
data: [${stats.activePRs}, ${stats.shelvedPRs}, ${stats.mergedPRs}, ${stats.closedPRs}],
|
|
24466
|
-
backgroundColor: ['#3fb950', '#6e7681', '#a855f7', '#484f58'],
|
|
24467
|
-
borderColor: 'rgba(8, 11, 16, 0.8)',
|
|
24468
|
-
borderWidth: 2,
|
|
24469
|
-
hoverOffset: 8
|
|
24470
|
-
}]
|
|
24471
|
-
},
|
|
24472
|
-
options: {
|
|
24473
|
-
responsive: true,
|
|
24474
|
-
maintainAspectRatio: false,
|
|
24475
|
-
cutout: '65%',
|
|
24476
|
-
plugins: {
|
|
24477
|
-
legend: {
|
|
24478
|
-
position: 'bottom',
|
|
24479
|
-
labels: { padding: 16, usePointStyle: true, pointStyle: 'circle', font: { size: 11 } }
|
|
24480
|
-
}
|
|
24481
|
-
}
|
|
24482
|
-
}
|
|
24483
|
-
});
|
|
24484
|
-
|
|
24485
|
-
// === Repository Breakdown (with "Other" bucket + percentage tooltips) ===
|
|
24486
|
-
${(() => {
|
|
24487
|
-
const { excludeRepos: exRepos = [], excludeOrgs: exOrgs, minStars } = state.config;
|
|
24488
|
-
const starThreshold = minStars ?? 50;
|
|
24489
|
-
const shouldExcludeRepo = (repo) => {
|
|
24490
|
-
const repoLower = repo.toLowerCase();
|
|
24491
|
-
if (exRepos.some((r) => r.toLowerCase() === repoLower))
|
|
24492
|
-
return true;
|
|
24493
|
-
if (exOrgs?.some((o) => o.toLowerCase() === repoLower.split("/")[0]))
|
|
24494
|
-
return true;
|
|
24495
|
-
const score = (state.repoScores || {})[repo];
|
|
24496
|
-
if (score?.stargazersCount !== void 0 && score.stargazersCount < starThreshold)
|
|
24497
|
-
return true;
|
|
24498
|
-
return false;
|
|
24499
|
-
};
|
|
24500
|
-
const allRepoEntries = Object.entries(
|
|
24501
|
-
// Rebuild from full prsByRepo to get all repos, not just top 10
|
|
24502
|
-
(() => {
|
|
24503
|
-
const all = {};
|
|
24504
|
-
for (const pr of digest.openPRs || []) {
|
|
24505
|
-
if (shouldExcludeRepo(pr.repo))
|
|
24506
|
-
continue;
|
|
24507
|
-
if (!all[pr.repo])
|
|
24508
|
-
all[pr.repo] = { active: 0, merged: 0, closed: 0 };
|
|
24509
|
-
all[pr.repo].active++;
|
|
24510
|
-
}
|
|
24511
|
-
for (const [repo, score] of Object.entries(state.repoScores || {})) {
|
|
24512
|
-
if (shouldExcludeRepo(repo))
|
|
24513
|
-
continue;
|
|
24514
|
-
if (!all[repo])
|
|
24515
|
-
all[repo] = { active: 0, merged: 0, closed: 0 };
|
|
24516
|
-
all[repo].merged = score.mergedPRCount;
|
|
24517
|
-
all[repo].closed = score.closedWithoutMergeCount;
|
|
24518
|
-
}
|
|
24519
|
-
return all;
|
|
24520
|
-
})()
|
|
24521
|
-
).sort((a, b) => {
|
|
24522
|
-
const totalA = a[1].merged + a[1].active + a[1].closed;
|
|
24523
|
-
const totalB = b[1].merged + b[1].active + b[1].closed;
|
|
24524
|
-
return totalB - totalA;
|
|
24525
|
-
});
|
|
24526
|
-
const displayRepos = allRepoEntries.slice(0, 10);
|
|
24527
|
-
const otherRepos = allRepoEntries.slice(10);
|
|
24528
|
-
const grandTotal = allRepoEntries.reduce((sum, [, d]) => sum + d.merged + d.active + d.closed, 0);
|
|
24529
|
-
if (otherRepos.length > 0) {
|
|
24530
|
-
const otherData = otherRepos.reduce((acc, [, d]) => ({
|
|
24531
|
-
active: acc.active + d.active,
|
|
24532
|
-
merged: acc.merged + d.merged,
|
|
24533
|
-
closed: acc.closed + d.closed
|
|
24534
|
-
}), { active: 0, merged: 0, closed: 0 });
|
|
24535
|
-
displayRepos.push(["Other", otherData]);
|
|
24536
|
-
}
|
|
24537
|
-
const repoLabels = displayRepos.map(([repo]) => repo === "Other" ? "Other" : repo.split("/")[1] || repo);
|
|
24538
|
-
const mergedData = displayRepos.map(([, d]) => d.merged);
|
|
24539
|
-
const activeData = displayRepos.map(([, d]) => d.active);
|
|
24540
|
-
const closedData = displayRepos.map(([, d]) => d.closed);
|
|
24541
|
-
return `
|
|
24542
|
-
new Chart(document.getElementById('reposChart'), {
|
|
24543
|
-
type: 'bar',
|
|
24544
|
-
data: {
|
|
24545
|
-
labels: ${JSON.stringify(repoLabels)},
|
|
24546
|
-
datasets: [
|
|
24547
|
-
{ label: 'Merged', data: ${JSON.stringify(mergedData)}, backgroundColor: '#a855f7', borderRadius: 3 },
|
|
24548
|
-
{ label: 'Active', data: ${JSON.stringify(activeData)}, backgroundColor: '#3fb950', borderRadius: 3 },
|
|
24549
|
-
{ label: 'Closed', data: ${JSON.stringify(closedData)}, backgroundColor: '#484f58', borderRadius: 3 }
|
|
24550
|
-
]
|
|
24551
|
-
},
|
|
24552
|
-
options: {
|
|
24553
|
-
responsive: true,
|
|
24554
|
-
maintainAspectRatio: false,
|
|
24555
|
-
scales: {
|
|
24556
|
-
x: { stacked: true, grid: { display: false }, ticks: { font: { size: 10 } } },
|
|
24557
|
-
y: { stacked: true, grid: { color: 'rgba(48, 54, 61, 0.3)' }, ticks: { stepSize: 1 } }
|
|
24558
|
-
},
|
|
24559
|
-
plugins: {
|
|
24560
|
-
legend: { position: 'bottom', labels: { padding: 16, usePointStyle: true, pointStyle: 'circle', font: { size: 11 } } },
|
|
24561
|
-
tooltip: {
|
|
24562
|
-
callbacks: {
|
|
24563
|
-
afterBody: function(context) {
|
|
24564
|
-
const idx = context[0].dataIndex;
|
|
24565
|
-
const total = ${JSON.stringify(mergedData)}[idx] + ${JSON.stringify(activeData)}[idx] + ${JSON.stringify(closedData)}[idx];
|
|
24566
|
-
const pct = ${grandTotal} > 0 ? ((total / ${grandTotal}) * 100).toFixed(1) : '0.0';
|
|
24567
|
-
return pct + '% of all PRs';
|
|
24568
|
-
}
|
|
24569
|
-
}
|
|
24570
|
-
}
|
|
24571
|
-
}
|
|
24572
|
-
}
|
|
24573
|
-
});`;
|
|
24574
|
-
})()}
|
|
24575
|
-
|
|
24576
|
-
// === Contribution Timeline (grouped bar: Opened/Merged/Closed) ===
|
|
24577
|
-
${(() => {
|
|
24578
|
-
const now = /* @__PURE__ */ new Date();
|
|
24579
|
-
const allMonths = [];
|
|
24580
|
-
for (let offset = 5; offset >= 0; offset--) {
|
|
24581
|
-
const d = new Date(now.getFullYear(), now.getMonth() - offset, 1);
|
|
24582
|
-
allMonths.push(`${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`);
|
|
24583
|
-
}
|
|
24584
|
-
return `
|
|
24585
|
-
const timelineMonths = ${JSON.stringify(allMonths)};
|
|
24586
|
-
const openedData = ${JSON.stringify(monthlyOpened)};
|
|
24587
|
-
const mergedData = ${JSON.stringify(monthlyMerged)};
|
|
24588
|
-
const closedData = ${JSON.stringify(monthlyClosed)};
|
|
24589
|
-
new Chart(document.getElementById('monthlyChart'), {
|
|
24590
|
-
type: 'bar',
|
|
24591
|
-
data: {
|
|
24592
|
-
labels: timelineMonths,
|
|
24593
|
-
datasets: [
|
|
24594
|
-
{
|
|
24595
|
-
label: 'Opened',
|
|
24596
|
-
data: timelineMonths.map(m => openedData[m] || 0),
|
|
24597
|
-
backgroundColor: '#58a6ff',
|
|
24598
|
-
borderRadius: 3
|
|
24599
|
-
},
|
|
24600
|
-
{
|
|
24601
|
-
label: 'Merged',
|
|
24602
|
-
data: timelineMonths.map(m => mergedData[m] || 0),
|
|
24603
|
-
backgroundColor: '#a855f7',
|
|
24604
|
-
borderRadius: 3
|
|
24605
|
-
},
|
|
24606
|
-
{
|
|
24607
|
-
label: 'Closed',
|
|
24608
|
-
data: timelineMonths.map(m => closedData[m] || 0),
|
|
24609
|
-
backgroundColor: '#484f58',
|
|
24610
|
-
borderRadius: 3
|
|
24611
|
-
}
|
|
24612
|
-
]
|
|
24613
|
-
},
|
|
24614
|
-
options: {
|
|
24615
|
-
responsive: true,
|
|
24616
|
-
maintainAspectRatio: false,
|
|
24617
|
-
scales: {
|
|
24618
|
-
x: { grid: { display: false } },
|
|
24619
|
-
y: { grid: { color: 'rgba(48, 54, 61, 0.3)' }, beginAtZero: true, ticks: { stepSize: 1 } }
|
|
24620
|
-
},
|
|
24621
|
-
plugins: {
|
|
24622
|
-
legend: { position: 'bottom', labels: { padding: 16, usePointStyle: true, pointStyle: 'circle', font: { size: 11 } } }
|
|
24623
|
-
},
|
|
24624
|
-
interaction: { intersect: false, mode: 'index' }
|
|
24625
|
-
}
|
|
24626
|
-
});`;
|
|
24627
|
-
})()}
|
|
24628
|
-
|
|
24641
|
+
${generateDashboardScripts(stats, monthlyMerged, monthlyClosed, monthlyOpened, digest, state)}
|
|
24629
24642
|
</script>
|
|
24630
24643
|
</body>
|
|
24631
24644
|
</html>`;
|
|
@@ -24633,6 +24646,11 @@ function generateDashboardHtml(stats, monthlyMerged, monthlyClosed, monthlyOpene
|
|
|
24633
24646
|
var init_dashboard_templates = __esm({
|
|
24634
24647
|
"../core/dist/commands/dashboard-templates.js"() {
|
|
24635
24648
|
"use strict";
|
|
24649
|
+
init_dashboard_formatters();
|
|
24650
|
+
init_dashboard_styles();
|
|
24651
|
+
init_dashboard_components();
|
|
24652
|
+
init_dashboard_scripts();
|
|
24653
|
+
init_dashboard_formatters();
|
|
24636
24654
|
}
|
|
24637
24655
|
});
|
|
24638
24656
|
|
|
@@ -26493,8 +26511,8 @@ function getErrorMap() {
|
|
|
26493
26511
|
|
|
26494
26512
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
|
|
26495
26513
|
var makeIssue = (params) => {
|
|
26496
|
-
const { data, path:
|
|
26497
|
-
const fullPath = [...
|
|
26514
|
+
const { data, path: path6, errorMaps, issueData } = params;
|
|
26515
|
+
const fullPath = [...path6, ...issueData.path || []];
|
|
26498
26516
|
const fullIssue = {
|
|
26499
26517
|
...issueData,
|
|
26500
26518
|
path: fullPath
|
|
@@ -26610,11 +26628,11 @@ var errorUtil;
|
|
|
26610
26628
|
|
|
26611
26629
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
|
|
26612
26630
|
var ParseInputLazyPath = class {
|
|
26613
|
-
constructor(parent, value,
|
|
26631
|
+
constructor(parent, value, path6, key) {
|
|
26614
26632
|
this._cachedPath = [];
|
|
26615
26633
|
this.parent = parent;
|
|
26616
26634
|
this.data = value;
|
|
26617
|
-
this._path =
|
|
26635
|
+
this._path = path6;
|
|
26618
26636
|
this._key = key;
|
|
26619
26637
|
}
|
|
26620
26638
|
get path() {
|
|
@@ -31741,25 +31759,25 @@ var Protocol = class {
|
|
|
31741
31759
|
});
|
|
31742
31760
|
}
|
|
31743
31761
|
_resetTimeout(messageId) {
|
|
31744
|
-
const
|
|
31745
|
-
if (!
|
|
31762
|
+
const info2 = this._timeoutInfo.get(messageId);
|
|
31763
|
+
if (!info2)
|
|
31746
31764
|
return false;
|
|
31747
|
-
const totalElapsed = Date.now() -
|
|
31748
|
-
if (
|
|
31765
|
+
const totalElapsed = Date.now() - info2.startTime;
|
|
31766
|
+
if (info2.maxTotalTimeout && totalElapsed >= info2.maxTotalTimeout) {
|
|
31749
31767
|
this._timeoutInfo.delete(messageId);
|
|
31750
31768
|
throw McpError.fromError(ErrorCode.RequestTimeout, "Maximum total timeout exceeded", {
|
|
31751
|
-
maxTotalTimeout:
|
|
31769
|
+
maxTotalTimeout: info2.maxTotalTimeout,
|
|
31752
31770
|
totalElapsed
|
|
31753
31771
|
});
|
|
31754
31772
|
}
|
|
31755
|
-
clearTimeout(
|
|
31756
|
-
|
|
31773
|
+
clearTimeout(info2.timeoutId);
|
|
31774
|
+
info2.timeoutId = setTimeout(info2.onTimeout, info2.timeout);
|
|
31757
31775
|
return true;
|
|
31758
31776
|
}
|
|
31759
31777
|
_cleanupTimeout(messageId) {
|
|
31760
|
-
const
|
|
31761
|
-
if (
|
|
31762
|
-
clearTimeout(
|
|
31778
|
+
const info2 = this._timeoutInfo.get(messageId);
|
|
31779
|
+
if (info2) {
|
|
31780
|
+
clearTimeout(info2.timeoutId);
|
|
31763
31781
|
this._timeoutInfo.delete(messageId);
|
|
31764
31782
|
}
|
|
31765
31783
|
}
|
|
@@ -34434,24 +34452,28 @@ init_core3();
|
|
|
34434
34452
|
// ../core/dist/commands/validation.js
|
|
34435
34453
|
init_errors3();
|
|
34436
34454
|
var PR_URL_PATTERN = /^https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+$/;
|
|
34437
|
-
var
|
|
34455
|
+
var ISSUE_OR_PR_URL_PATTERN = /^https:\/\/github\.com\/[^/]+\/[^/]+\/(issues|pull)\/\d+$/;
|
|
34438
34456
|
var MAX_URL_LENGTH = 2048;
|
|
34439
34457
|
var MAX_MESSAGE_LENGTH = 1e3;
|
|
34440
34458
|
function validateGitHubUrl(url, pattern, entityType) {
|
|
34441
34459
|
if (pattern.test(url))
|
|
34442
34460
|
return;
|
|
34443
|
-
const
|
|
34444
|
-
|
|
34461
|
+
const examples = {
|
|
34462
|
+
PR: "https://github.com/owner/repo/pull/123",
|
|
34463
|
+
issue: "https://github.com/owner/repo/issues/123",
|
|
34464
|
+
"issue or PR": "https://github.com/owner/repo/issues/123 or https://github.com/owner/repo/pull/123"
|
|
34465
|
+
};
|
|
34466
|
+
throw new ValidationError(`Invalid ${entityType} URL: ${url}. Expected format: ${examples[entityType]}`);
|
|
34445
34467
|
}
|
|
34446
34468
|
function validateUrl(url) {
|
|
34447
34469
|
if (url.length > MAX_URL_LENGTH) {
|
|
34448
|
-
throw new
|
|
34470
|
+
throw new ValidationError(`URL exceeds maximum length of ${MAX_URL_LENGTH} characters`);
|
|
34449
34471
|
}
|
|
34450
34472
|
return url;
|
|
34451
34473
|
}
|
|
34452
34474
|
function validateMessage(message) {
|
|
34453
34475
|
if (message.length > MAX_MESSAGE_LENGTH) {
|
|
34454
|
-
throw new
|
|
34476
|
+
throw new ValidationError(`Message exceeds maximum length of ${MAX_MESSAGE_LENGTH} characters`);
|
|
34455
34477
|
}
|
|
34456
34478
|
return message;
|
|
34457
34479
|
}
|
|
@@ -34564,27 +34586,29 @@ async function runComments(options) {
|
|
|
34564
34586
|
}
|
|
34565
34587
|
const { owner, repo, number: pull_number } = parsed;
|
|
34566
34588
|
const { data: pr } = await octokit.pulls.get({ owner, repo, pull_number });
|
|
34567
|
-
const reviewComments = await
|
|
34568
|
-
|
|
34569
|
-
|
|
34570
|
-
|
|
34571
|
-
|
|
34572
|
-
|
|
34573
|
-
|
|
34574
|
-
|
|
34575
|
-
|
|
34576
|
-
|
|
34577
|
-
|
|
34578
|
-
|
|
34579
|
-
|
|
34580
|
-
|
|
34581
|
-
|
|
34582
|
-
|
|
34583
|
-
|
|
34584
|
-
|
|
34585
|
-
|
|
34586
|
-
|
|
34587
|
-
|
|
34589
|
+
const [reviewComments, issueComments, reviews] = await Promise.all([
|
|
34590
|
+
paginateAll((page) => octokit.pulls.listReviewComments({
|
|
34591
|
+
owner,
|
|
34592
|
+
repo,
|
|
34593
|
+
pull_number,
|
|
34594
|
+
per_page: 100,
|
|
34595
|
+
page
|
|
34596
|
+
})),
|
|
34597
|
+
paginateAll((page) => octokit.issues.listComments({
|
|
34598
|
+
owner,
|
|
34599
|
+
repo,
|
|
34600
|
+
issue_number: pull_number,
|
|
34601
|
+
per_page: 100,
|
|
34602
|
+
page
|
|
34603
|
+
})),
|
|
34604
|
+
paginateAll((page) => octokit.pulls.listReviews({
|
|
34605
|
+
owner,
|
|
34606
|
+
repo,
|
|
34607
|
+
pull_number,
|
|
34608
|
+
per_page: 100,
|
|
34609
|
+
page
|
|
34610
|
+
}))
|
|
34611
|
+
]);
|
|
34588
34612
|
const username = stateManager2.getState().config.githubUsername;
|
|
34589
34613
|
const filterComment = (c) => {
|
|
34590
34614
|
if (!c.user)
|
|
@@ -34961,24 +34985,24 @@ async function runUnshelve(options) {
|
|
|
34961
34985
|
// ../core/dist/commands/dismiss.js
|
|
34962
34986
|
init_core3();
|
|
34963
34987
|
async function runDismiss(options) {
|
|
34964
|
-
validateUrl(options.
|
|
34965
|
-
validateGitHubUrl(options.
|
|
34988
|
+
validateUrl(options.url);
|
|
34989
|
+
validateGitHubUrl(options.url, ISSUE_OR_PR_URL_PATTERN, "issue or PR");
|
|
34966
34990
|
const stateManager2 = getStateManager();
|
|
34967
|
-
const added = stateManager2.dismissIssue(options.
|
|
34991
|
+
const added = stateManager2.dismissIssue(options.url, (/* @__PURE__ */ new Date()).toISOString());
|
|
34968
34992
|
if (added) {
|
|
34969
34993
|
stateManager2.save();
|
|
34970
34994
|
}
|
|
34971
|
-
return { dismissed: added, url: options.
|
|
34995
|
+
return { dismissed: added, url: options.url };
|
|
34972
34996
|
}
|
|
34973
34997
|
async function runUndismiss(options) {
|
|
34974
|
-
validateUrl(options.
|
|
34975
|
-
validateGitHubUrl(options.
|
|
34998
|
+
validateUrl(options.url);
|
|
34999
|
+
validateGitHubUrl(options.url, ISSUE_OR_PR_URL_PATTERN, "issue or PR");
|
|
34976
35000
|
const stateManager2 = getStateManager();
|
|
34977
|
-
const removed = stateManager2.undismissIssue(options.
|
|
35001
|
+
const removed = stateManager2.undismissIssue(options.url);
|
|
34978
35002
|
if (removed) {
|
|
34979
35003
|
stateManager2.save();
|
|
34980
35004
|
}
|
|
34981
|
-
return { undismissed: removed, url: options.
|
|
35005
|
+
return { undismissed: removed, url: options.url };
|
|
34982
35006
|
}
|
|
34983
35007
|
|
|
34984
35008
|
// ../core/dist/commands/snooze.js
|
|
@@ -35019,14 +35043,15 @@ async function runUnsnooze(options) {
|
|
|
35019
35043
|
|
|
35020
35044
|
// ../core/dist/commands/startup.js
|
|
35021
35045
|
var fs6 = __toESM(require("fs"), 1);
|
|
35022
|
-
var path5 = __toESM(require("path"), 1);
|
|
35023
35046
|
var import_child_process2 = require("child_process");
|
|
35024
35047
|
init_core3();
|
|
35048
|
+
init_errors3();
|
|
35025
35049
|
init_daily();
|
|
35026
35050
|
|
|
35027
35051
|
// ../core/dist/commands/dashboard.js
|
|
35028
35052
|
var fs5 = __toESM(require("fs"), 1);
|
|
35029
35053
|
init_core3();
|
|
35054
|
+
init_errors3();
|
|
35030
35055
|
init_json();
|
|
35031
35056
|
init_dashboard_data();
|
|
35032
35057
|
init_dashboard_templates();
|
|
@@ -35047,15 +35072,6 @@ function writeDashboardFromState() {
|
|
|
35047
35072
|
}
|
|
35048
35073
|
|
|
35049
35074
|
// ../core/dist/commands/startup.js
|
|
35050
|
-
function getVersion() {
|
|
35051
|
-
try {
|
|
35052
|
-
const pkgPath = path5.join(path5.dirname(process.argv[1]), "..", "package.json");
|
|
35053
|
-
return JSON.parse(fs6.readFileSync(pkgPath, "utf-8")).version;
|
|
35054
|
-
} catch (error2) {
|
|
35055
|
-
console.error("[STARTUP] Failed to detect CLI version:", error2 instanceof Error ? error2.message : error2);
|
|
35056
|
-
return "0.0.0";
|
|
35057
|
-
}
|
|
35058
|
-
}
|
|
35059
35075
|
function parseIssueListPathFromConfig(configContent) {
|
|
35060
35076
|
const match = configContent.match(/^---\n([\s\S]*?)\n---/);
|
|
35061
35077
|
if (!match)
|
|
@@ -35092,7 +35108,7 @@ function detectIssueList() {
|
|
|
35092
35108
|
source = "configured";
|
|
35093
35109
|
}
|
|
35094
35110
|
} catch (error2) {
|
|
35095
|
-
console.error("[STARTUP] Failed to read config:", error2
|
|
35111
|
+
console.error("[STARTUP] Failed to read config:", errorMessage(error2));
|
|
35096
35112
|
}
|
|
35097
35113
|
}
|
|
35098
35114
|
if (!issueListPath) {
|
|
@@ -35112,7 +35128,7 @@ function detectIssueList() {
|
|
|
35112
35128
|
const { availableCount, completedCount } = countIssueListItems(content);
|
|
35113
35129
|
return { path: issueListPath, source, availableCount, completedCount };
|
|
35114
35130
|
} catch (error2) {
|
|
35115
|
-
console.error(`[STARTUP] Failed to read issue list at ${issueListPath}:`, error2
|
|
35131
|
+
console.error(`[STARTUP] Failed to read issue list at ${issueListPath}:`, errorMessage(error2));
|
|
35116
35132
|
return { path: issueListPath, source, availableCount: 0, completedCount: 0 };
|
|
35117
35133
|
}
|
|
35118
35134
|
}
|
|
@@ -35127,7 +35143,7 @@ function openInBrowser(filePath) {
|
|
|
35127
35143
|
});
|
|
35128
35144
|
}
|
|
35129
35145
|
async function runStartup() {
|
|
35130
|
-
const version3 =
|
|
35146
|
+
const version3 = getCLIVersion();
|
|
35131
35147
|
const stateManager2 = getStateManager();
|
|
35132
35148
|
if (!stateManager2.isSetupComplete()) {
|
|
35133
35149
|
return { version: version3, setupComplete: false };
|
|
@@ -35150,7 +35166,7 @@ async function runStartup() {
|
|
|
35150
35166
|
dashboardOpened = true;
|
|
35151
35167
|
}
|
|
35152
35168
|
} catch (error2) {
|
|
35153
|
-
console.error("[STARTUP] Dashboard generation failed:", error2
|
|
35169
|
+
console.error("[STARTUP] Dashboard generation failed:", errorMessage(error2));
|
|
35154
35170
|
}
|
|
35155
35171
|
if (dashboardOpened) {
|
|
35156
35172
|
daily.briefSummary += " | Dashboard opened in browser";
|
|
@@ -35165,26 +35181,29 @@ async function runStartup() {
|
|
|
35165
35181
|
};
|
|
35166
35182
|
}
|
|
35167
35183
|
|
|
35184
|
+
// ../core/dist/commands/parse-list.js
|
|
35185
|
+
init_errors3();
|
|
35186
|
+
|
|
35168
35187
|
// ../core/dist/commands/check-integration.js
|
|
35169
35188
|
init_core3();
|
|
35189
|
+
init_errors3();
|
|
35170
35190
|
|
|
35171
35191
|
// ../core/dist/commands/local-repos.js
|
|
35172
|
-
var
|
|
35192
|
+
var path5 = __toESM(require("path"), 1);
|
|
35173
35193
|
var os2 = __toESM(require("os"), 1);
|
|
35174
35194
|
init_core3();
|
|
35195
|
+
init_errors3();
|
|
35175
35196
|
var DEFAULT_SCAN_PATHS = [
|
|
35176
|
-
|
|
35177
|
-
|
|
35178
|
-
|
|
35179
|
-
|
|
35180
|
-
|
|
35181
|
-
|
|
35197
|
+
path5.join(os2.homedir(), "Documents", "oss"),
|
|
35198
|
+
path5.join(os2.homedir(), "dev"),
|
|
35199
|
+
path5.join(os2.homedir(), "projects"),
|
|
35200
|
+
path5.join(os2.homedir(), "src"),
|
|
35201
|
+
path5.join(os2.homedir(), "code"),
|
|
35202
|
+
path5.join(os2.homedir(), "repos")
|
|
35182
35203
|
];
|
|
35183
35204
|
|
|
35184
35205
|
// src/tools.ts
|
|
35185
|
-
|
|
35186
|
-
return e instanceof Error ? e.message : String(e);
|
|
35187
|
-
}
|
|
35206
|
+
init_core3();
|
|
35188
35207
|
function ok(data) {
|
|
35189
35208
|
return {
|
|
35190
35209
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) ?? "null" }]
|
|
@@ -35397,9 +35416,9 @@ function registerTools(server) {
|
|
|
35397
35416
|
server.registerTool(
|
|
35398
35417
|
"dismiss",
|
|
35399
35418
|
{
|
|
35400
|
-
description: "Dismiss a GitHub issue so it no longer appears in
|
|
35419
|
+
description: "Dismiss a GitHub issue or PR so it no longer appears in notifications.",
|
|
35401
35420
|
inputSchema: {
|
|
35402
|
-
|
|
35421
|
+
url: external_exports.string().describe("Full GitHub issue or PR URL to dismiss")
|
|
35403
35422
|
},
|
|
35404
35423
|
annotations: { readOnlyHint: false, destructiveHint: false }
|
|
35405
35424
|
},
|
|
@@ -35408,9 +35427,9 @@ function registerTools(server) {
|
|
|
35408
35427
|
server.registerTool(
|
|
35409
35428
|
"undismiss",
|
|
35410
35429
|
{
|
|
35411
|
-
description: "Undismiss a previously dismissed issue
|
|
35430
|
+
description: "Undismiss a previously dismissed issue or PR, re-enabling notifications.",
|
|
35412
35431
|
inputSchema: {
|
|
35413
|
-
|
|
35432
|
+
url: external_exports.string().describe("Full GitHub issue or PR URL to undismiss")
|
|
35414
35433
|
},
|
|
35415
35434
|
annotations: { readOnlyHint: false, destructiveHint: false }
|
|
35416
35435
|
},
|
|
@@ -35444,6 +35463,7 @@ function registerTools(server) {
|
|
|
35444
35463
|
|
|
35445
35464
|
// src/resources.ts
|
|
35446
35465
|
init_core3();
|
|
35466
|
+
init_core3();
|
|
35447
35467
|
function resourceContent(uri, data) {
|
|
35448
35468
|
return {
|
|
35449
35469
|
contents: [{ uri: uri.href, mimeType: "application/json", text: JSON.stringify(data, null, 2) }]
|
|
@@ -35508,7 +35528,7 @@ function registerResources(server) {
|
|
|
35508
35528
|
const openPRs = getStateManager().getState().lastDigest?.openPRs ?? [];
|
|
35509
35529
|
return {
|
|
35510
35530
|
resources: openPRs.map((pr) => {
|
|
35511
|
-
const
|
|
35531
|
+
const { owner, repo } = splitRepo(pr.repo);
|
|
35512
35532
|
return {
|
|
35513
35533
|
uri: `oss://pr/${owner}/${repo}/${pr.number}`,
|
|
35514
35534
|
name: `${pr.repo}#${pr.number}`,
|
|
@@ -35549,6 +35569,7 @@ function registerResources(server) {
|
|
|
35549
35569
|
}
|
|
35550
35570
|
|
|
35551
35571
|
// src/prompts.ts
|
|
35572
|
+
init_core3();
|
|
35552
35573
|
function userMessage(text) {
|
|
35553
35574
|
return {
|
|
35554
35575
|
messages: [
|