@oss-autopilot/core 0.42.0 → 0.42.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1196,7 +1196,7 @@ var require_command = __commonJS({
1196
1196
  "../../node_modules/.pnpm/commander@14.0.3/node_modules/commander/lib/command.js"(exports2) {
1197
1197
  var EventEmitter = require("node:events").EventEmitter;
1198
1198
  var childProcess = require("node:child_process");
1199
- var path11 = require("node:path");
1199
+ var path10 = require("node:path");
1200
1200
  var fs10 = require("node:fs");
1201
1201
  var process2 = require("node:process");
1202
1202
  var { Argument: Argument2, humanReadableArgName } = require_argument();
@@ -2209,9 +2209,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
2209
2209
  let launchWithNode = false;
2210
2210
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
2211
2211
  function findFile(baseDir, baseName) {
2212
- const localBin = path11.resolve(baseDir, baseName);
2212
+ const localBin = path10.resolve(baseDir, baseName);
2213
2213
  if (fs10.existsSync(localBin)) return localBin;
2214
- if (sourceExt.includes(path11.extname(baseName))) return void 0;
2214
+ if (sourceExt.includes(path10.extname(baseName))) return void 0;
2215
2215
  const foundExt = sourceExt.find(
2216
2216
  (ext) => fs10.existsSync(`${localBin}${ext}`)
2217
2217
  );
@@ -2229,17 +2229,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
2229
2229
  } catch {
2230
2230
  resolvedScriptPath = this._scriptPath;
2231
2231
  }
2232
- executableDir = path11.resolve(
2233
- path11.dirname(resolvedScriptPath),
2232
+ executableDir = path10.resolve(
2233
+ path10.dirname(resolvedScriptPath),
2234
2234
  executableDir
2235
2235
  );
2236
2236
  }
2237
2237
  if (executableDir) {
2238
2238
  let localFile = findFile(executableDir, executableFile);
2239
2239
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
2240
- const legacyName = path11.basename(
2240
+ const legacyName = path10.basename(
2241
2241
  this._scriptPath,
2242
- path11.extname(this._scriptPath)
2242
+ path10.extname(this._scriptPath)
2243
2243
  );
2244
2244
  if (legacyName !== this._name) {
2245
2245
  localFile = findFile(
@@ -2250,7 +2250,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2250
2250
  }
2251
2251
  executableFile = localFile || executableFile;
2252
2252
  }
2253
- launchWithNode = sourceExt.includes(path11.extname(executableFile));
2253
+ launchWithNode = sourceExt.includes(path10.extname(executableFile));
2254
2254
  let proc;
2255
2255
  if (process2.platform !== "win32") {
2256
2256
  if (launchWithNode) {
@@ -3165,7 +3165,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
3165
3165
  * @return {Command}
3166
3166
  */
3167
3167
  nameFromFilename(filename) {
3168
- this._name = path11.basename(filename, path11.extname(filename));
3168
+ this._name = path10.basename(filename, path10.extname(filename));
3169
3169
  return this;
3170
3170
  }
3171
3171
  /**
@@ -3179,9 +3179,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3179
3179
  * @param {string} [path]
3180
3180
  * @return {(string|null|Command)}
3181
3181
  */
3182
- executableDir(path12) {
3183
- if (path12 === void 0) return this._executableDir;
3184
- this._executableDir = path12;
3182
+ executableDir(path11) {
3183
+ if (path11 === void 0) return this._executableDir;
3184
+ this._executableDir = path11;
3185
3185
  return this;
3186
3186
  }
3187
3187
  /**
@@ -3498,6 +3498,16 @@ var init_types = __esm({
3498
3498
  });
3499
3499
 
3500
3500
  // src/core/errors.ts
3501
+ function errorMessage(e) {
3502
+ return e instanceof Error ? e.message : String(e);
3503
+ }
3504
+ function getHttpStatusCode(error) {
3505
+ if (error && typeof error === "object" && "status" in error) {
3506
+ const status = error.status;
3507
+ return typeof status === "number" && Number.isFinite(status) ? status : void 0;
3508
+ }
3509
+ return void 0;
3510
+ }
3501
3511
  var OssAutopilotError, ConfigurationError, ValidationError;
3502
3512
  var init_errors = __esm({
3503
3513
  "src/core/errors.ts"() {
@@ -3640,6 +3650,17 @@ function splitRepo(repoFullName) {
3640
3650
  const [owner, repo] = repoFullName.split("/");
3641
3651
  return { owner, repo };
3642
3652
  }
3653
+ function isOwnRepo(owner, username) {
3654
+ return owner.toLowerCase() === username.toLowerCase();
3655
+ }
3656
+ function getCLIVersion() {
3657
+ try {
3658
+ const pkgPath = path.join(path.dirname(process.argv[1]), "..", "package.json");
3659
+ return JSON.parse(fs.readFileSync(pkgPath, "utf-8")).version;
3660
+ } catch {
3661
+ return "0.0.0";
3662
+ }
3663
+ }
3643
3664
  function formatRelativeTime(dateStr) {
3644
3665
  const date = new Date(dateStr);
3645
3666
  const diffMs = Date.now() - date.getTime();
@@ -3960,8 +3981,7 @@ var init_state = __esm({
3960
3981
  debug(MODULE2, "Migration complete!");
3961
3982
  return true;
3962
3983
  } catch (error) {
3963
- const errorMessage = error instanceof Error ? error.message : String(error);
3964
- warn(MODULE2, `Failed to migrate state: ${errorMessage}`);
3984
+ warn(MODULE2, `Failed to migrate state: ${errorMessage(error)}`);
3965
3985
  const newStatePath2 = getStatePath();
3966
3986
  if (fs2.existsSync(newStatePath2) && fs2.existsSync(LEGACY_STATE_FILE)) {
3967
3987
  try {
@@ -4113,11 +4133,11 @@ var init_state = __esm({
4113
4133
  try {
4114
4134
  fs2.unlinkSync(path2.join(backupDir, file));
4115
4135
  } catch (error) {
4116
- warn(MODULE2, `Could not delete old backup ${file}:`, error instanceof Error ? error.message : error);
4136
+ warn(MODULE2, `Could not delete old backup ${file}:`, errorMessage(error));
4117
4137
  }
4118
4138
  }
4119
4139
  } catch (error) {
4120
- warn(MODULE2, "Could not clean up backups:", error instanceof Error ? error.message : error);
4140
+ warn(MODULE2, "Could not clean up backups:", errorMessage(error));
4121
4141
  }
4122
4142
  }
4123
4143
  /**
@@ -5900,17 +5920,17 @@ function requestLog(octokit) {
5900
5920
  octokit.log.debug("request", options);
5901
5921
  const start = Date.now();
5902
5922
  const requestOptions = octokit.request.endpoint.parse(options);
5903
- const path11 = requestOptions.url.replace(options.baseUrl, "");
5923
+ const path10 = requestOptions.url.replace(options.baseUrl, "");
5904
5924
  return request2(options).then((response) => {
5905
5925
  const requestId = response.headers["x-github-request-id"];
5906
5926
  octokit.log.info(
5907
- `${requestOptions.method} ${path11} - ${response.status} with id ${requestId} in ${Date.now() - start}ms`
5927
+ `${requestOptions.method} ${path10} - ${response.status} with id ${requestId} in ${Date.now() - start}ms`
5908
5928
  );
5909
5929
  return response;
5910
5930
  }).catch((error) => {
5911
5931
  const requestId = error.response?.headers["x-github-request-id"] || "UNKNOWN";
5912
5932
  octokit.log.error(
5913
- `${requestOptions.method} ${path11} - ${error.status} with id ${requestId} in ${Date.now() - start}ms`
5933
+ `${requestOptions.method} ${path10} - ${error.status} with id ${requestId} in ${Date.now() - start}ms`
5914
5934
  );
5915
5935
  throw error;
5916
5936
  });
@@ -9891,7 +9911,7 @@ function isAuthRequest(method, pathname) {
9891
9911
  }
9892
9912
  function routeMatcher(paths) {
9893
9913
  const regexes = paths.map(
9894
- (path11) => path11.split("/").map((c) => c.startsWith("{") ? "(?:.+?)" : c).join("/")
9914
+ (path10) => path10.split("/").map((c) => c.startsWith("{") ? "(?:.+?)" : c).join("/")
9895
9915
  );
9896
9916
  const regex2 = `^(?:${regexes.map((r) => `(?:${r})`).join("|")})[^/]*$`;
9897
9917
  return new RegExp(regex2, "i");
@@ -10219,10 +10239,7 @@ async function cachedRequest(cache, url, fetcher) {
10219
10239
  }
10220
10240
  }
10221
10241
  function isNotModifiedError(err) {
10222
- if (err && typeof err === "object" && "status" in err) {
10223
- return err.status === 304;
10224
- }
10225
- return false;
10242
+ return getHttpStatusCode(err) === 304;
10226
10243
  }
10227
10244
  var fs3, path3, crypto, MODULE4, DEFAULT_MAX_AGE_MS, HttpCache, _httpCache;
10228
10245
  var init_http_cache = __esm({
@@ -10233,6 +10250,7 @@ var init_http_cache = __esm({
10233
10250
  crypto = __toESM(require("crypto"), 1);
10234
10251
  init_utils();
10235
10252
  init_logger();
10253
+ init_errors();
10236
10254
  MODULE4 = "http-cache";
10237
10255
  DEFAULT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
10238
10256
  HttpCache = class {
@@ -10250,6 +10268,18 @@ var init_http_cache = __esm({
10250
10268
  pathFor(url) {
10251
10269
  return path3.join(this.cacheDir, `${this.keyFor(url)}.json`);
10252
10270
  }
10271
+ /**
10272
+ * Return the cached body if the entry exists and is younger than `maxAgeMs`.
10273
+ * Useful for time-based caching where ETag validation isn't applicable
10274
+ * (e.g., caching aggregated results from paginated API calls).
10275
+ */
10276
+ getIfFresh(key, maxAgeMs) {
10277
+ const entry = this.get(key);
10278
+ if (!entry) return null;
10279
+ const age = Date.now() - new Date(entry.cachedAt).getTime();
10280
+ if (!Number.isFinite(age) || age < 0 || age > maxAgeMs) return null;
10281
+ return entry.body;
10282
+ }
10253
10283
  /**
10254
10284
  * Look up a cached response. Returns `null` if no cache entry exists.
10255
10285
  */
@@ -10813,11 +10843,28 @@ var init_display_utils = __esm({
10813
10843
  });
10814
10844
 
10815
10845
  // src/core/github-stats.ts
10816
- async function fetchUserMergedPRCounts(octokit, githubUsername) {
10846
+ function isCachedPRCounts(v) {
10847
+ if (typeof v !== "object" || v === null) return false;
10848
+ const obj = v;
10849
+ return Array.isArray(obj.reposEntries) && typeof obj.monthlyCounts === "object" && obj.monthlyCounts !== null && typeof obj.monthlyOpenedCounts === "object" && obj.monthlyOpenedCounts !== null && typeof obj.dailyActivityCounts === "object" && obj.dailyActivityCounts !== null;
10850
+ }
10851
+ async function fetchUserPRCounts(octokit, githubUsername, query, label, accumulateRepo) {
10817
10852
  if (!githubUsername) {
10818
10853
  return { repos: /* @__PURE__ */ new Map(), monthlyCounts: {}, monthlyOpenedCounts: {}, dailyActivityCounts: {} };
10819
10854
  }
10820
- debug(MODULE6, `Fetching merged PR counts for @${githubUsername}...`);
10855
+ const cache = getHttpCache();
10856
+ const cacheKey = `pr-counts:${label}:${githubUsername}`;
10857
+ const cached = cache.getIfFresh(cacheKey, PR_COUNTS_CACHE_TTL_MS);
10858
+ if (cached && isCachedPRCounts(cached)) {
10859
+ debug(MODULE6, `Using cached ${label} PR counts for @${githubUsername}`);
10860
+ return {
10861
+ repos: new Map(cached.reposEntries),
10862
+ monthlyCounts: cached.monthlyCounts,
10863
+ monthlyOpenedCounts: cached.monthlyOpenedCounts,
10864
+ dailyActivityCounts: cached.dailyActivityCounts
10865
+ };
10866
+ }
10867
+ debug(MODULE6, `Fetching ${label} PR counts for @${githubUsername}...`);
10821
10868
  const repos = /* @__PURE__ */ new Map();
10822
10869
  const monthlyCounts = {};
10823
10870
  const monthlyOpenedCounts = {};
@@ -10826,7 +10873,7 @@ async function fetchUserMergedPRCounts(octokit, githubUsername) {
10826
10873
  let fetched = 0;
10827
10874
  while (true) {
10828
10875
  const { data } = await octokit.search.issuesAndPullRequests({
10829
- q: `is:pr is:merged author:${githubUsername}`,
10876
+ q: `is:pr ${query} author:${githubUsername}`,
10830
10877
  sort: "updated",
10831
10878
  order: "desc",
10832
10879
  per_page: 100,
@@ -10835,25 +10882,18 @@ async function fetchUserMergedPRCounts(octokit, githubUsername) {
10835
10882
  for (const item of data.items) {
10836
10883
  const parsed = extractOwnerRepo(item.html_url);
10837
10884
  if (!parsed) {
10838
- warn(MODULE6, `Skipping merged PR with unparseable URL: ${item.html_url}`);
10885
+ warn(MODULE6, `Skipping ${label} PR with unparseable URL: ${item.html_url}`);
10839
10886
  continue;
10840
10887
  }
10841
10888
  const { owner } = parsed;
10842
10889
  const repo = `${owner}/${parsed.repo}`;
10843
- if (owner.toLowerCase() === githubUsername.toLowerCase()) continue;
10844
- const mergedAt = item.pull_request?.merged_at || item.closed_at || "";
10845
- const existing = repos.get(repo);
10846
- if (existing) {
10847
- existing.count += 1;
10848
- if (mergedAt && mergedAt > existing.lastMergedAt) {
10849
- existing.lastMergedAt = mergedAt;
10850
- }
10851
- } else {
10852
- repos.set(repo, { count: 1, lastMergedAt: mergedAt });
10853
- }
10854
- if (mergedAt) {
10855
- const month = mergedAt.slice(0, 7);
10890
+ if (isOwnRepo(owner, githubUsername)) continue;
10891
+ const primaryDate = accumulateRepo(repos, repo, item);
10892
+ if (primaryDate) {
10893
+ const month = primaryDate.slice(0, 7);
10856
10894
  monthlyCounts[month] = (monthlyCounts[month] || 0) + 1;
10895
+ const day = primaryDate.slice(0, 10);
10896
+ if (day.length === 10) dailyActivityCounts[day] = (dailyActivityCounts[day] || 0) + 1;
10857
10897
  }
10858
10898
  if (item.created_at) {
10859
10899
  const openedMonth = item.created_at.slice(0, 7);
@@ -10861,10 +10901,6 @@ async function fetchUserMergedPRCounts(octokit, githubUsername) {
10861
10901
  const openedDay = item.created_at.slice(0, 10);
10862
10902
  if (openedDay.length === 10) dailyActivityCounts[openedDay] = (dailyActivityCounts[openedDay] || 0) + 1;
10863
10903
  }
10864
- if (mergedAt) {
10865
- const mergedDay = mergedAt.slice(0, 10);
10866
- if (mergedDay.length === 10) dailyActivityCounts[mergedDay] = (dailyActivityCounts[mergedDay] || 0) + 1;
10867
- }
10868
10904
  }
10869
10905
  fetched += data.items.length;
10870
10906
  if (fetched >= data.total_count || fetched >= 1e3 || data.items.length === 0) {
@@ -10872,59 +10908,41 @@ async function fetchUserMergedPRCounts(octokit, githubUsername) {
10872
10908
  }
10873
10909
  page++;
10874
10910
  }
10875
- debug(MODULE6, `Found ${fetched} merged PRs across ${repos.size} repos`);
10911
+ debug(MODULE6, `Found ${fetched} ${label} PRs across ${repos.size} repos`);
10912
+ cache.set(cacheKey, "", {
10913
+ reposEntries: Array.from(repos.entries()),
10914
+ monthlyCounts,
10915
+ monthlyOpenedCounts,
10916
+ dailyActivityCounts
10917
+ });
10876
10918
  return { repos, monthlyCounts, monthlyOpenedCounts, dailyActivityCounts };
10877
10919
  }
10878
- async function fetchUserClosedPRCounts(octokit, githubUsername) {
10879
- if (!githubUsername) {
10880
- return { repos: /* @__PURE__ */ new Map(), monthlyCounts: {}, monthlyOpenedCounts: {}, dailyActivityCounts: {} };
10881
- }
10882
- debug(MODULE6, `Fetching closed PR counts for @${githubUsername}...`);
10883
- const repos = /* @__PURE__ */ new Map();
10884
- const monthlyCounts = {};
10885
- const monthlyOpenedCounts = {};
10886
- const dailyActivityCounts = {};
10887
- let page = 1;
10888
- let fetched = 0;
10889
- while (true) {
10890
- const { data } = await octokit.search.issuesAndPullRequests({
10891
- q: `is:pr is:closed is:unmerged author:${githubUsername}`,
10892
- sort: "updated",
10893
- order: "desc",
10894
- per_page: 100,
10895
- page
10896
- });
10897
- for (const item of data.items) {
10898
- const parsed = extractOwnerRepo(item.html_url);
10899
- if (!parsed) {
10900
- warn(MODULE6, `Skipping closed PR with unparseable URL: ${item.html_url}`);
10901
- continue;
10902
- }
10903
- const { owner } = parsed;
10904
- const repo = `${owner}/${parsed.repo}`;
10905
- if (owner.toLowerCase() === githubUsername.toLowerCase()) continue;
10906
- repos.set(repo, (repos.get(repo) || 0) + 1);
10907
- if (item.closed_at) {
10908
- const closedMonth = item.closed_at.slice(0, 7);
10909
- monthlyCounts[closedMonth] = (monthlyCounts[closedMonth] || 0) + 1;
10910
- const closedDay = item.closed_at.slice(0, 10);
10911
- if (closedDay.length === 10) dailyActivityCounts[closedDay] = (dailyActivityCounts[closedDay] || 0) + 1;
10912
- }
10913
- if (item.created_at) {
10914
- const openedMonth = item.created_at.slice(0, 7);
10915
- monthlyOpenedCounts[openedMonth] = (monthlyOpenedCounts[openedMonth] || 0) + 1;
10916
- const openedDay = item.created_at.slice(0, 10);
10917
- if (openedDay.length === 10) dailyActivityCounts[openedDay] = (dailyActivityCounts[openedDay] || 0) + 1;
10918
- }
10920
+ function fetchUserMergedPRCounts(octokit, githubUsername) {
10921
+ return fetchUserPRCounts(octokit, githubUsername, "is:merged", "merged", (repos, repo, item) => {
10922
+ if (!item.pull_request?.merged_at) {
10923
+ warn(
10924
+ MODULE6,
10925
+ `merged_at missing for merged PR ${item.html_url}${item.closed_at ? ", falling back to closed_at" : ", no date available"}`
10926
+ );
10919
10927
  }
10920
- fetched += data.items.length;
10921
- if (fetched >= data.total_count || fetched >= 1e3 || data.items.length === 0) {
10922
- break;
10928
+ const mergedAt = item.pull_request?.merged_at || item.closed_at || "";
10929
+ const existing = repos.get(repo);
10930
+ if (existing) {
10931
+ existing.count += 1;
10932
+ if (mergedAt && mergedAt > existing.lastMergedAt) {
10933
+ existing.lastMergedAt = mergedAt;
10934
+ }
10935
+ } else {
10936
+ repos.set(repo, { count: 1, lastMergedAt: mergedAt });
10923
10937
  }
10924
- page++;
10925
- }
10926
- debug(MODULE6, `Found ${fetched} closed (unmerged) PRs across ${repos.size} repos`);
10927
- return { repos, monthlyCounts, monthlyOpenedCounts, dailyActivityCounts };
10938
+ return mergedAt;
10939
+ });
10940
+ }
10941
+ function fetchUserClosedPRCounts(octokit, githubUsername) {
10942
+ return fetchUserPRCounts(octokit, githubUsername, "is:closed is:unmerged", "closed", (repos, repo, item) => {
10943
+ repos.set(repo, (repos.get(repo) || 0) + 1);
10944
+ return item.closed_at || "";
10945
+ });
10928
10946
  }
10929
10947
  async function fetchRecentPRs(octokit, config, query, label, days, mapItem) {
10930
10948
  if (!config.githubUsername) {
@@ -10949,7 +10967,7 @@ async function fetchRecentPRs(octokit, config, query, label, days, mapItem) {
10949
10967
  continue;
10950
10968
  }
10951
10969
  const repo = `${parsed.owner}/${parsed.repo}`;
10952
- if (parsed.owner.toLowerCase() === config.githubUsername.toLowerCase()) continue;
10970
+ if (isOwnRepo(parsed.owner, config.githubUsername)) continue;
10953
10971
  if (config.excludeRepos.includes(repo)) continue;
10954
10972
  if (config.excludeOrgs?.some((org) => parsed.owner.toLowerCase() === org.toLowerCase())) continue;
10955
10973
  results.push(mapItem(item, { owner: parsed.owner, repo, number: parsed.number }));
@@ -10998,14 +11016,15 @@ async function fetchRecentlyMergedPRs(octokit, config, days = 7) {
10998
11016
  }
10999
11017
  );
11000
11018
  }
11001
- var MODULE6;
11019
+ var MODULE6, PR_COUNTS_CACHE_TTL_MS;
11002
11020
  var init_github_stats = __esm({
11003
11021
  "src/core/github-stats.ts"() {
11004
11022
  "use strict";
11005
11023
  init_utils();
11006
- init_errors();
11007
11024
  init_logger();
11025
+ init_http_cache();
11008
11026
  MODULE6 = "github-stats";
11027
+ PR_COUNTS_CACHE_TTL_MS = 60 * 60 * 1e3;
11009
11028
  }
11010
11029
  });
11011
11030
 
@@ -11106,9 +11125,9 @@ var init_pr_monitor = __esm({
11106
11125
  const pr = await this.fetchPRDetails(item.html_url);
11107
11126
  if (pr) prs.push(pr);
11108
11127
  } catch (error) {
11109
- const errorMessage = error instanceof Error ? error.message : String(error);
11110
- warn("pr-monitor", `Error fetching ${item.html_url}: ${errorMessage}`);
11111
- failures.push({ prUrl: item.html_url, error: errorMessage });
11128
+ const errMsg = errorMessage(error);
11129
+ warn("pr-monitor", `Error fetching ${item.html_url}: ${errMsg}`);
11130
+ failures.push({ prUrl: item.html_url, error: errMsg });
11112
11131
  }
11113
11132
  },
11114
11133
  MAX_CONCURRENT_REQUESTS
@@ -11155,12 +11174,12 @@ var init_pr_monitor = __esm({
11155
11174
  paginateAll(
11156
11175
  (page) => this.octokit.pulls.listReviewComments({ owner, repo, pull_number: number, per_page: 100, page })
11157
11176
  ).catch((err) => {
11158
- const status2 = err?.status;
11177
+ const status2 = getHttpStatusCode(err);
11159
11178
  if (status2 === 429) {
11160
11179
  throw err;
11161
11180
  }
11162
11181
  if (status2 === 403) {
11163
- const msg = (err?.message ?? "").toLowerCase();
11182
+ const msg = errorMessage(err).toLowerCase();
11164
11183
  if (msg.includes("rate limit") || msg.includes("abuse detection")) {
11165
11184
  throw err;
11166
11185
  }
@@ -11327,7 +11346,7 @@ var init_pr_monitor = __esm({
11327
11346
  this.octokit.repos.getCombinedStatusForRef({ owner, repo, ref: sha }),
11328
11347
  // 404 is expected for repos without check runs configured; log other errors for debugging
11329
11348
  this.octokit.checks.listForRef({ owner, repo, ref: sha }).catch((err) => {
11330
- const status = err?.status;
11349
+ const status = getHttpStatusCode(err);
11331
11350
  if (status === 404) {
11332
11351
  debug("pr-monitor", `Check runs 404 for ${owner}/${repo}@${sha.slice(0, 7)} (no checks configured)`);
11333
11352
  } else {
@@ -11353,8 +11372,8 @@ var init_pr_monitor = __esm({
11353
11372
  const combinedAnalysis = analyzeCombinedStatus(combinedStatus);
11354
11373
  return mergeStatuses(checkRunAnalysis, combinedAnalysis, checkRuns.length);
11355
11374
  } catch (error) {
11356
- const statusCode = error.status;
11357
- const errorMessage = error instanceof Error ? error.message : String(error);
11375
+ const statusCode = getHttpStatusCode(error);
11376
+ const errMsg = errorMessage(error);
11358
11377
  if (statusCode === 401) {
11359
11378
  warn("pr-monitor", `CI check failed for ${owner}/${repo}: Invalid token`);
11360
11379
  } else if (statusCode === 403) {
@@ -11363,7 +11382,7 @@ var init_pr_monitor = __esm({
11363
11382
  debug("pr-monitor", `CI check 404 for ${owner}/${repo} (no CI configured)`);
11364
11383
  return { status: "unknown", failingCheckNames: [], failingCheckConclusions: /* @__PURE__ */ new Map() };
11365
11384
  } else {
11366
- warn("pr-monitor", `Failed to check CI for ${owner}/${repo}@${sha.slice(0, 7)}: ${errorMessage}`);
11385
+ warn("pr-monitor", `Failed to check CI for ${owner}/${repo}@${sha.slice(0, 7)}: ${errMsg}`);
11367
11386
  }
11368
11387
  return { status: "unknown", failingCheckNames: [], failingCheckConclusions: /* @__PURE__ */ new Map() };
11369
11388
  }
@@ -11424,10 +11443,7 @@ var init_pr_monitor = __esm({
11424
11443
  results.set(result.value.repo, result.value.stars);
11425
11444
  } else {
11426
11445
  chunkFailures++;
11427
- warn(
11428
- MODULE7,
11429
- `Failed to fetch stars for ${chunk[j]}: ${result.reason instanceof Error ? result.reason.message : result.reason}`
11430
- );
11446
+ warn(MODULE7, `Failed to fetch stars for ${chunk[j]}: ${errorMessage(result.reason)}`);
11431
11447
  }
11432
11448
  }
11433
11449
  if (chunkFailures === chunk.length && chunk.length > 0) {
@@ -11441,42 +11457,6 @@ var init_pr_monitor = __esm({
11441
11457
  debug(MODULE7, `Fetched star counts for ${results.size}/${repos.length} repos`);
11442
11458
  return results;
11443
11459
  }
11444
- /**
11445
- * Shared helper: search for recent PRs and filter out own repos, excluded repos/orgs.
11446
- * Returns parsed search results that pass all filters.
11447
- */
11448
- async fetchRecentPRs(query, label, days, mapItem) {
11449
- const config = this.stateManager.getState().config;
11450
- if (!config.githubUsername) {
11451
- warn(MODULE7, `Skipping recently ${label} PRs fetch: no githubUsername configured. Run /setup-oss to configure.`);
11452
- return [];
11453
- }
11454
- const sinceDate = /* @__PURE__ */ new Date();
11455
- sinceDate.setDate(sinceDate.getDate() - days);
11456
- const since = sinceDate.toISOString().split("T")[0];
11457
- debug(MODULE7, `Fetching recently ${label} PRs for @${config.githubUsername} (since ${since})...`);
11458
- const { data } = await this.octokit.search.issuesAndPullRequests({
11459
- q: query.replace("{username}", config.githubUsername).replace("{since}", since),
11460
- sort: "updated",
11461
- order: "desc",
11462
- per_page: 100
11463
- });
11464
- const results = [];
11465
- for (const item of data.items) {
11466
- const parsed = parseGitHubUrl(item.html_url);
11467
- if (!parsed) {
11468
- warn(MODULE7, `Could not parse GitHub URL from API response: ${item.html_url}`);
11469
- continue;
11470
- }
11471
- const repo = `${parsed.owner}/${parsed.repo}`;
11472
- if (parsed.owner.toLowerCase() === config.githubUsername.toLowerCase()) continue;
11473
- if (config.excludeRepos.includes(repo)) continue;
11474
- if (config.excludeOrgs?.some((org) => parsed.owner.toLowerCase() === org.toLowerCase())) continue;
11475
- results.push(mapItem(item, { owner: parsed.owner, repo, number: parsed.number }));
11476
- }
11477
- debug(MODULE7, `Found ${results.length} recently ${label} PRs`);
11478
- return results;
11479
- }
11480
11460
  /**
11481
11461
  * Fetch PRs closed without merge in the last N days.
11482
11462
  * Delegates to github-stats module.
@@ -11901,7 +11881,7 @@ var init_issue_vetting = __esm({
11901
11881
  if (_IssueVetter.isRateLimitError(error)) {
11902
11882
  rateLimitFailures++;
11903
11883
  }
11904
- warn(MODULE8, `Error vetting issue ${url}:`, error instanceof Error ? error.message : error);
11884
+ warn(MODULE8, `Error vetting issue ${url}:`, errorMessage(error));
11905
11885
  });
11906
11886
  pending.push(task);
11907
11887
  if (pending.length >= MAX_CONCURRENT_REQUESTS2) {
@@ -11921,10 +11901,10 @@ var init_issue_vetting = __esm({
11921
11901
  }
11922
11902
  /** Check if an error is a GitHub rate limit error (429 or rate-limit 403). */
11923
11903
  static isRateLimitError(error) {
11924
- const status = error?.status;
11904
+ const status = getHttpStatusCode(error);
11925
11905
  if (status === 429) return true;
11926
11906
  if (status === 403) {
11927
- const msg = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
11907
+ const msg = errorMessage(error).toLowerCase();
11928
11908
  return msg.includes("rate limit");
11929
11909
  }
11930
11910
  return false;
@@ -11950,12 +11930,12 @@ var init_issue_vetting = __esm({
11950
11930
  });
11951
11931
  return { passed: data.total_count === 0 && linkedPRs.length === 0 };
11952
11932
  } catch (error) {
11953
- const errorMessage = error instanceof Error ? error.message : String(error);
11933
+ const errMsg = errorMessage(error);
11954
11934
  warn(
11955
11935
  MODULE8,
11956
- `Failed to check for existing PRs on ${owner}/${repo}#${issueNumber}: ${errorMessage}. Assuming no existing PR.`
11936
+ `Failed to check for existing PRs on ${owner}/${repo}#${issueNumber}: ${errMsg}. Assuming no existing PR.`
11957
11937
  );
11958
- return { passed: true, inconclusive: true, reason: errorMessage };
11938
+ return { passed: true, inconclusive: true, reason: errMsg };
11959
11939
  }
11960
11940
  }
11961
11941
  /**
@@ -11971,8 +11951,8 @@ var init_issue_vetting = __esm({
11971
11951
  });
11972
11952
  return data.total_count;
11973
11953
  } catch (error) {
11974
- const errorMessage = error instanceof Error ? error.message : String(error);
11975
- warn(MODULE8, `Could not check merged PRs in ${owner}/${repo}: ${errorMessage}. Defaulting to 0.`);
11954
+ const errMsg = errorMessage(error);
11955
+ warn(MODULE8, `Could not check merged PRs in ${owner}/${repo}: ${errMsg}. Defaulting to 0.`);
11976
11956
  return 0;
11977
11957
  }
11978
11958
  }
@@ -12015,12 +11995,9 @@ var init_issue_vetting = __esm({
12015
11995
  }
12016
11996
  return { passed: true };
12017
11997
  } catch (error) {
12018
- const errorMessage = error instanceof Error ? error.message : String(error);
12019
- warn(
12020
- MODULE8,
12021
- `Failed to check claim status on ${owner}/${repo}#${issueNumber}: ${errorMessage}. Assuming not claimed.`
12022
- );
12023
- return { passed: true, inconclusive: true, reason: errorMessage };
11998
+ const errMsg = errorMessage(error);
11999
+ warn(MODULE8, `Failed to check claim status on ${owner}/${repo}#${issueNumber}: ${errMsg}. Assuming not claimed.`);
12000
+ return { passed: true, inconclusive: true, reason: errMsg };
12024
12001
  }
12025
12002
  }
12026
12003
  async checkProjectHealth(owner, repo) {
@@ -12051,8 +12028,8 @@ var init_issue_vetting = __esm({
12051
12028
  ciStatus = "passing";
12052
12029
  }
12053
12030
  } catch (error) {
12054
- const errorMessage = error instanceof Error ? error.message : String(error);
12055
- warn(MODULE8, `Failed to check CI status for ${owner}/${repo}: ${errorMessage}. Defaulting to unknown.`);
12031
+ const errMsg = errorMessage(error);
12032
+ warn(MODULE8, `Failed to check CI status for ${owner}/${repo}: ${errMsg}. Defaulting to unknown.`);
12056
12033
  }
12057
12034
  return {
12058
12035
  repo: `${owner}/${repo}`,
@@ -12067,8 +12044,8 @@ var init_issue_vetting = __esm({
12067
12044
  forksCount: repoData.forks_count
12068
12045
  };
12069
12046
  } catch (error) {
12070
- const errorMessage = error instanceof Error ? error.message : String(error);
12071
- warn(MODULE8, `Error checking project health for ${owner}/${repo}: ${errorMessage}`);
12047
+ const errMsg = errorMessage(error);
12048
+ warn(MODULE8, `Error checking project health for ${owner}/${repo}: ${errMsg}`);
12072
12049
  return {
12073
12050
  repo: `${owner}/${repo}`,
12074
12051
  lastCommitAt: "",
@@ -12078,7 +12055,7 @@ var init_issue_vetting = __esm({
12078
12055
  ciStatus: "unknown",
12079
12056
  isActive: false,
12080
12057
  checkFailed: true,
12081
- failureReason: errorMessage
12058
+ failureReason: errMsg
12082
12059
  };
12083
12060
  }
12084
12061
  }
@@ -12231,18 +12208,18 @@ var init_issue_discovery = __esm({
12231
12208
  return starredRepos;
12232
12209
  } catch (error) {
12233
12210
  const cachedRepos = this.stateManager.getStarredRepos();
12234
- const errorMessage = error instanceof Error ? error.message : String(error);
12235
- warn(MODULE9, "Error fetching starred repos:", errorMessage);
12211
+ const errMsg = errorMessage(error);
12212
+ warn(MODULE9, "Error fetching starred repos:", errMsg);
12236
12213
  if (cachedRepos.length === 0) {
12237
12214
  warn(
12238
12215
  MODULE9,
12239
- `Failed to fetch starred repositories from GitHub API. No cached repos available. Error: ${errorMessage}
12216
+ `Failed to fetch starred repositories from GitHub API. No cached repos available. Error: ${errMsg}
12240
12217
  Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`
12241
12218
  );
12242
12219
  } else {
12243
12220
  warn(
12244
12221
  MODULE9,
12245
- `Failed to fetch starred repositories from GitHub API. Using ${cachedRepos.length} cached repos instead. Error: ${errorMessage}`
12222
+ `Failed to fetch starred repositories from GitHub API. Using ${cachedRepos.length} cached repos instead. Error: ${errMsg}`
12246
12223
  );
12247
12224
  }
12248
12225
  return cachedRepos;
@@ -12281,10 +12258,10 @@ Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`
12281
12258
  warn(MODULE9, this.rateLimitWarning);
12282
12259
  }
12283
12260
  } catch (error) {
12284
- if (error?.status === 401) {
12261
+ if (getHttpStatusCode(error) === 401) {
12285
12262
  throw error;
12286
12263
  }
12287
- warn(MODULE9, "Could not check rate limit:", error instanceof Error ? error.message : error);
12264
+ warn(MODULE9, "Could not check rate limit:", errorMessage(error));
12288
12265
  }
12289
12266
  const mergedPRRepos = this.stateManager.getReposWithMergedPRs();
12290
12267
  const mergedPRRepoSet = new Set(mergedPRRepos);
@@ -12450,12 +12427,12 @@ Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`
12450
12427
  }
12451
12428
  console.log(`Found ${starFiltered.length} candidates from general search`);
12452
12429
  } catch (error) {
12453
- const errorMessage = error instanceof Error ? error.message : String(error);
12454
- phase2Error = errorMessage;
12430
+ const errMsg = errorMessage(error);
12431
+ phase2Error = errMsg;
12455
12432
  if (IssueVetter.isRateLimitError(error)) {
12456
12433
  rateLimitHitDuringSearch = true;
12457
12434
  }
12458
- warn(MODULE9, `Error in general issue search: ${errorMessage}`);
12435
+ warn(MODULE9, `Error in general issue search: ${errMsg}`);
12459
12436
  }
12460
12437
  }
12461
12438
  let phase3Error = null;
@@ -12519,12 +12496,12 @@ Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`
12519
12496
  }
12520
12497
  console.log(`Found ${starFiltered.length} candidates from maintained-repo search`);
12521
12498
  } catch (error) {
12522
- const errorMessage = error instanceof Error ? error.message : String(error);
12523
- phase3Error = errorMessage;
12499
+ const errMsg = errorMessage(error);
12500
+ phase3Error = errMsg;
12524
12501
  if (IssueVetter.isRateLimitError(error)) {
12525
12502
  rateLimitHitDuringSearch = true;
12526
12503
  }
12527
- warn(MODULE9, `Error in maintained-repo search: ${errorMessage}`);
12504
+ warn(MODULE9, `Error in maintained-repo search: ${errMsg}`);
12528
12505
  }
12529
12506
  }
12530
12507
  if (allCandidates.length === 0) {
@@ -12600,11 +12577,7 @@ Tip: Ensure your GITHUB_TOKEN has the 'read:user' scope and try again.`
12600
12577
  rateLimitFailures++;
12601
12578
  }
12602
12579
  const batchRepos = batch.join(", ");
12603
- warn(
12604
- MODULE9,
12605
- `Error searching issues in batch [${batchRepos}]:`,
12606
- error instanceof Error ? error.message : error
12607
- );
12580
+ warn(MODULE9, `Error searching issues in batch [${batchRepos}]:`, errorMessage(error));
12608
12581
  }
12609
12582
  }
12610
12583
  const allBatchesFailed = failedBatches === batches.length && batches.length > 0;
@@ -12798,7 +12771,7 @@ var init_issue_conversation = __esm({
12798
12771
  }
12799
12772
  const { owner, repo } = parsed;
12800
12773
  const repoFullName = `${owner}/${repo}`;
12801
- if (owner.toLowerCase() === username.toLowerCase()) continue;
12774
+ if (isOwnRepo(owner, username)) continue;
12802
12775
  if (item.user?.login?.toLowerCase() === username.toLowerCase()) continue;
12803
12776
  if (config.excludeRepos.includes(repoFullName)) continue;
12804
12777
  if (config.excludeOrgs?.some((org) => owner.toLowerCase() === org.toLowerCase())) continue;
@@ -12823,7 +12796,7 @@ var init_issue_conversation = __esm({
12823
12796
  });
12824
12797
  }
12825
12798
  } catch (error) {
12826
- const msg = error instanceof Error ? error.message : String(error);
12799
+ const msg = errorMessage(error);
12827
12800
  failures.push({ issueUrl: item.html_url, error: msg });
12828
12801
  warn(MODULE10, `Error analyzing issue ${item.html_url}: ${msg}`);
12829
12802
  }
@@ -13377,6 +13350,7 @@ var init_core = __esm({
13377
13350
  init_comment_utils();
13378
13351
  init_github();
13379
13352
  init_utils();
13353
+ init_errors();
13380
13354
  init_logger();
13381
13355
  init_http_cache();
13382
13356
  init_daily_logic();
@@ -13478,15 +13452,15 @@ async function fetchPRData(prMonitor, token) {
13478
13452
  prMonitor.fetchUserMergedPRCounts(),
13479
13453
  prMonitor.fetchUserClosedPRCounts(),
13480
13454
  prMonitor.fetchRecentlyClosedPRs().catch((err) => {
13481
- console.error(`Warning: Failed to fetch recently closed PRs: ${err instanceof Error ? err.message : err}`);
13455
+ console.error(`Warning: Failed to fetch recently closed PRs: ${errorMessage(err)}`);
13482
13456
  return [];
13483
13457
  }),
13484
13458
  prMonitor.fetchRecentlyMergedPRs().catch((err) => {
13485
- console.error(`Warning: Failed to fetch recently merged PRs: ${err instanceof Error ? err.message : err}`);
13459
+ console.error(`Warning: Failed to fetch recently merged PRs: ${errorMessage(err)}`);
13486
13460
  return [];
13487
13461
  }),
13488
13462
  issueMonitor.fetchCommentedIssues().catch((error) => {
13489
- const msg = error instanceof Error ? error.message : String(error);
13463
+ const msg = errorMessage(error);
13490
13464
  if (msg.includes("No GitHub username configured")) {
13491
13465
  console.error(`[DAILY] Issue conversation tracking requires setup: ${msg}`);
13492
13466
  } else {
@@ -13543,10 +13517,7 @@ async function updateRepoScores(prMonitor, prs, mergedCounts, closedCounts) {
13543
13517
  stateManager2.updateRepoScore(repo, { mergedPRCount: count, lastMergedAt: lastMergedAt || void 0 });
13544
13518
  } catch (error) {
13545
13519
  mergedCountFailures++;
13546
- console.error(
13547
- `[DAILY] Failed to update merged count for ${repo}:`,
13548
- error instanceof Error ? error.message : error
13549
- );
13520
+ console.error(`[DAILY] Failed to update merged count for ${repo}:`, errorMessage(error));
13550
13521
  }
13551
13522
  }
13552
13523
  if (mergedCountFailures === mergedCounts.size && mergedCounts.size > 0) {
@@ -13566,10 +13537,7 @@ async function updateRepoScores(prMonitor, prs, mergedCounts, closedCounts) {
13566
13537
  stateManager2.updateRepoScore(repo, { closedWithoutMergeCount: count });
13567
13538
  } catch (error) {
13568
13539
  closedCountFailures++;
13569
- console.error(
13570
- `[DAILY] Failed to update closed count for ${repo}:`,
13571
- error instanceof Error ? error.message : error
13572
- );
13540
+ console.error(`[DAILY] Failed to update closed count for ${repo}:`, errorMessage(error));
13573
13541
  }
13574
13542
  }
13575
13543
  if (closedCountFailures === closedCounts.size && closedCounts.size > 0) {
@@ -13582,7 +13550,7 @@ async function updateRepoScores(prMonitor, prs, mergedCounts, closedCounts) {
13582
13550
  stateManager2.updateRepoScore(repo, { signals });
13583
13551
  } catch (error) {
13584
13552
  signalUpdateFailures++;
13585
- console.error(`[DAILY] Failed to update signals for ${repo}:`, error instanceof Error ? error.message : error);
13553
+ console.error(`[DAILY] Failed to update signals for ${repo}:`, errorMessage(error));
13586
13554
  }
13587
13555
  }
13588
13556
  if (signalUpdateFailures === repoSignals.size && repoSignals.size > 0) {
@@ -13595,7 +13563,7 @@ async function updateRepoScores(prMonitor, prs, mergedCounts, closedCounts) {
13595
13563
  try {
13596
13564
  starCounts = await prMonitor.fetchRepoStarCounts(allRepos);
13597
13565
  } catch (error) {
13598
- console.error("[DAILY] Failed to fetch repo star counts:", error instanceof Error ? error.message : error);
13566
+ console.error("[DAILY] Failed to fetch repo star counts:", errorMessage(error));
13599
13567
  console.error(
13600
13568
  "[DAILY] Dashboard minStars filter will use cached star counts (or be skipped for repos without cached data)."
13601
13569
  );
@@ -13607,7 +13575,7 @@ async function updateRepoScores(prMonitor, prs, mergedCounts, closedCounts) {
13607
13575
  stateManager2.updateRepoScore(repo, { stargazersCount: stars });
13608
13576
  } catch (error) {
13609
13577
  starUpdateFailures++;
13610
- console.error(`[DAILY] Failed to update star count for ${repo}:`, error instanceof Error ? error.message : error);
13578
+ console.error(`[DAILY] Failed to update star count for ${repo}:`, errorMessage(error));
13611
13579
  }
13612
13580
  }
13613
13581
  if (starUpdateFailures === starCounts.size && starCounts.size > 0) {
@@ -13619,7 +13587,7 @@ async function updateRepoScores(prMonitor, prs, mergedCounts, closedCounts) {
13619
13587
  stateManager2.addTrustedProject(repo);
13620
13588
  } catch (error) {
13621
13589
  trustSyncFailures++;
13622
- console.error(`[DAILY] Failed to sync trusted project ${repo}:`, error instanceof Error ? error.message : error);
13590
+ console.error(`[DAILY] Failed to sync trusted project ${repo}:`, errorMessage(error));
13623
13591
  }
13624
13592
  }
13625
13593
  if (trustSyncFailures === mergedCounts.size && mergedCounts.size > 0) {
@@ -13633,12 +13601,12 @@ function updateAnalytics(prs, monthlyCounts, monthlyClosedCounts, openedFromMerg
13633
13601
  try {
13634
13602
  stateManager2.setMonthlyMergedCounts(monthlyCounts);
13635
13603
  } catch (error) {
13636
- console.error("[DAILY] Failed to store monthly merged counts:", error instanceof Error ? error.message : error);
13604
+ console.error("[DAILY] Failed to store monthly merged counts:", errorMessage(error));
13637
13605
  }
13638
13606
  try {
13639
13607
  stateManager2.setMonthlyClosedCounts(monthlyClosedCounts);
13640
13608
  } catch (error) {
13641
- console.error("[DAILY] Failed to store monthly closed counts:", error instanceof Error ? error.message : error);
13609
+ console.error("[DAILY] Failed to store monthly closed counts:", errorMessage(error));
13642
13610
  }
13643
13611
  try {
13644
13612
  const combinedOpenedCounts = { ...openedFromMerged };
@@ -13653,10 +13621,7 @@ function updateAnalytics(prs, monthlyCounts, monthlyClosedCounts, openedFromMerg
13653
13621
  }
13654
13622
  stateManager2.setMonthlyOpenedCounts(combinedOpenedCounts);
13655
13623
  } catch (error) {
13656
- console.error(
13657
- "[DAILY] Failed to compute/store monthly opened counts:",
13658
- error instanceof Error ? error.message : error
13659
- );
13624
+ console.error("[DAILY] Failed to compute/store monthly opened counts:", errorMessage(error));
13660
13625
  }
13661
13626
  }
13662
13627
  function partitionPRs(prMonitor, prs, recentlyClosedPRs, recentlyMergedPRs) {
@@ -13671,7 +13636,7 @@ function partitionPRs(prMonitor, prs, recentlyClosedPRs, recentlyMergedPRs) {
13671
13636
  stateManager2.save();
13672
13637
  }
13673
13638
  } catch (error) {
13674
- console.error("[DAILY] Failed to expire/persist snoozes:", error instanceof Error ? error.message : error);
13639
+ console.error("[DAILY] Failed to expire/persist snoozes:", errorMessage(error));
13675
13640
  }
13676
13641
  const shelvedPRs = [];
13677
13642
  const autoUnshelvedPRs = [];
@@ -13801,6 +13766,7 @@ var init_daily = __esm({
13801
13766
  "src/commands/daily.ts"() {
13802
13767
  "use strict";
13803
13768
  init_core();
13769
+ init_errors();
13804
13770
  init_json();
13805
13771
  init_core();
13806
13772
  }
@@ -13891,17 +13857,17 @@ var init_search = __esm({
13891
13857
  function validateGitHubUrl(url, pattern, entityType) {
13892
13858
  if (pattern.test(url)) return;
13893
13859
  const example = entityType === "PR" ? "https://github.com/owner/repo/pull/123" : "https://github.com/owner/repo/issues/123";
13894
- throw new Error(`Invalid ${entityType} URL: ${url}. Expected format: ${example}`);
13860
+ throw new ValidationError(`Invalid ${entityType} URL: ${url}. Expected format: ${example}`);
13895
13861
  }
13896
13862
  function validateUrl(url) {
13897
13863
  if (url.length > MAX_URL_LENGTH) {
13898
- throw new Error(`URL exceeds maximum length of ${MAX_URL_LENGTH} characters`);
13864
+ throw new ValidationError(`URL exceeds maximum length of ${MAX_URL_LENGTH} characters`);
13899
13865
  }
13900
13866
  return url;
13901
13867
  }
13902
13868
  function validateMessage(message) {
13903
13869
  if (message.length > MAX_MESSAGE_LENGTH) {
13904
- throw new Error(`Message exceeds maximum length of ${MAX_MESSAGE_LENGTH} characters`);
13870
+ throw new ValidationError(`Message exceeds maximum length of ${MAX_MESSAGE_LENGTH} characters`);
13905
13871
  }
13906
13872
  return message;
13907
13873
  }
@@ -14063,33 +14029,35 @@ async function runComments(options) {
14063
14029
  }
14064
14030
  const { owner, repo, number: pull_number } = parsed;
14065
14031
  const { data: pr } = await octokit.pulls.get({ owner, repo, pull_number });
14066
- const reviewComments = await paginateAll(
14067
- (page) => octokit.pulls.listReviewComments({
14068
- owner,
14069
- repo,
14070
- pull_number,
14071
- per_page: 100,
14072
- page
14073
- })
14074
- );
14075
- const issueComments = await paginateAll(
14076
- (page) => octokit.issues.listComments({
14077
- owner,
14078
- repo,
14079
- issue_number: pull_number,
14080
- per_page: 100,
14081
- page
14082
- })
14083
- );
14084
- const reviews = await paginateAll(
14085
- (page) => octokit.pulls.listReviews({
14086
- owner,
14087
- repo,
14088
- pull_number,
14089
- per_page: 100,
14090
- page
14091
- })
14092
- );
14032
+ const [reviewComments, issueComments, reviews] = await Promise.all([
14033
+ paginateAll(
14034
+ (page) => octokit.pulls.listReviewComments({
14035
+ owner,
14036
+ repo,
14037
+ pull_number,
14038
+ per_page: 100,
14039
+ page
14040
+ })
14041
+ ),
14042
+ paginateAll(
14043
+ (page) => octokit.issues.listComments({
14044
+ owner,
14045
+ repo,
14046
+ issue_number: pull_number,
14047
+ per_page: 100,
14048
+ page
14049
+ })
14050
+ ),
14051
+ paginateAll(
14052
+ (page) => octokit.pulls.listReviews({
14053
+ owner,
14054
+ repo,
14055
+ pull_number,
14056
+ per_page: 100,
14057
+ page
14058
+ })
14059
+ )
14060
+ ]);
14093
14061
  const username = stateManager2.getState().config.githubUsername;
14094
14062
  const filterComment = (c) => {
14095
14063
  if (!c.user) return false;
@@ -14487,17 +14455,17 @@ async function fetchDashboardData(token) {
14487
14455
  const [{ prs, failures }, recentlyClosedPRs, recentlyMergedPRs, mergedResult, closedResult, fetchedIssues] = await Promise.all([
14488
14456
  prMonitor.fetchUserOpenPRs(),
14489
14457
  prMonitor.fetchRecentlyClosedPRs().catch((err) => {
14490
- console.error(`Warning: Failed to fetch recently closed PRs: ${err instanceof Error ? err.message : err}`);
14458
+ console.error(`Warning: Failed to fetch recently closed PRs: ${errorMessage(err)}`);
14491
14459
  return [];
14492
14460
  }),
14493
14461
  prMonitor.fetchRecentlyMergedPRs().catch((err) => {
14494
- console.error(`Warning: Failed to fetch recently merged PRs: ${err instanceof Error ? err.message : err}`);
14462
+ console.error(`Warning: Failed to fetch recently merged PRs: ${errorMessage(err)}`);
14495
14463
  return [];
14496
14464
  }),
14497
14465
  prMonitor.fetchUserMergedPRCounts(),
14498
14466
  prMonitor.fetchUserClosedPRCounts(),
14499
14467
  issueMonitor.fetchCommentedIssues().catch((error) => {
14500
- const msg = error instanceof Error ? error.message : String(error);
14468
+ const msg = errorMessage(error);
14501
14469
  if (msg.includes("No GitHub username configured")) {
14502
14470
  console.error(`[DASHBOARD] Issue conversation tracking requires setup: ${msg}`);
14503
14471
  } else {
@@ -14521,12 +14489,12 @@ async function fetchDashboardData(token) {
14521
14489
  try {
14522
14490
  stateManager2.setMonthlyMergedCounts(monthlyCounts);
14523
14491
  } catch (error) {
14524
- console.error("[DASHBOARD] Failed to store monthly merged counts:", error instanceof Error ? error.message : error);
14492
+ console.error("[DASHBOARD] Failed to store monthly merged counts:", errorMessage(error));
14525
14493
  }
14526
14494
  try {
14527
14495
  stateManager2.setMonthlyClosedCounts(monthlyClosedCounts);
14528
14496
  } catch (error) {
14529
- console.error("[DASHBOARD] Failed to store monthly closed counts:", error instanceof Error ? error.message : error);
14497
+ console.error("[DASHBOARD] Failed to store monthly closed counts:", errorMessage(error));
14530
14498
  }
14531
14499
  try {
14532
14500
  const combinedOpenedCounts = { ...openedFromMerged };
@@ -14541,7 +14509,7 @@ async function fetchDashboardData(token) {
14541
14509
  }
14542
14510
  stateManager2.setMonthlyOpenedCounts(combinedOpenedCounts);
14543
14511
  } catch (error) {
14544
- console.error("[DASHBOARD] Failed to store monthly opened counts:", error instanceof Error ? error.message : error);
14512
+ console.error("[DASHBOARD] Failed to store monthly opened counts:", errorMessage(error));
14545
14513
  }
14546
14514
  const digest = prMonitor.generateDigest(prs, recentlyClosedPRs, recentlyMergedPRs);
14547
14515
  const shelvedUrls = new Set(stateManager2.getState().config.shelvedPRUrls || []);
@@ -14581,6 +14549,7 @@ var init_dashboard_data = __esm({
14581
14549
  "src/commands/dashboard-data.ts"() {
14582
14550
  "use strict";
14583
14551
  init_core();
14552
+ init_errors();
14584
14553
  init_daily();
14585
14554
  }
14586
14555
  });
@@ -16320,7 +16289,7 @@ async function startDashboardServer(options) {
16320
16289
  stateManager2.save();
16321
16290
  } catch (error) {
16322
16291
  console.error("Action failed:", body.action, body.url, error);
16323
- sendError(res, 500, `Action failed: ${error instanceof Error ? error.message : String(error)}`);
16292
+ sendError(res, 500, `Action failed: ${errorMessage(error)}`);
16324
16293
  return;
16325
16294
  }
16326
16295
  if (cachedDigest) {
@@ -16343,7 +16312,7 @@ async function startDashboardServer(options) {
16343
16312
  sendJson(res, 200, cachedJsonData);
16344
16313
  } catch (error) {
16345
16314
  console.error("Dashboard refresh failed:", error);
16346
- sendError(res, 500, `Refresh failed: ${error instanceof Error ? error.message : String(error)}`);
16315
+ sendError(res, 500, `Refresh failed: ${errorMessage(error)}`);
16347
16316
  }
16348
16317
  }
16349
16318
  function serveStaticFile(requestUrl, res) {
@@ -16464,6 +16433,7 @@ var init_dashboard_server = __esm({
16464
16433
  fs5 = __toESM(require("fs"), 1);
16465
16434
  path5 = __toESM(require("path"), 1);
16466
16435
  init_core();
16436
+ init_errors();
16467
16437
  init_dashboard_data();
16468
16438
  init_dashboard_templates();
16469
16439
  VALID_ACTIONS = /* @__PURE__ */ new Set(["shelve", "unshelve", "snooze", "unsnooze"]);
@@ -16512,7 +16482,7 @@ async function runDashboard(options) {
16512
16482
  digest = result.digest;
16513
16483
  commentedIssues = result.commentedIssues;
16514
16484
  } catch (error) {
16515
- console.error("Failed to fetch fresh data:", error instanceof Error ? error.message : error);
16485
+ console.error("Failed to fetch fresh data:", errorMessage(error));
16516
16486
  console.error("Falling back to cached data (issue conversations unavailable)...");
16517
16487
  digest = stateManager2.getState().lastDigest;
16518
16488
  }
@@ -16642,6 +16612,7 @@ var init_dashboard = __esm({
16642
16612
  path6 = __toESM(require("path"), 1);
16643
16613
  import_child_process2 = require("child_process");
16644
16614
  init_core();
16615
+ init_errors();
16645
16616
  init_json();
16646
16617
  init_dashboard_data();
16647
16618
  init_dashboard_templates();
@@ -16727,7 +16698,7 @@ async function runParseList(options) {
16727
16698
  try {
16728
16699
  content = fs7.readFileSync(filePath, "utf-8");
16729
16700
  } catch (error) {
16730
- const msg = error instanceof Error ? error.message : String(error);
16701
+ const msg = errorMessage(error);
16731
16702
  throw new Error(`Failed to read file: ${msg}`, { cause: error });
16732
16703
  }
16733
16704
  return parseIssueList(content);
@@ -16738,6 +16709,7 @@ var init_parse_list = __esm({
16738
16709
  "use strict";
16739
16710
  fs7 = __toESM(require("fs"), 1);
16740
16711
  path7 = __toESM(require("path"), 1);
16712
+ init_errors();
16741
16713
  }
16742
16714
  });
16743
16715
 
@@ -16782,7 +16754,7 @@ async function runCheckIntegration(options) {
16782
16754
  }).trim();
16783
16755
  newFiles = output ? output.split("\n").filter(Boolean) : [];
16784
16756
  } catch (error) {
16785
- const msg = error instanceof Error ? error.message : String(error);
16757
+ const msg = errorMessage(error);
16786
16758
  throw new Error(`Failed to run git diff: ${msg}`, { cause: error });
16787
16759
  }
16788
16760
  const codeFiles = newFiles.filter((f) => {
@@ -16826,9 +16798,9 @@ async function runCheckIntegration(options) {
16826
16798
  referencedBy.push(...matches);
16827
16799
  }
16828
16800
  } catch (error) {
16829
- const exitCode = error && typeof error === "object" && "status" in error ? error.status : null;
16830
- if (exitCode !== null && exitCode !== 1) {
16831
- const msg = error instanceof Error ? error.message : String(error);
16801
+ const exitCode = error && typeof error === "object" && "status" in error ? error.status : void 0;
16802
+ if (exitCode !== void 0 && exitCode !== 1) {
16803
+ const msg = errorMessage(error);
16832
16804
  debug("check-integration", `git grep failed for "${pattern}": ${msg}`);
16833
16805
  }
16834
16806
  }
@@ -16855,6 +16827,7 @@ var init_check_integration = __esm({
16855
16827
  path8 = __toESM(require("path"), 1);
16856
16828
  import_child_process3 = require("child_process");
16857
16829
  init_core();
16830
+ init_errors();
16858
16831
  CODE_EXTENSIONS = /* @__PURE__ */ new Set([
16859
16832
  ".ts",
16860
16833
  ".tsx",
@@ -16976,7 +16949,7 @@ async function runLocalRepos(options) {
16976
16949
  stateManager2.setLocalRepoCache({ repos, scanPaths, cachedAt });
16977
16950
  stateManager2.save();
16978
16951
  } catch (error) {
16979
- const msg = error instanceof Error ? error.message : String(error);
16952
+ const msg = errorMessage(error);
16980
16953
  debug("local-repos", `Failed to cache scan results: ${msg}`);
16981
16954
  }
16982
16955
  return {
@@ -16995,6 +16968,7 @@ var init_local_repos = __esm({
16995
16968
  os2 = __toESM(require("os"), 1);
16996
16969
  import_child_process4 = require("child_process");
16997
16970
  init_core();
16971
+ init_errors();
16998
16972
  DEFAULT_SCAN_PATHS = [
16999
16973
  path9.join(os2.homedir(), "Documents", "oss"),
17000
16974
  path9.join(os2.homedir(), "dev"),
@@ -17014,15 +16988,6 @@ __export(startup_exports, {
17014
16988
  parseIssueListPathFromConfig: () => parseIssueListPathFromConfig,
17015
16989
  runStartup: () => runStartup
17016
16990
  });
17017
- function getVersion() {
17018
- try {
17019
- const pkgPath = path10.join(path10.dirname(process.argv[1]), "..", "package.json");
17020
- return JSON.parse(fs9.readFileSync(pkgPath, "utf-8")).version;
17021
- } catch (error) {
17022
- console.error("[STARTUP] Failed to detect CLI version:", error instanceof Error ? error.message : error);
17023
- return "0.0.0";
17024
- }
17025
- }
17026
16991
  function parseIssueListPathFromConfig(configContent) {
17027
16992
  const match = configContent.match(/^---\n([\s\S]*?)\n---/);
17028
16993
  if (!match) return void 0;
@@ -17058,7 +17023,7 @@ function detectIssueList() {
17058
17023
  source = "configured";
17059
17024
  }
17060
17025
  } catch (error) {
17061
- console.error("[STARTUP] Failed to read config:", error instanceof Error ? error.message : error);
17026
+ console.error("[STARTUP] Failed to read config:", errorMessage(error));
17062
17027
  }
17063
17028
  }
17064
17029
  if (!issueListPath) {
@@ -17077,10 +17042,7 @@ function detectIssueList() {
17077
17042
  const { availableCount, completedCount } = countIssueListItems(content);
17078
17043
  return { path: issueListPath, source, availableCount, completedCount };
17079
17044
  } catch (error) {
17080
- console.error(
17081
- `[STARTUP] Failed to read issue list at ${issueListPath}:`,
17082
- error instanceof Error ? error.message : error
17083
- );
17045
+ console.error(`[STARTUP] Failed to read issue list at ${issueListPath}:`, errorMessage(error));
17084
17046
  return { path: issueListPath, source, availableCount: 0, completedCount: 0 };
17085
17047
  }
17086
17048
  }
@@ -17095,7 +17057,7 @@ function openInBrowser(filePath) {
17095
17057
  });
17096
17058
  }
17097
17059
  async function runStartup() {
17098
- const version = getVersion();
17060
+ const version = getCLIVersion();
17099
17061
  const stateManager2 = getStateManager();
17100
17062
  if (!stateManager2.isSetupComplete()) {
17101
17063
  return { version, setupComplete: false };
@@ -17118,7 +17080,7 @@ async function runStartup() {
17118
17080
  dashboardOpened = true;
17119
17081
  }
17120
17082
  } catch (error) {
17121
- console.error("[STARTUP] Dashboard generation failed:", error instanceof Error ? error.message : error);
17083
+ console.error("[STARTUP] Dashboard generation failed:", errorMessage(error));
17122
17084
  }
17123
17085
  if (dashboardOpened) {
17124
17086
  daily.briefSummary += " | Dashboard opened in browser";
@@ -17132,14 +17094,14 @@ async function runStartup() {
17132
17094
  issueList
17133
17095
  };
17134
17096
  }
17135
- var fs9, path10, import_child_process5;
17097
+ var fs9, import_child_process5;
17136
17098
  var init_startup = __esm({
17137
17099
  "src/commands/startup.ts"() {
17138
17100
  "use strict";
17139
17101
  fs9 = __toESM(require("fs"), 1);
17140
- path10 = __toESM(require("path"), 1);
17141
17102
  import_child_process5 = require("child_process");
17142
17103
  init_core();
17104
+ init_errors();
17143
17105
  init_daily();
17144
17106
  init_dashboard();
17145
17107
  }
@@ -17282,6 +17244,7 @@ var {
17282
17244
 
17283
17245
  // src/cli.ts
17284
17246
  init_core();
17247
+ init_errors();
17285
17248
  init_json();
17286
17249
  function printRepos(repos) {
17287
17250
  const entries = Object.entries(repos).sort(([a], [b]) => a.localeCompare(b));
@@ -17292,7 +17255,7 @@ function printRepos(repos) {
17292
17255
  }
17293
17256
  }
17294
17257
  function handleCommandError(err, json) {
17295
- const msg = err instanceof Error ? err.message : String(err);
17258
+ const msg = errorMessage(err);
17296
17259
  if (json) {
17297
17260
  outputJsonError(msg);
17298
17261
  } else {
@@ -17300,16 +17263,7 @@ function handleCommandError(err, json) {
17300
17263
  }
17301
17264
  process.exit(1);
17302
17265
  }
17303
- var VERSION10 = (() => {
17304
- try {
17305
- const fs10 = require("fs");
17306
- const path11 = require("path");
17307
- const pkgPath = path11.join(path11.dirname(process.argv[1]), "..", "package.json");
17308
- return JSON.parse(fs10.readFileSync(pkgPath, "utf-8")).version;
17309
- } catch (_err) {
17310
- return "0.0.0";
17311
- }
17312
- })();
17266
+ var VERSION10 = getCLIVersion();
17313
17267
  var LOCAL_ONLY_COMMANDS = [
17314
17268
  "help",
17315
17269
  "status",
@@ -17701,8 +17655,8 @@ program2.command("parse-issue-list <path>").description("Parse a markdown issue
17701
17655
  if (options.json) {
17702
17656
  outputJson(data);
17703
17657
  } else {
17704
- const path11 = await import("path");
17705
- const resolvedPath = path11.resolve(filePath);
17658
+ const path10 = await import("path");
17659
+ const resolvedPath = path10.resolve(filePath);
17706
17660
  console.log(`
17707
17661
  \u{1F4CB} Issue List: ${resolvedPath}
17708
17662
  `);