@oss-autopilot/core 0.41.0 → 0.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/cli.bundle.cjs +1300 -1020
  2. package/dist/cli.js +591 -55
  3. package/dist/commands/check-integration.d.ts +3 -3
  4. package/dist/commands/check-integration.js +5 -39
  5. package/dist/commands/comments.d.ts +6 -9
  6. package/dist/commands/comments.js +78 -228
  7. package/dist/commands/config.d.ts +8 -2
  8. package/dist/commands/config.js +6 -28
  9. package/dist/commands/daily.d.ts +28 -4
  10. package/dist/commands/daily.js +19 -32
  11. package/dist/commands/dashboard-server.d.ts +14 -0
  12. package/dist/commands/dashboard-server.js +361 -0
  13. package/dist/commands/dashboard.d.ts +5 -0
  14. package/dist/commands/dashboard.js +49 -0
  15. package/dist/commands/dismiss.d.ts +13 -5
  16. package/dist/commands/dismiss.js +4 -24
  17. package/dist/commands/index.d.ts +33 -0
  18. package/dist/commands/index.js +22 -0
  19. package/dist/commands/init.d.ts +5 -4
  20. package/dist/commands/init.js +4 -14
  21. package/dist/commands/local-repos.d.ts +4 -5
  22. package/dist/commands/local-repos.js +4 -32
  23. package/dist/commands/parse-list.d.ts +3 -4
  24. package/dist/commands/parse-list.js +6 -38
  25. package/dist/commands/read.d.ts +11 -5
  26. package/dist/commands/read.js +4 -18
  27. package/dist/commands/search.d.ts +3 -3
  28. package/dist/commands/search.js +39 -65
  29. package/dist/commands/setup.d.ts +34 -5
  30. package/dist/commands/setup.js +75 -166
  31. package/dist/commands/shelve.d.ts +13 -5
  32. package/dist/commands/shelve.js +4 -24
  33. package/dist/commands/snooze.d.ts +15 -9
  34. package/dist/commands/snooze.js +16 -59
  35. package/dist/commands/startup.d.ts +11 -6
  36. package/dist/commands/startup.js +39 -67
  37. package/dist/commands/status.d.ts +3 -3
  38. package/dist/commands/status.js +10 -29
  39. package/dist/commands/track.d.ts +10 -9
  40. package/dist/commands/track.js +17 -39
  41. package/dist/commands/validation.d.ts +2 -2
  42. package/dist/commands/validation.js +3 -11
  43. package/dist/commands/vet.d.ts +3 -3
  44. package/dist/commands/vet.js +16 -26
  45. package/dist/formatters/json.d.ts +58 -0
  46. package/package.json +5 -1
@@ -1196,8 +1196,8 @@ var require_command = __commonJS({
1196
1196
  "../../node_modules/.pnpm/commander@14.0.3/node_modules/commander/lib/command.js"(exports2) {
1197
1197
  var EventEmitter = require("node:events").EventEmitter;
1198
1198
  var childProcess = require("node:child_process");
1199
- var path9 = require("node:path");
1200
- var fs9 = require("node:fs");
1199
+ var path11 = require("node:path");
1200
+ var fs10 = require("node:fs");
1201
1201
  var process2 = require("node:process");
1202
1202
  var { Argument: Argument2, humanReadableArgName } = require_argument();
1203
1203
  var { CommanderError: CommanderError2 } = require_error();
@@ -2191,7 +2191,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2191
2191
  * @param {string} subcommandName
2192
2192
  */
2193
2193
  _checkForMissingExecutable(executableFile, executableDir, subcommandName) {
2194
- if (fs9.existsSync(executableFile)) return;
2194
+ if (fs10.existsSync(executableFile)) return;
2195
2195
  const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
2196
2196
  const executableMissing = `'${executableFile}' does not exist
2197
2197
  - if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
@@ -2209,11 +2209,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
2209
2209
  let launchWithNode = false;
2210
2210
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
2211
2211
  function findFile(baseDir, baseName) {
2212
- const localBin = path9.resolve(baseDir, baseName);
2213
- if (fs9.existsSync(localBin)) return localBin;
2214
- if (sourceExt.includes(path9.extname(baseName))) return void 0;
2212
+ const localBin = path11.resolve(baseDir, baseName);
2213
+ if (fs10.existsSync(localBin)) return localBin;
2214
+ if (sourceExt.includes(path11.extname(baseName))) return void 0;
2215
2215
  const foundExt = sourceExt.find(
2216
- (ext) => fs9.existsSync(`${localBin}${ext}`)
2216
+ (ext) => fs10.existsSync(`${localBin}${ext}`)
2217
2217
  );
2218
2218
  if (foundExt) return `${localBin}${foundExt}`;
2219
2219
  return void 0;
@@ -2225,21 +2225,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
2225
2225
  if (this._scriptPath) {
2226
2226
  let resolvedScriptPath;
2227
2227
  try {
2228
- resolvedScriptPath = fs9.realpathSync(this._scriptPath);
2228
+ resolvedScriptPath = fs10.realpathSync(this._scriptPath);
2229
2229
  } catch {
2230
2230
  resolvedScriptPath = this._scriptPath;
2231
2231
  }
2232
- executableDir = path9.resolve(
2233
- path9.dirname(resolvedScriptPath),
2232
+ executableDir = path11.resolve(
2233
+ path11.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 = path9.basename(
2240
+ const legacyName = path11.basename(
2241
2241
  this._scriptPath,
2242
- path9.extname(this._scriptPath)
2242
+ path11.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(path9.extname(executableFile));
2253
+ launchWithNode = sourceExt.includes(path11.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 = path9.basename(filename, path9.extname(filename));
3168
+ this._name = path11.basename(filename, path11.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(path10) {
3183
- if (path10 === void 0) return this._executableDir;
3184
- this._executableDir = path10;
3182
+ executableDir(path12) {
3183
+ if (path12 === void 0) return this._executableDir;
3184
+ this._executableDir = path12;
3185
3185
  return this;
3186
3186
  }
3187
3187
  /**
@@ -3681,6 +3681,15 @@ function getGitHubToken() {
3681
3681
  }
3682
3682
  return null;
3683
3683
  }
3684
+ function requireGitHubToken() {
3685
+ const token = getGitHubToken();
3686
+ if (!token) {
3687
+ throw new ConfigurationError(
3688
+ "GitHub authentication required.\n\nOptions:\n 1. Use gh CLI: gh auth login\n 2. Set GITHUB_TOKEN environment variable\n\nThe gh CLI is recommended - install from https://cli.github.com"
3689
+ );
3690
+ }
3691
+ return token;
3692
+ }
3684
3693
  async function getGitHubTokenAsync() {
3685
3694
  if (cachedGitHubToken) {
3686
3695
  return cachedGitHubToken;
@@ -3694,12 +3703,12 @@ async function getGitHubTokenAsync() {
3694
3703
  return cachedGitHubToken;
3695
3704
  }
3696
3705
  try {
3697
- const token = await new Promise((resolve3, reject) => {
3706
+ const token = await new Promise((resolve5, reject) => {
3698
3707
  (0, import_child_process.execFile)("gh", ["auth", "token"], { encoding: "utf-8", timeout: 2e3 }, (error, stdout) => {
3699
3708
  if (error) {
3700
3709
  reject(error);
3701
3710
  } else {
3702
- resolve3(stdout.trim());
3711
+ resolve5(stdout.trim());
3703
3712
  }
3704
3713
  });
3705
3714
  });
@@ -5891,17 +5900,17 @@ function requestLog(octokit) {
5891
5900
  octokit.log.debug("request", options);
5892
5901
  const start = Date.now();
5893
5902
  const requestOptions = octokit.request.endpoint.parse(options);
5894
- const path9 = requestOptions.url.replace(options.baseUrl, "");
5903
+ const path11 = requestOptions.url.replace(options.baseUrl, "");
5895
5904
  return request2(options).then((response) => {
5896
5905
  const requestId = response.headers["x-github-request-id"];
5897
5906
  octokit.log.info(
5898
- `${requestOptions.method} ${path9} - ${response.status} with id ${requestId} in ${Date.now() - start}ms`
5907
+ `${requestOptions.method} ${path11} - ${response.status} with id ${requestId} in ${Date.now() - start}ms`
5899
5908
  );
5900
5909
  return response;
5901
5910
  }).catch((error) => {
5902
5911
  const requestId = error.response?.headers["x-github-request-id"] || "UNKNOWN";
5903
5912
  octokit.log.error(
5904
- `${requestOptions.method} ${path9} - ${error.status} with id ${requestId} in ${Date.now() - start}ms`
5913
+ `${requestOptions.method} ${path11} - ${error.status} with id ${requestId} in ${Date.now() - start}ms`
5905
5914
  );
5906
5915
  throw error;
5907
5916
  });
@@ -8970,8 +8979,8 @@ var require_light = __commonJS({
8970
8979
  return this.Promise.resolve();
8971
8980
  }
8972
8981
  yieldLoop(t = 0) {
8973
- return new this.Promise(function(resolve3, reject) {
8974
- return setTimeout(resolve3, t);
8982
+ return new this.Promise(function(resolve5, reject) {
8983
+ return setTimeout(resolve5, t);
8975
8984
  });
8976
8985
  }
8977
8986
  computePenalty() {
@@ -9182,15 +9191,15 @@ var require_light = __commonJS({
9182
9191
  return this._queue.length === 0;
9183
9192
  }
9184
9193
  async _tryToRun() {
9185
- var args, cb, error, reject, resolve3, returned, task;
9194
+ var args, cb, error, reject, resolve5, returned, task;
9186
9195
  if (this._running < 1 && this._queue.length > 0) {
9187
9196
  this._running++;
9188
- ({ task, args, resolve: resolve3, reject } = this._queue.shift());
9197
+ ({ task, args, resolve: resolve5, reject } = this._queue.shift());
9189
9198
  cb = await (async function() {
9190
9199
  try {
9191
9200
  returned = await task(...args);
9192
9201
  return function() {
9193
- return resolve3(returned);
9202
+ return resolve5(returned);
9194
9203
  };
9195
9204
  } catch (error1) {
9196
9205
  error = error1;
@@ -9205,13 +9214,13 @@ var require_light = __commonJS({
9205
9214
  }
9206
9215
  }
9207
9216
  schedule(task, ...args) {
9208
- var promise, reject, resolve3;
9209
- resolve3 = reject = null;
9217
+ var promise, reject, resolve5;
9218
+ resolve5 = reject = null;
9210
9219
  promise = new this.Promise(function(_resolve, _reject) {
9211
- resolve3 = _resolve;
9220
+ resolve5 = _resolve;
9212
9221
  return reject = _reject;
9213
9222
  });
9214
- this._queue.push({ task, args, resolve: resolve3, reject });
9223
+ this._queue.push({ task, args, resolve: resolve5, reject });
9215
9224
  this._tryToRun();
9216
9225
  return promise;
9217
9226
  }
@@ -9612,14 +9621,14 @@ var require_light = __commonJS({
9612
9621
  counts = this._states.counts;
9613
9622
  return counts[0] + counts[1] + counts[2] + counts[3] === at;
9614
9623
  };
9615
- return new this.Promise((resolve3, reject) => {
9624
+ return new this.Promise((resolve5, reject) => {
9616
9625
  if (finished()) {
9617
- return resolve3();
9626
+ return resolve5();
9618
9627
  } else {
9619
9628
  return this.on("done", () => {
9620
9629
  if (finished()) {
9621
9630
  this.removeAllListeners("done");
9622
- return resolve3();
9631
+ return resolve5();
9623
9632
  }
9624
9633
  });
9625
9634
  }
@@ -9712,9 +9721,9 @@ var require_light = __commonJS({
9712
9721
  options = parser$5.load(options, this.jobDefaults);
9713
9722
  }
9714
9723
  task = (...args2) => {
9715
- return new this.Promise(function(resolve3, reject) {
9724
+ return new this.Promise(function(resolve5, reject) {
9716
9725
  return fn(...args2, function(...args3) {
9717
- return (args3[0] != null ? reject : resolve3)(args3);
9726
+ return (args3[0] != null ? reject : resolve5)(args3);
9718
9727
  });
9719
9728
  });
9720
9729
  };
@@ -9882,7 +9891,7 @@ function isAuthRequest(method, pathname) {
9882
9891
  }
9883
9892
  function routeMatcher(paths) {
9884
9893
  const regexes = paths.map(
9885
- (path9) => path9.split("/").map((c) => c.startsWith("{") ? "(?:.+?)" : c).join("/")
9894
+ (path11) => path11.split("/").map((c) => c.startsWith("{") ? "(?:.+?)" : c).join("/")
9886
9895
  );
9887
9896
  const regex2 = `^(?:${regexes.map((r) => `(?:${r})`).join("|")})[^/]*$`;
9888
9897
  return new RegExp(regex2, "i");
@@ -13455,25 +13464,9 @@ __export(daily_exports, {
13455
13464
  groupPRsByRepo: () => groupPRsByRepo,
13456
13465
  printDigest: () => printDigest,
13457
13466
  runDaily: () => runDaily,
13467
+ runDailyForDisplay: () => runDailyForDisplay,
13458
13468
  toShelvedPRRef: () => toShelvedPRRef
13459
13469
  });
13460
- async function runDaily(options) {
13461
- const token = getGitHubToken();
13462
- try {
13463
- await runDailyInner(token, options);
13464
- } catch (error) {
13465
- const msg = error instanceof Error ? error.message : String(error);
13466
- if (options.json) {
13467
- outputJsonError(`Daily check failed: ${msg}`);
13468
- } else {
13469
- console.error(`[FATAL] Daily check failed: ${msg}`);
13470
- if (error instanceof Error && error.stack) {
13471
- console.error(error.stack);
13472
- }
13473
- }
13474
- process.exit(1);
13475
- }
13476
- }
13477
13470
  async function fetchPRData(prMonitor, token) {
13478
13471
  const { prs, failures } = await prMonitor.fetchUserOpenPRs();
13479
13472
  if (failures.length > 0) {
@@ -13796,14 +13789,13 @@ async function executeDailyCheckInternal(token) {
13796
13789
  const { activePRs, shelvedPRs, digest } = partitionPRs(prMonitor, prs, recentlyClosedPRs, recentlyMergedPRs);
13797
13790
  return generateDigestOutput(digest, activePRs, shelvedPRs, commentedIssues, failures);
13798
13791
  }
13799
- async function runDailyInner(token, options) {
13800
- if (options.json) {
13801
- const result = await executeDailyCheck(token);
13802
- outputJson(result);
13803
- } else {
13804
- const result = await executeDailyCheckInternal(token);
13805
- printDigest(result.digest, result.capacity, result.commentedIssues);
13806
- }
13792
+ async function runDaily() {
13793
+ const token = requireGitHubToken();
13794
+ return executeDailyCheck(token);
13795
+ }
13796
+ async function runDailyForDisplay() {
13797
+ const token = requireGitHubToken();
13798
+ return executeDailyCheckInternal(token);
13807
13799
  }
13808
13800
  var init_daily = __esm({
13809
13801
  "src/commands/daily.ts"() {
@@ -13824,39 +13816,21 @@ async function runStatus(options) {
13824
13816
  const stats = stateManager2.getStats();
13825
13817
  const state = stateManager2.getState();
13826
13818
  const lastUpdated = state.lastDigestAt || state.lastRunAt;
13827
- if (options.json) {
13828
- const { totalTracked: _totalTracked, ...outputStats } = stats;
13829
- const output = {
13830
- stats: outputStats,
13831
- lastRunAt: state.lastRunAt
13832
- };
13833
- if (options.offline) {
13834
- output.offline = true;
13835
- output.lastUpdated = lastUpdated;
13836
- }
13837
- outputJson(output);
13838
- } else {
13839
- console.log("\n\u{1F4CA} OSS Status\n");
13840
- console.log(`Merged PRs: ${stats.mergedPRs}`);
13841
- console.log(`Closed PRs: ${stats.closedPRs}`);
13842
- console.log(`Merge Rate: ${stats.mergeRate}`);
13843
- console.log(`Needs Response: ${stats.needsResponse}`);
13844
- if (options.offline) {
13845
- console.log(`
13846
- Last Updated: ${lastUpdated || "Never"}`);
13847
- console.log("(Offline mode: showing cached data)");
13848
- } else {
13849
- console.log(`
13850
- Last Run: ${state.lastRunAt || "Never"}`);
13851
- }
13852
- console.log("\nRun with --json for structured output");
13819
+ const { totalTracked: _totalTracked, ...outputStats } = stats;
13820
+ const output = {
13821
+ stats: outputStats,
13822
+ lastRunAt: state.lastRunAt
13823
+ };
13824
+ if (options.offline) {
13825
+ output.offline = true;
13826
+ output.lastUpdated = lastUpdated;
13853
13827
  }
13828
+ return output;
13854
13829
  }
13855
13830
  var init_status = __esm({
13856
13831
  "src/commands/status.ts"() {
13857
13832
  "use strict";
13858
13833
  init_core();
13859
- init_json();
13860
13834
  }
13861
13835
  });
13862
13836
 
@@ -13866,94 +13840,58 @@ __export(search_exports, {
13866
13840
  runSearch: () => runSearch
13867
13841
  });
13868
13842
  async function runSearch(options) {
13869
- const token = getGitHubToken();
13843
+ const token = requireGitHubToken();
13870
13844
  const discovery = new IssueDiscovery(token);
13871
- if (!options.json) {
13872
- console.log(`
13873
- \u{1F50D} Searching for issues (max ${options.maxResults})...
13874
- `);
13875
- }
13876
13845
  const candidates = await discovery.searchIssues({ maxResults: options.maxResults });
13877
- if (options.json) {
13878
- const stateManager2 = getStateManager();
13879
- const { config } = stateManager2.getState();
13880
- const excludedRepos = config.excludeRepos || [];
13881
- const aiPolicyBlocklist = config.aiPolicyBlocklist ?? DEFAULT_CONFIG.aiPolicyBlocklist ?? [];
13882
- const searchOutput = {
13883
- candidates: candidates.map((c) => {
13884
- const repoScoreRecord = stateManager2.getRepoScore(c.issue.repo);
13885
- return {
13886
- issue: {
13887
- repo: c.issue.repo,
13888
- number: c.issue.number,
13889
- title: c.issue.title,
13890
- url: c.issue.url,
13891
- labels: c.issue.labels
13892
- },
13893
- recommendation: c.recommendation,
13894
- reasonsToApprove: c.reasonsToApprove,
13895
- reasonsToSkip: c.reasonsToSkip,
13896
- searchPriority: c.searchPriority,
13897
- viabilityScore: c.viabilityScore,
13898
- repoScore: repoScoreRecord ? {
13899
- score: repoScoreRecord.score,
13900
- mergedPRCount: repoScoreRecord.mergedPRCount,
13901
- closedWithoutMergeCount: repoScoreRecord.closedWithoutMergeCount,
13902
- isResponsive: repoScoreRecord.signals?.isResponsive ?? false,
13903
- lastMergedAt: repoScoreRecord.lastMergedAt
13904
- } : void 0
13905
- };
13906
- }),
13907
- excludedRepos,
13908
- aiPolicyBlocklist
13909
- };
13910
- if (discovery.rateLimitWarning) {
13911
- searchOutput.rateLimitWarning = discovery.rateLimitWarning;
13912
- }
13913
- outputJson(searchOutput);
13914
- } else {
13915
- if (candidates.length === 0) {
13916
- if (discovery.rateLimitWarning) {
13917
- console.warn(`
13918
- \u26A0 ${discovery.rateLimitWarning}
13919
- `);
13920
- } else {
13921
- console.log("No matching issues found.");
13922
- }
13923
- return;
13924
- }
13925
- if (discovery.rateLimitWarning) {
13926
- console.warn(`
13927
- \u26A0 ${discovery.rateLimitWarning}
13928
- `);
13929
- }
13930
- console.log(`Found ${candidates.length} candidates:
13931
- `);
13932
- for (const candidate of candidates) {
13933
- console.log(discovery.formatCandidate(candidate));
13934
- console.log("---");
13935
- }
13846
+ const stateManager2 = getStateManager();
13847
+ const { config } = stateManager2.getState();
13848
+ const excludedRepos = config.excludeRepos || [];
13849
+ const aiPolicyBlocklist = config.aiPolicyBlocklist ?? DEFAULT_CONFIG.aiPolicyBlocklist ?? [];
13850
+ const searchOutput = {
13851
+ candidates: candidates.map((c) => {
13852
+ const repoScoreRecord = stateManager2.getRepoScore(c.issue.repo);
13853
+ return {
13854
+ issue: {
13855
+ repo: c.issue.repo,
13856
+ number: c.issue.number,
13857
+ title: c.issue.title,
13858
+ url: c.issue.url,
13859
+ labels: c.issue.labels
13860
+ },
13861
+ recommendation: c.recommendation,
13862
+ reasonsToApprove: c.reasonsToApprove,
13863
+ reasonsToSkip: c.reasonsToSkip,
13864
+ searchPriority: c.searchPriority,
13865
+ viabilityScore: c.viabilityScore,
13866
+ repoScore: repoScoreRecord ? {
13867
+ score: repoScoreRecord.score,
13868
+ mergedPRCount: repoScoreRecord.mergedPRCount,
13869
+ closedWithoutMergeCount: repoScoreRecord.closedWithoutMergeCount,
13870
+ isResponsive: repoScoreRecord.signals?.isResponsive ?? false,
13871
+ lastMergedAt: repoScoreRecord.lastMergedAt
13872
+ } : void 0
13873
+ };
13874
+ }),
13875
+ excludedRepos,
13876
+ aiPolicyBlocklist
13877
+ };
13878
+ if (discovery.rateLimitWarning) {
13879
+ searchOutput.rateLimitWarning = discovery.rateLimitWarning;
13936
13880
  }
13881
+ return searchOutput;
13937
13882
  }
13938
13883
  var init_search = __esm({
13939
13884
  "src/commands/search.ts"() {
13940
13885
  "use strict";
13941
13886
  init_core();
13942
- init_json();
13943
13887
  }
13944
13888
  });
13945
13889
 
13946
13890
  // src/commands/validation.ts
13947
- function validateGitHubUrl(url, pattern, entityType, json) {
13891
+ function validateGitHubUrl(url, pattern, entityType) {
13948
13892
  if (pattern.test(url)) return;
13949
13893
  const example = entityType === "PR" ? "https://github.com/owner/repo/pull/123" : "https://github.com/owner/repo/issues/123";
13950
- const msg = `Invalid ${entityType} URL: ${url}. Expected format: ${example}`;
13951
- if (json) {
13952
- outputJsonError(msg);
13953
- } else {
13954
- console.error(`Error: ${msg}`);
13955
- }
13956
- process.exit(1);
13894
+ throw new Error(`Invalid ${entityType} URL: ${url}. Expected format: ${example}`);
13957
13895
  }
13958
13896
  function validateUrl(url) {
13959
13897
  if (url.length > MAX_URL_LENGTH) {
@@ -13996,7 +13934,6 @@ var init_validation = __esm({
13996
13934
  "src/commands/validation.ts"() {
13997
13935
  "use strict";
13998
13936
  init_errors();
13999
- init_json();
14000
13937
  PR_URL_PATTERN = /^https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+$/;
14001
13938
  ISSUE_URL_PATTERN = /^https:\/\/github\.com\/[^/]+\/[^/]+\/issues\/\d+$/;
14002
13939
  MAX_URL_LENGTH = 2048;
@@ -14014,38 +13951,28 @@ __export(vet_exports, {
14014
13951
  });
14015
13952
  async function runVet(options) {
14016
13953
  validateUrl(options.issueUrl);
14017
- const token = getGitHubToken();
13954
+ const token = requireGitHubToken();
14018
13955
  const discovery = new IssueDiscovery(token);
14019
- if (!options.json) {
14020
- console.log(`
14021
- \u{1F50D} Vetting issue: ${options.issueUrl}
14022
- `);
14023
- }
14024
13956
  const candidate = await discovery.vetIssue(options.issueUrl);
14025
- if (options.json) {
14026
- outputJson({
14027
- issue: {
14028
- repo: candidate.issue.repo,
14029
- number: candidate.issue.number,
14030
- title: candidate.issue.title,
14031
- url: candidate.issue.url,
14032
- labels: candidate.issue.labels
14033
- },
14034
- recommendation: candidate.recommendation,
14035
- reasonsToApprove: candidate.reasonsToApprove,
14036
- reasonsToSkip: candidate.reasonsToSkip,
14037
- projectHealth: candidate.projectHealth,
14038
- vettingResult: candidate.vettingResult
14039
- });
14040
- } else {
14041
- console.log(discovery.formatCandidate(candidate));
14042
- }
13957
+ return {
13958
+ issue: {
13959
+ repo: candidate.issue.repo,
13960
+ number: candidate.issue.number,
13961
+ title: candidate.issue.title,
13962
+ url: candidate.issue.url,
13963
+ labels: candidate.issue.labels
13964
+ },
13965
+ recommendation: candidate.recommendation,
13966
+ reasonsToApprove: candidate.reasonsToApprove,
13967
+ reasonsToSkip: candidate.reasonsToSkip,
13968
+ projectHealth: candidate.projectHealth,
13969
+ vettingResult: candidate.vettingResult
13970
+ };
14043
13971
  }
14044
13972
  var init_vet = __esm({
14045
13973
  "src/commands/vet.ts"() {
14046
13974
  "use strict";
14047
13975
  init_core();
14048
- init_json();
14049
13976
  init_validation();
14050
13977
  }
14051
13978
  });
@@ -14058,59 +13985,37 @@ __export(track_exports, {
14058
13985
  });
14059
13986
  async function runTrack(options) {
14060
13987
  validateUrl(options.prUrl);
14061
- validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR", options.json);
14062
- const token = getGitHubToken();
13988
+ validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR");
13989
+ const token = requireGitHubToken();
14063
13990
  const octokit = getOctokit(token);
14064
13991
  const parsed = parseGitHubUrl(options.prUrl);
14065
13992
  if (!parsed || parsed.type !== "pull") {
14066
- if (options.json) {
14067
- outputJsonError(`Invalid PR URL: ${options.prUrl}`);
14068
- } else {
14069
- console.error(`Error: Invalid PR URL: ${options.prUrl}`);
14070
- }
14071
- process.exit(1);
13993
+ throw new Error(`Invalid PR URL: ${options.prUrl}`);
14072
13994
  }
14073
13995
  const { owner, repo, number } = parsed;
14074
- if (!options.json) {
14075
- console.log(`
14076
- \u{1F4CC} Fetching PR: ${options.prUrl}
14077
- `);
14078
- }
14079
13996
  const { data: ghPR } = await octokit.pulls.get({ owner, repo, pull_number: number });
14080
- const pr = {
14081
- repo: `${owner}/${repo}`,
14082
- number,
14083
- title: ghPR.title,
14084
- url: options.prUrl
13997
+ return {
13998
+ pr: {
13999
+ repo: `${owner}/${repo}`,
14000
+ number,
14001
+ title: ghPR.title,
14002
+ url: options.prUrl
14003
+ }
14085
14004
  };
14086
- if (options.json) {
14087
- outputJson({ pr });
14088
- } else {
14089
- console.log(`PR: ${pr.repo}#${pr.number} - ${pr.title}`);
14090
- console.log("Note: In v2, PRs are tracked automatically via the daily run.");
14091
- }
14092
14005
  }
14093
14006
  async function runUntrack(options) {
14094
14007
  validateUrl(options.prUrl);
14095
- validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR", options.json);
14096
- if (options.json) {
14097
- outputJson({
14098
- removed: false,
14099
- url: options.prUrl,
14100
- message: "In v2, PRs are fetched fresh on each daily run \u2014 there is no local tracking list to remove from."
14101
- });
14102
- } else {
14103
- console.log(
14104
- "Note: In v2, PRs are fetched fresh on each daily run \u2014 there is no local tracking list to remove from."
14105
- );
14106
- console.log("Use `shelve` to temporarily hide a PR from the daily summary.");
14107
- }
14008
+ validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR");
14009
+ return {
14010
+ removed: false,
14011
+ url: options.prUrl,
14012
+ message: "In v2, PRs are fetched fresh on each daily run \u2014 there is no local tracking list to remove from."
14013
+ };
14108
14014
  }
14109
14015
  var init_track = __esm({
14110
14016
  "src/commands/track.ts"() {
14111
14017
  "use strict";
14112
14018
  init_core();
14113
- init_json();
14114
14019
  init_validation();
14115
14020
  init_utils();
14116
14021
  }
@@ -14123,30 +14028,19 @@ __export(read_exports, {
14123
14028
  });
14124
14029
  async function runRead(options) {
14125
14030
  if (!options.all && !options.prUrl) {
14126
- if (options.json) {
14127
- outputJsonError("PR URL or --all flag required");
14128
- } else {
14129
- console.error("Usage: oss-autopilot read <pr-url> or oss-autopilot read --all");
14130
- }
14131
- process.exit(1);
14031
+ throw new Error("PR URL or --all flag required");
14132
14032
  }
14133
14033
  if (options.prUrl) {
14134
14034
  validateUrl(options.prUrl);
14135
14035
  }
14136
- if (options.json) {
14137
- if (options.all) {
14138
- outputJson({ markedAsRead: 0, all: true, message: "In v2, PR read state is not tracked locally." });
14139
- } else {
14140
- outputJson({ marked: false, url: options.prUrl, message: "In v2, PR read state is not tracked locally." });
14141
- }
14142
- } else {
14143
- console.log("Note: In v2, PR read state is not tracked locally. PRs are fetched fresh on each daily run.");
14036
+ if (options.all) {
14037
+ return { markedAsRead: 0, all: true, message: "In v2, PR read state is not tracked locally." };
14144
14038
  }
14039
+ return { marked: false, url: options.prUrl, message: "In v2, PR read state is not tracked locally." };
14145
14040
  }
14146
14041
  var init_read = __esm({
14147
14042
  "src/commands/read.ts"() {
14148
14043
  "use strict";
14149
- init_json();
14150
14044
  init_validation();
14151
14045
  }
14152
14046
  });
@@ -14160,17 +14054,12 @@ __export(comments_exports, {
14160
14054
  });
14161
14055
  async function runComments(options) {
14162
14056
  validateUrl(options.prUrl);
14163
- const token = getGitHubToken();
14057
+ const token = requireGitHubToken();
14164
14058
  const stateManager2 = getStateManager();
14165
14059
  const octokit = getOctokit(token);
14166
14060
  const parsed = parseGitHubUrl(options.prUrl);
14167
14061
  if (!parsed || parsed.type !== "pull") {
14168
- if (options.json) {
14169
- outputJsonError("Invalid PR URL format");
14170
- } else {
14171
- console.error("Invalid PR URL format");
14172
- }
14173
- process.exit(1);
14062
+ throw new Error("Invalid PR URL format");
14174
14063
  }
14175
14064
  const { owner, repo, number: pull_number } = parsed;
14176
14065
  const { data: pr } = await octokit.pulls.get({ owner, repo, pull_number });
@@ -14211,240 +14100,104 @@ async function runComments(options) {
14211
14100
  const relevantReviewComments = reviewComments.filter(filterComment).sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
14212
14101
  const relevantIssueComments = issueComments.filter(filterComment).sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
14213
14102
  const relevantReviews = reviews.filter((r) => filterComment(r) && r.body && r.body.trim()).sort((a, b) => new Date(b.submitted_at || 0).getTime() - new Date(a.submitted_at || 0).getTime());
14214
- if (options.json) {
14215
- outputJson({
14216
- pr: {
14217
- title: pr.title,
14218
- state: pr.state,
14219
- mergeable: pr.mergeable,
14220
- head: pr.head.ref,
14221
- base: pr.base.ref,
14222
- url: pr.html_url
14223
- },
14224
- reviews: relevantReviews.map((r) => ({
14225
- user: r.user?.login,
14226
- state: r.state,
14227
- body: r.body,
14228
- submittedAt: r.submitted_at
14229
- })),
14230
- reviewComments: relevantReviewComments.map((c) => ({
14231
- user: c.user?.login,
14232
- body: c.body,
14233
- path: c.path,
14234
- createdAt: c.created_at
14235
- })),
14236
- issueComments: relevantIssueComments.map((c) => ({
14237
- user: c.user?.login,
14238
- body: c.body,
14239
- createdAt: c.created_at
14240
- })),
14241
- summary: {
14242
- reviewCount: relevantReviews.length,
14243
- inlineCommentCount: relevantReviewComments.length,
14244
- discussionCommentCount: relevantIssueComments.length
14245
- }
14246
- });
14247
- return;
14248
- }
14249
- console.log(`
14250
- \u{1F4AC} Fetching comments for: ${options.prUrl}
14251
- `);
14252
- console.log(`## ${pr.title}
14253
- `);
14254
- console.log(`**Status:** ${pr.state} | **Mergeable:** ${pr.mergeable ?? "checking..."}`);
14255
- console.log(`**Branch:** ${pr.head.ref} \u2192 ${pr.base.ref}`);
14256
- console.log(`**URL:** ${pr.html_url}
14257
- `);
14258
- if (relevantReviews.length > 0) {
14259
- console.log("### Reviews (newest first)\n");
14260
- for (const review of relevantReviews) {
14261
- const state = review.state === "APPROVED" ? "\u2705" : review.state === "CHANGES_REQUESTED" ? "\u274C" : "\u{1F4AC}";
14262
- const time = review.submitted_at ? formatRelativeTime(review.submitted_at) : "";
14263
- console.log(`${state} **@${review.user?.login}** (${review.state}) - ${time}`);
14264
- if (review.body) {
14265
- console.log(`> ${review.body.split("\n").join("\n> ")}
14266
- `);
14267
- }
14268
- }
14269
- }
14270
- if (relevantReviewComments.length > 0) {
14271
- console.log("### Inline Comments (newest first)\n");
14272
- for (const comment of relevantReviewComments) {
14273
- const time = formatRelativeTime(comment.created_at);
14274
- console.log(`**@${comment.user?.login}** on \`${comment.path}\` - ${time}`);
14275
- console.log(`> ${comment.body.split("\n").join("\n> ")}`);
14276
- if (comment.diff_hunk) {
14277
- console.log(`\`\`\`diff
14278
- ${comment.diff_hunk.slice(-500)}
14279
- \`\`\``);
14280
- }
14281
- console.log("");
14282
- }
14283
- }
14284
- if (relevantIssueComments.length > 0) {
14285
- console.log("### Discussion (newest first)\n");
14286
- for (const comment of relevantIssueComments) {
14287
- const time = formatRelativeTime(comment.created_at);
14288
- console.log(`**@${comment.user?.login}** - ${time}`);
14289
- console.log(`> ${comment.body?.split("\n").join("\n> ")}
14290
- `);
14103
+ return {
14104
+ pr: {
14105
+ title: pr.title,
14106
+ state: pr.state,
14107
+ mergeable: pr.mergeable,
14108
+ head: pr.head.ref,
14109
+ base: pr.base.ref,
14110
+ url: pr.html_url
14111
+ },
14112
+ reviews: relevantReviews.map((r) => ({
14113
+ user: r.user?.login,
14114
+ state: r.state,
14115
+ body: r.body ?? null,
14116
+ submittedAt: r.submitted_at ?? null
14117
+ })),
14118
+ reviewComments: relevantReviewComments.map((c) => ({
14119
+ user: c.user?.login,
14120
+ body: c.body,
14121
+ path: c.path,
14122
+ createdAt: c.created_at
14123
+ })),
14124
+ issueComments: relevantIssueComments.map((c) => ({
14125
+ user: c.user?.login,
14126
+ body: c.body,
14127
+ createdAt: c.created_at
14128
+ })),
14129
+ summary: {
14130
+ reviewCount: relevantReviews.length,
14131
+ inlineCommentCount: relevantReviewComments.length,
14132
+ discussionCommentCount: relevantIssueComments.length
14291
14133
  }
14292
- }
14293
- if (relevantReviewComments.length === 0 && relevantIssueComments.length === 0 && relevantReviews.length === 0) {
14294
- console.log("No comments from other users.\n");
14295
- }
14296
- console.log("---");
14297
- console.log(
14298
- `**Summary:** ${relevantReviews.length} reviews, ${relevantReviewComments.length} inline comments, ${relevantIssueComments.length} discussion comments`
14299
- );
14134
+ };
14300
14135
  }
14301
14136
  async function runPost(options) {
14302
14137
  validateUrl(options.url);
14303
- const token = getGitHubToken();
14304
- let message = options.message;
14305
- if (options.stdin) {
14306
- const chunks = [];
14307
- for await (const chunk of process.stdin) {
14308
- chunks.push(chunk);
14309
- }
14310
- message = Buffer.concat(chunks).toString("utf-8").trim();
14311
- }
14312
- if (!message) {
14313
- if (options.json) {
14314
- outputJsonError("No message provided");
14315
- } else {
14316
- console.error("Error: No message provided");
14317
- }
14318
- process.exit(1);
14319
- }
14320
- try {
14321
- validateMessage(message);
14322
- } catch (error) {
14323
- if (options.json) {
14324
- outputJsonError(error instanceof Error ? error.message : "Invalid message");
14325
- } else {
14326
- console.error(`Error: ${error instanceof Error ? error.message : "Invalid message"}`);
14327
- }
14328
- process.exit(1);
14138
+ if (!options.message.trim()) {
14139
+ throw new Error("No message provided");
14329
14140
  }
14141
+ validateMessage(options.message);
14142
+ const token = requireGitHubToken();
14330
14143
  const parsed = parseGitHubUrl(options.url);
14331
14144
  if (!parsed) {
14332
- if (options.json) {
14333
- outputJsonError("Invalid GitHub URL format");
14334
- } else {
14335
- console.error("Invalid GitHub URL format");
14336
- }
14337
- process.exit(1);
14145
+ throw new Error("Invalid GitHub URL format");
14338
14146
  }
14339
14147
  const { owner, repo, number } = parsed;
14340
14148
  const octokit = getOctokit(token);
14341
- if (!options.json) {
14342
- console.log("\n\u{1F4DD} Posting comment to:", options.url);
14343
- console.log("---");
14344
- console.log(message);
14345
- console.log("---\n");
14346
- }
14347
- try {
14348
- const { data: comment } = await octokit.issues.createComment({
14349
- owner,
14350
- repo,
14351
- issue_number: number,
14352
- body: message
14353
- });
14354
- if (options.json) {
14355
- outputJson({
14356
- commentUrl: comment.html_url,
14357
- url: options.url
14358
- });
14359
- } else {
14360
- console.log("\u2705 Comment posted successfully!");
14361
- console.log(` ${comment.html_url}`);
14362
- }
14363
- } catch (error) {
14364
- if (options.json) {
14365
- outputJsonError(error instanceof Error ? error.message : "Unknown error");
14366
- } else {
14367
- console.error("\u274C Failed to post comment:", error instanceof Error ? error.message : error);
14368
- }
14369
- process.exit(1);
14370
- }
14149
+ const { data: comment } = await octokit.issues.createComment({
14150
+ owner,
14151
+ repo,
14152
+ issue_number: number,
14153
+ body: options.message
14154
+ });
14155
+ return {
14156
+ commentUrl: comment.html_url,
14157
+ url: options.url
14158
+ };
14371
14159
  }
14372
14160
  async function runClaim(options) {
14373
14161
  validateUrl(options.issueUrl);
14374
- const token = getGitHubToken();
14162
+ const token = requireGitHubToken();
14375
14163
  const message = options.message || "Hi! I'd like to work on this issue. Could you assign it to me?";
14376
- try {
14377
- validateMessage(message);
14378
- } catch (error) {
14379
- if (options.json) {
14380
- outputJsonError(error instanceof Error ? error.message : "Invalid message");
14381
- } else {
14382
- console.error(`Error: ${error instanceof Error ? error.message : "Invalid message"}`);
14383
- }
14384
- process.exit(1);
14385
- }
14164
+ validateMessage(message);
14386
14165
  const parsed = parseGitHubUrl(options.issueUrl);
14387
14166
  if (!parsed || parsed.type !== "issues") {
14388
- if (options.json) {
14389
- outputJsonError("Invalid issue URL format (must be an issue, not a PR)");
14390
- } else {
14391
- console.error("Invalid issue URL format (must be an issue, not a PR)");
14392
- }
14393
- process.exit(1);
14167
+ throw new Error("Invalid issue URL format (must be an issue, not a PR)");
14394
14168
  }
14395
14169
  const { owner, repo, number } = parsed;
14396
- if (!options.json) {
14397
- console.log("\n\u{1F64B} Claiming issue:", options.issueUrl);
14398
- console.log("---");
14399
- console.log(message);
14400
- console.log("---\n");
14401
- }
14402
14170
  const octokit = getOctokit(token);
14403
- try {
14404
- const { data: comment } = await octokit.issues.createComment({
14405
- owner,
14406
- repo,
14407
- issue_number: number,
14408
- body: message
14409
- });
14410
- const stateManager2 = getStateManager();
14411
- stateManager2.addIssue({
14412
- id: number,
14413
- url: options.issueUrl,
14414
- repo: `${owner}/${repo}`,
14415
- number,
14416
- title: "(claimed)",
14417
- status: "claimed",
14418
- labels: [],
14419
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
14420
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
14421
- vetted: false
14422
- });
14423
- stateManager2.save();
14424
- if (options.json) {
14425
- outputJson({
14426
- commentUrl: comment.html_url,
14427
- issueUrl: options.issueUrl
14428
- });
14429
- } else {
14430
- console.log("\u2705 Issue claimed!");
14431
- console.log(` ${comment.html_url}`);
14432
- }
14433
- } catch (error) {
14434
- if (options.json) {
14435
- outputJsonError(error instanceof Error ? error.message : "Unknown error");
14436
- } else {
14437
- console.error("\u274C Failed to claim issue:", error instanceof Error ? error.message : error);
14438
- }
14439
- process.exit(1);
14440
- }
14171
+ const { data: comment } = await octokit.issues.createComment({
14172
+ owner,
14173
+ repo,
14174
+ issue_number: number,
14175
+ body: message
14176
+ });
14177
+ const stateManager2 = getStateManager();
14178
+ stateManager2.addIssue({
14179
+ id: number,
14180
+ url: options.issueUrl,
14181
+ repo: `${owner}/${repo}`,
14182
+ number,
14183
+ title: "(claimed)",
14184
+ status: "claimed",
14185
+ labels: [],
14186
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
14187
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
14188
+ vetted: false
14189
+ });
14190
+ stateManager2.save();
14191
+ return {
14192
+ commentUrl: comment.html_url,
14193
+ issueUrl: options.issueUrl
14194
+ };
14441
14195
  }
14442
14196
  var init_comments = __esm({
14443
14197
  "src/commands/comments.ts"() {
14444
14198
  "use strict";
14445
14199
  init_core();
14446
14200
  init_pagination();
14447
- init_json();
14448
14201
  init_validation();
14449
14202
  }
14450
14203
  });
@@ -14454,28 +14207,14 @@ var config_exports = {};
14454
14207
  __export(config_exports, {
14455
14208
  runConfig: () => runConfig
14456
14209
  });
14457
- function exitWithError(msg, json) {
14458
- if (json) {
14459
- outputJsonError(msg);
14460
- } else {
14461
- console.error(msg);
14462
- }
14463
- process.exit(1);
14464
- }
14465
14210
  async function runConfig(options) {
14466
14211
  const stateManager2 = getStateManager();
14467
14212
  const currentConfig = stateManager2.getState().config;
14468
14213
  if (!options.key) {
14469
- if (options.json) {
14470
- outputJson({ config: currentConfig });
14471
- } else {
14472
- console.log("\n\u2699\uFE0F Current Configuration:\n");
14473
- console.log(JSON.stringify(currentConfig, null, 2));
14474
- }
14475
- return;
14214
+ return { config: currentConfig };
14476
14215
  }
14477
14216
  if (!options.value) {
14478
- exitWithError("Value required", options.json);
14217
+ throw new Error("Value required");
14479
14218
  }
14480
14219
  const value = options.value;
14481
14220
  switch (options.key) {
@@ -14495,9 +14234,8 @@ async function runConfig(options) {
14495
14234
  case "exclude-repo": {
14496
14235
  const parts = value.split("/");
14497
14236
  if (parts.length !== 2 || !parts[0] || !parts[1]) {
14498
- exitWithError(
14499
- `Invalid repo format "${value}". Use "owner/repo" format. To exclude an entire org, use: config exclude-org ${value}`,
14500
- options.json
14237
+ throw new Error(
14238
+ `Invalid repo format "${value}". Use "owner/repo" format. To exclude an entire org, use: config exclude-org ${value}`
14501
14239
  );
14502
14240
  }
14503
14241
  const valueLower = value.toLowerCase();
@@ -14509,9 +14247,8 @@ async function runConfig(options) {
14509
14247
  }
14510
14248
  case "exclude-org": {
14511
14249
  if (value.includes("/")) {
14512
- exitWithError(
14513
- `Invalid org name "${value}". Use just the org name (e.g., "facebook"), not "owner/repo" format. To exclude a specific repo, use: config exclude-repo ${value}`,
14514
- options.json
14250
+ throw new Error(
14251
+ `Invalid org name "${value}". Use just the org name (e.g., "facebook"), not "owner/repo" format. To exclude a specific repo, use: config exclude-repo ${value}`
14515
14252
  );
14516
14253
  }
14517
14254
  const currentOrgs = currentConfig.excludeOrgs ?? [];
@@ -14522,20 +14259,15 @@ async function runConfig(options) {
14522
14259
  break;
14523
14260
  }
14524
14261
  default:
14525
- exitWithError(`Unknown config key: ${options.key}`, options.json);
14262
+ throw new Error(`Unknown config key: ${options.key}`);
14526
14263
  }
14527
14264
  stateManager2.save();
14528
- if (options.json) {
14529
- outputJson({ success: true, key: options.key, value });
14530
- } else {
14531
- console.log(`Set ${options.key} to: ${value}`);
14532
- }
14265
+ return { success: true, key: options.key, value };
14533
14266
  }
14534
14267
  var init_config = __esm({
14535
14268
  "src/commands/config.ts"() {
14536
14269
  "use strict";
14537
14270
  init_core();
14538
- init_json();
14539
14271
  }
14540
14272
  });
14541
14273
 
@@ -14547,28 +14279,17 @@ __export(init_exports, {
14547
14279
  async function runInit(options) {
14548
14280
  validateGitHubUsername(options.username);
14549
14281
  const stateManager2 = getStateManager();
14550
- if (!options.json) {
14551
- console.log(`
14552
- \u{1F680} Initializing for @${options.username}...
14553
- `);
14554
- }
14555
14282
  stateManager2.updateConfig({ githubUsername: options.username });
14556
14283
  stateManager2.save();
14557
- if (options.json) {
14558
- outputJson({
14559
- username: options.username,
14560
- message: "Username saved. Run `daily` to fetch your open PRs from GitHub."
14561
- });
14562
- } else {
14563
- console.log(`Username set to @${options.username}.`);
14564
- console.log("Run `oss-autopilot daily` to fetch your open PRs from GitHub.");
14565
- }
14284
+ return {
14285
+ username: options.username,
14286
+ message: "Username saved. Run `daily` to fetch your open PRs from GitHub."
14287
+ };
14566
14288
  }
14567
14289
  var init_init = __esm({
14568
14290
  "src/commands/init.ts"() {
14569
14291
  "use strict";
14570
14292
  init_core();
14571
- init_json();
14572
14293
  init_validation();
14573
14294
  }
14574
14295
  });
@@ -14584,6 +14305,7 @@ async function runSetup(options) {
14584
14305
  const config = stateManager2.getState().config;
14585
14306
  if (options.set && options.set.length > 0) {
14586
14307
  const results = {};
14308
+ const warnings = [];
14587
14309
  for (const setting of options.set) {
14588
14310
  const [key, ...valueParts] = setting.split("=");
14589
14311
  const value = valueParts.join("=");
@@ -14649,15 +14371,11 @@ async function runSetup(options) {
14649
14371
  }
14650
14372
  }
14651
14373
  if (invalid.length > 0) {
14652
- if (!options.json) {
14653
- console.warn(`Warning: Skipping invalid entries (expected "owner/repo" format): ${invalid.join(", ")}`);
14654
- }
14374
+ warnings.push(`Warning: Skipping invalid entries (expected "owner/repo" format): ${invalid.join(", ")}`);
14655
14375
  results["aiPolicyBlocklist_invalidEntries"] = invalid.join(", ");
14656
14376
  }
14657
14377
  if (valid.length === 0 && entries.length > 0) {
14658
- if (!options.json) {
14659
- console.warn("Warning: All entries were invalid. Blocklist not updated.");
14660
- }
14378
+ warnings.push("Warning: All entries were invalid. Blocklist not updated.");
14661
14379
  results[key] = "(all entries invalid)";
14662
14380
  break;
14663
14381
  }
@@ -14672,175 +14390,91 @@ async function runSetup(options) {
14672
14390
  }
14673
14391
  break;
14674
14392
  default:
14675
- if (!options.json) {
14676
- console.warn(`Unknown setting: ${key}`);
14677
- }
14393
+ warnings.push(`Unknown setting: ${key}`);
14678
14394
  }
14679
14395
  }
14680
14396
  stateManager2.save();
14681
- if (options.json) {
14682
- outputJson({ success: true, settings: results });
14683
- } else {
14684
- for (const [key, value] of Object.entries(results)) {
14685
- console.log(`\u2713 ${key}: ${value}`);
14686
- }
14687
- }
14688
- return;
14397
+ return { success: true, settings: results, warnings: warnings.length > 0 ? warnings : void 0 };
14689
14398
  }
14690
14399
  if (config.setupComplete && !options.reset) {
14691
- if (options.json) {
14692
- outputJson({
14693
- setupComplete: true,
14694
- config: {
14695
- githubUsername: config.githubUsername,
14696
- maxActivePRs: config.maxActivePRs,
14697
- dormantThresholdDays: config.dormantThresholdDays,
14698
- approachingDormantDays: config.approachingDormantDays,
14699
- languages: config.languages,
14700
- labels: config.labels
14701
- }
14702
- });
14703
- } else {
14704
- console.log("\n\u2699\uFE0F OSS Autopilot Setup\n");
14705
- console.log("\u2713 Setup already complete!\n");
14706
- console.log("Current settings:");
14707
- console.log(` GitHub username: ${config.githubUsername || "(not set)"}`);
14708
- console.log(` Max active PRs: ${config.maxActivePRs}`);
14709
- console.log(` Dormant threshold: ${config.dormantThresholdDays} days`);
14710
- console.log(` Approaching dormant: ${config.approachingDormantDays} days`);
14711
- console.log(` Languages: ${config.languages.join(", ")}`);
14712
- console.log(` Labels: ${config.labels.join(", ")}`);
14713
- console.log(`
14714
- Run 'setup --reset' to reconfigure.`);
14715
- }
14716
- return;
14717
- }
14718
- if (options.json) {
14719
- outputJson({
14720
- setupRequired: true,
14721
- prompts: [
14722
- {
14723
- setting: "username",
14724
- prompt: "What is your GitHub username?",
14725
- current: config.githubUsername || null,
14726
- required: true,
14727
- type: "string"
14728
- },
14729
- {
14730
- setting: "maxActivePRs",
14731
- prompt: "How many PRs do you want to work on at once?",
14732
- current: config.maxActivePRs,
14733
- default: 10,
14734
- type: "number"
14735
- },
14736
- {
14737
- setting: "dormantDays",
14738
- prompt: "After how many days of inactivity should a PR be considered dormant?",
14739
- current: config.dormantThresholdDays,
14740
- default: 30,
14741
- type: "number"
14742
- },
14743
- {
14744
- setting: "approachingDays",
14745
- prompt: "At how many days should we warn about approaching dormancy?",
14746
- current: config.approachingDormantDays,
14747
- default: 25,
14748
- type: "number"
14749
- },
14750
- {
14751
- setting: "languages",
14752
- prompt: "What programming languages do you want to contribute to?",
14753
- current: config.languages,
14754
- default: ["typescript", "javascript"],
14755
- type: "list"
14756
- },
14757
- {
14758
- setting: "labels",
14759
- prompt: "What issue labels should we search for?",
14760
- current: config.labels,
14761
- default: ["good first issue", "help wanted"],
14762
- type: "list"
14763
- },
14764
- {
14765
- setting: "aiPolicyBlocklist",
14766
- prompt: "Repos with anti-AI contribution policies to block (owner/repo, comma-separated)?",
14767
- current: config.aiPolicyBlocklist ?? DEFAULT_CONFIG.aiPolicyBlocklist,
14768
- default: ["matplotlib/matplotlib"],
14769
- type: "list"
14770
- }
14771
- ]
14772
- });
14773
- } else {
14774
- console.log("\n\u2699\uFE0F OSS Autopilot Setup\n");
14775
- console.log("SETUP_REQUIRED");
14776
- console.log("---");
14777
- console.log("Please configure the following settings:\n");
14778
- console.log("SETTING: username");
14779
- console.log("PROMPT: What is your GitHub username?");
14780
- console.log(`CURRENT: ${config.githubUsername || "(not set)"}`);
14781
- console.log("REQUIRED: true");
14782
- console.log("");
14783
- console.log("SETTING: maxActivePRs");
14784
- console.log("PROMPT: How many PRs do you want to work on at once?");
14785
- console.log(`CURRENT: ${config.maxActivePRs}`);
14786
- console.log("DEFAULT: 10");
14787
- console.log("TYPE: number");
14788
- console.log("");
14789
- console.log("SETTING: dormantDays");
14790
- console.log("PROMPT: After how many days of inactivity should a PR be considered dormant?");
14791
- console.log(`CURRENT: ${config.dormantThresholdDays}`);
14792
- console.log("DEFAULT: 30");
14793
- console.log("TYPE: number");
14794
- console.log("");
14795
- console.log("SETTING: approachingDays");
14796
- console.log("PROMPT: At how many days should we warn about approaching dormancy?");
14797
- console.log(`CURRENT: ${config.approachingDormantDays}`);
14798
- console.log("DEFAULT: 25");
14799
- console.log("TYPE: number");
14800
- console.log("");
14801
- console.log("SETTING: languages");
14802
- console.log("PROMPT: What programming languages do you want to contribute to? (comma-separated)");
14803
- console.log(`CURRENT: ${config.languages.join(", ")}`);
14804
- console.log("DEFAULT: typescript, javascript");
14805
- console.log("TYPE: list");
14806
- console.log("");
14807
- console.log("SETTING: labels");
14808
- console.log("PROMPT: What issue labels should we search for? (comma-separated)");
14809
- console.log(`CURRENT: ${config.labels.join(", ")}`);
14810
- console.log("DEFAULT: good first issue, help wanted");
14811
- console.log("TYPE: list");
14812
- console.log("");
14813
- console.log("SETTING: aiPolicyBlocklist");
14814
- console.log("PROMPT: Repos with anti-AI contribution policies to block? (owner/repo, comma-separated)");
14815
- console.log(`CURRENT: ${(config.aiPolicyBlocklist ?? DEFAULT_CONFIG.aiPolicyBlocklist ?? []).join(", ")}`);
14816
- console.log("DEFAULT: matplotlib/matplotlib");
14817
- console.log("TYPE: list");
14818
- console.log("");
14819
- console.log("---");
14820
- console.log("END_SETUP_PROMPTS");
14400
+ return {
14401
+ setupComplete: true,
14402
+ config: {
14403
+ githubUsername: config.githubUsername,
14404
+ maxActivePRs: config.maxActivePRs,
14405
+ dormantThresholdDays: config.dormantThresholdDays,
14406
+ approachingDormantDays: config.approachingDormantDays,
14407
+ languages: config.languages,
14408
+ labels: config.labels
14409
+ }
14410
+ };
14821
14411
  }
14412
+ return {
14413
+ setupRequired: true,
14414
+ prompts: [
14415
+ {
14416
+ setting: "username",
14417
+ prompt: "What is your GitHub username?",
14418
+ current: config.githubUsername || null,
14419
+ required: true,
14420
+ type: "string"
14421
+ },
14422
+ {
14423
+ setting: "maxActivePRs",
14424
+ prompt: "How many PRs do you want to work on at once?",
14425
+ current: config.maxActivePRs,
14426
+ default: 10,
14427
+ type: "number"
14428
+ },
14429
+ {
14430
+ setting: "dormantDays",
14431
+ prompt: "After how many days of inactivity should a PR be considered dormant?",
14432
+ current: config.dormantThresholdDays,
14433
+ default: 30,
14434
+ type: "number"
14435
+ },
14436
+ {
14437
+ setting: "approachingDays",
14438
+ prompt: "At how many days should we warn about approaching dormancy?",
14439
+ current: config.approachingDormantDays,
14440
+ default: 25,
14441
+ type: "number"
14442
+ },
14443
+ {
14444
+ setting: "languages",
14445
+ prompt: "What programming languages do you want to contribute to?",
14446
+ current: config.languages,
14447
+ default: ["typescript", "javascript"],
14448
+ type: "list"
14449
+ },
14450
+ {
14451
+ setting: "labels",
14452
+ prompt: "What issue labels should we search for?",
14453
+ current: config.labels,
14454
+ default: ["good first issue", "help wanted"],
14455
+ type: "list"
14456
+ },
14457
+ {
14458
+ setting: "aiPolicyBlocklist",
14459
+ prompt: "Repos with anti-AI contribution policies to block (owner/repo, comma-separated)?",
14460
+ current: config.aiPolicyBlocklist ?? DEFAULT_CONFIG.aiPolicyBlocklist ?? null,
14461
+ default: ["matplotlib/matplotlib"],
14462
+ type: "list"
14463
+ }
14464
+ ]
14465
+ };
14822
14466
  }
14823
- async function runCheckSetup(options) {
14467
+ async function runCheckSetup() {
14824
14468
  const stateManager2 = getStateManager();
14825
- if (options.json) {
14826
- outputJson({
14827
- setupComplete: stateManager2.isSetupComplete(),
14828
- username: stateManager2.getState().config.githubUsername
14829
- });
14830
- } else {
14831
- if (stateManager2.isSetupComplete()) {
14832
- console.log("SETUP_COMPLETE");
14833
- console.log(`username=${stateManager2.getState().config.githubUsername}`);
14834
- } else {
14835
- console.log("SETUP_INCOMPLETE");
14836
- }
14837
- }
14469
+ return {
14470
+ setupComplete: stateManager2.isSetupComplete(),
14471
+ username: stateManager2.getState().config.githubUsername
14472
+ };
14838
14473
  }
14839
14474
  var init_setup = __esm({
14840
14475
  "src/commands/setup.ts"() {
14841
14476
  "use strict";
14842
14477
  init_core();
14843
- init_json();
14844
14478
  init_validation();
14845
14479
  }
14846
14480
  });
@@ -16532,10 +16166,325 @@ var init_dashboard_templates = __esm({
16532
16166
  }
16533
16167
  });
16534
16168
 
16169
+ // src/commands/dashboard-server.ts
16170
+ var dashboard_server_exports = {};
16171
+ __export(dashboard_server_exports, {
16172
+ startDashboardServer: () => startDashboardServer
16173
+ });
16174
+ function buildDashboardJson(digest, state, commentedIssues) {
16175
+ const prsByRepo = computePRsByRepo(digest, state);
16176
+ const topRepos = computeTopRepos(prsByRepo);
16177
+ const { monthlyMerged } = getMonthlyData(state);
16178
+ const stats = buildDashboardStats(digest, state);
16179
+ const issueResponses = commentedIssues.filter((i) => i.status === "new_response");
16180
+ return {
16181
+ stats,
16182
+ prsByRepo,
16183
+ topRepos: topRepos.map(([repo, data]) => ({ repo, ...data })),
16184
+ monthlyMerged,
16185
+ activePRs: digest.openPRs || [],
16186
+ shelvedPRUrls: state.config.shelvedPRUrls || [],
16187
+ commentedIssues,
16188
+ issueResponses
16189
+ };
16190
+ }
16191
+ function readBody(req, maxBytes = MAX_BODY_BYTES) {
16192
+ return new Promise((resolve5, reject) => {
16193
+ const chunks = [];
16194
+ let totalLength = 0;
16195
+ let aborted = false;
16196
+ req.on("data", (chunk) => {
16197
+ if (aborted) return;
16198
+ totalLength += chunk.length;
16199
+ if (totalLength > maxBytes) {
16200
+ aborted = true;
16201
+ req.destroy();
16202
+ reject(new Error("Body too large"));
16203
+ return;
16204
+ }
16205
+ chunks.push(chunk);
16206
+ });
16207
+ req.on("end", () => {
16208
+ if (!aborted) resolve5(Buffer.concat(chunks).toString("utf-8"));
16209
+ });
16210
+ req.on("error", (err) => {
16211
+ if (!aborted) reject(err);
16212
+ });
16213
+ });
16214
+ }
16215
+ function sendJson(res, statusCode, data) {
16216
+ const body = JSON.stringify(data);
16217
+ res.writeHead(statusCode, {
16218
+ "Content-Type": "application/json",
16219
+ "Content-Length": Buffer.byteLength(body)
16220
+ });
16221
+ res.end(body);
16222
+ }
16223
+ function sendError(res, statusCode, message) {
16224
+ sendJson(res, statusCode, { error: message });
16225
+ }
16226
+ async function startDashboardServer(options) {
16227
+ const { port: requestedPort, assetsDir, token, open } = options;
16228
+ const stateManager2 = getStateManager();
16229
+ const resolvedAssetsDir = path5.resolve(assetsDir);
16230
+ let cachedDigest;
16231
+ let cachedCommentedIssues = [];
16232
+ if (token) {
16233
+ try {
16234
+ console.error("Fetching dashboard data from GitHub...");
16235
+ const result = await fetchDashboardData(token);
16236
+ cachedDigest = result.digest;
16237
+ cachedCommentedIssues = result.commentedIssues;
16238
+ } catch (error) {
16239
+ console.error("Failed to fetch data from GitHub:", error);
16240
+ console.error("Falling back to cached data...");
16241
+ cachedDigest = stateManager2.getState().lastDigest;
16242
+ }
16243
+ } else {
16244
+ cachedDigest = stateManager2.getState().lastDigest;
16245
+ }
16246
+ if (!cachedDigest) {
16247
+ console.error("No dashboard data available. Run the daily check first:");
16248
+ console.error(" GITHUB_TOKEN=$(gh auth token) npm start -- daily");
16249
+ process.exit(1);
16250
+ }
16251
+ let cachedJsonData;
16252
+ try {
16253
+ cachedJsonData = buildDashboardJson(cachedDigest, stateManager2.getState(), cachedCommentedIssues);
16254
+ } catch (error) {
16255
+ console.error("Failed to build dashboard data from cached digest:", error);
16256
+ console.error("Your state data may be corrupted. Try running: daily --json");
16257
+ process.exit(1);
16258
+ }
16259
+ const server = http.createServer(async (req, res) => {
16260
+ const method = req.method || "GET";
16261
+ const url = req.url || "/";
16262
+ try {
16263
+ if (url === "/api/data" && method === "GET") {
16264
+ sendJson(res, 200, cachedJsonData);
16265
+ return;
16266
+ }
16267
+ if (url === "/api/action" && method === "POST") {
16268
+ await handleAction(req, res);
16269
+ return;
16270
+ }
16271
+ if (url === "/api/refresh" && method === "POST") {
16272
+ await handleRefresh(req, res);
16273
+ return;
16274
+ }
16275
+ if (method === "GET") {
16276
+ serveStaticFile(url, res);
16277
+ return;
16278
+ }
16279
+ sendError(res, 405, "Method not allowed");
16280
+ } catch (error) {
16281
+ console.error("Unhandled request error:", method, url, error);
16282
+ if (!res.headersSent) {
16283
+ sendError(res, 500, "Internal server error");
16284
+ }
16285
+ }
16286
+ });
16287
+ async function handleAction(req, res) {
16288
+ let body;
16289
+ try {
16290
+ const raw = await readBody(req);
16291
+ body = JSON.parse(raw);
16292
+ } catch (e) {
16293
+ const isBodyTooLarge = e instanceof Error && e.message === "Body too large";
16294
+ sendError(res, isBodyTooLarge ? 413 : 400, isBodyTooLarge ? "Request body too large" : "Invalid JSON body");
16295
+ return;
16296
+ }
16297
+ if (!body.action || !VALID_ACTIONS.has(body.action)) {
16298
+ sendError(res, 400, `Invalid action. Must be one of: ${[...VALID_ACTIONS].join(", ")}`);
16299
+ return;
16300
+ }
16301
+ if (!body.url || typeof body.url !== "string") {
16302
+ sendError(res, 400, 'Missing or invalid "url" field');
16303
+ return;
16304
+ }
16305
+ try {
16306
+ switch (body.action) {
16307
+ case "shelve":
16308
+ stateManager2.shelvePR(body.url);
16309
+ break;
16310
+ case "unshelve":
16311
+ stateManager2.unshelvePR(body.url);
16312
+ break;
16313
+ case "snooze":
16314
+ stateManager2.snoozePR(body.url, body.reason || "Snoozed via dashboard", body.days || 7);
16315
+ break;
16316
+ case "unsnooze":
16317
+ stateManager2.unsnoozePR(body.url);
16318
+ break;
16319
+ }
16320
+ stateManager2.save();
16321
+ } catch (error) {
16322
+ console.error("Action failed:", body.action, body.url, error);
16323
+ sendError(res, 500, `Action failed: ${error instanceof Error ? error.message : String(error)}`);
16324
+ return;
16325
+ }
16326
+ if (cachedDigest) {
16327
+ cachedJsonData = buildDashboardJson(cachedDigest, stateManager2.getState(), cachedCommentedIssues);
16328
+ }
16329
+ sendJson(res, 200, cachedJsonData);
16330
+ }
16331
+ async function handleRefresh(_req, res) {
16332
+ const currentToken = token || getGitHubToken();
16333
+ if (!currentToken) {
16334
+ sendError(res, 401, "No GitHub token available. Cannot refresh data.");
16335
+ return;
16336
+ }
16337
+ try {
16338
+ console.error("Refreshing dashboard data from GitHub...");
16339
+ const result = await fetchDashboardData(currentToken);
16340
+ cachedDigest = result.digest;
16341
+ cachedCommentedIssues = result.commentedIssues;
16342
+ cachedJsonData = buildDashboardJson(cachedDigest, stateManager2.getState(), cachedCommentedIssues);
16343
+ sendJson(res, 200, cachedJsonData);
16344
+ } catch (error) {
16345
+ console.error("Dashboard refresh failed:", error);
16346
+ sendError(res, 500, `Refresh failed: ${error instanceof Error ? error.message : String(error)}`);
16347
+ }
16348
+ }
16349
+ function serveStaticFile(requestUrl, res) {
16350
+ let urlPath;
16351
+ try {
16352
+ urlPath = decodeURIComponent(requestUrl.split("?")[0]);
16353
+ } catch (err) {
16354
+ console.error("Malformed URL received:", requestUrl, err);
16355
+ sendError(res, 400, "Malformed URL");
16356
+ return;
16357
+ }
16358
+ if (urlPath.includes("..")) {
16359
+ sendError(res, 403, "Forbidden");
16360
+ return;
16361
+ }
16362
+ const relativePath = urlPath === "/" ? "index.html" : urlPath.replace(/^\/+/, "");
16363
+ let filePath = path5.join(resolvedAssetsDir, relativePath);
16364
+ if (!filePath.startsWith(resolvedAssetsDir + path5.sep) && filePath !== resolvedAssetsDir) {
16365
+ sendError(res, 403, "Forbidden");
16366
+ return;
16367
+ }
16368
+ try {
16369
+ const stat = fs5.statSync(filePath);
16370
+ if (stat.isDirectory()) {
16371
+ filePath = path5.join(resolvedAssetsDir, "index.html");
16372
+ }
16373
+ } catch (err) {
16374
+ const nodeErr = err;
16375
+ if (nodeErr.code === "ENOENT") {
16376
+ filePath = path5.join(resolvedAssetsDir, "index.html");
16377
+ } else {
16378
+ console.error("Failed to stat file:", filePath, err);
16379
+ sendError(res, 500, "Internal server error");
16380
+ return;
16381
+ }
16382
+ }
16383
+ const ext = path5.extname(filePath).toLowerCase();
16384
+ const contentType = MIME_TYPES[ext] || "application/octet-stream";
16385
+ try {
16386
+ const content = fs5.readFileSync(filePath);
16387
+ res.writeHead(200, {
16388
+ "Content-Type": contentType,
16389
+ "Content-Length": content.length
16390
+ });
16391
+ res.end(content);
16392
+ } catch (error) {
16393
+ const nodeErr = error;
16394
+ if (nodeErr.code === "ENOENT") {
16395
+ sendError(res, 404, "Not found");
16396
+ } else {
16397
+ console.error("Failed to serve static file:", filePath, error);
16398
+ sendError(res, 500, "Failed to read file");
16399
+ }
16400
+ }
16401
+ }
16402
+ const MAX_PORT_ATTEMPTS = 10;
16403
+ let actualPort = requestedPort;
16404
+ for (let attempt = 0; attempt < MAX_PORT_ATTEMPTS; attempt++) {
16405
+ try {
16406
+ await new Promise((resolve5, reject) => {
16407
+ server.once("error", reject);
16408
+ server.listen(actualPort, "127.0.0.1", () => resolve5());
16409
+ });
16410
+ break;
16411
+ } catch (err) {
16412
+ const nodeErr = err;
16413
+ if (nodeErr.code === "EADDRINUSE" && attempt < MAX_PORT_ATTEMPTS - 1) {
16414
+ console.error(`Port ${actualPort} is in use, trying ${actualPort + 1}...`);
16415
+ actualPort++;
16416
+ continue;
16417
+ }
16418
+ console.error(`Failed to start server: ${nodeErr.message}`);
16419
+ process.exit(1);
16420
+ }
16421
+ }
16422
+ const serverUrl = `http://localhost:${actualPort}`;
16423
+ console.error(`Dashboard server running at ${serverUrl}`);
16424
+ if (open) {
16425
+ const { execFile: execFile4 } = await import("child_process");
16426
+ let openCmd;
16427
+ let args;
16428
+ switch (process.platform) {
16429
+ case "darwin":
16430
+ openCmd = "open";
16431
+ args = [serverUrl];
16432
+ break;
16433
+ case "win32":
16434
+ openCmd = "cmd";
16435
+ args = ["/c", "start", "", serverUrl];
16436
+ break;
16437
+ default:
16438
+ openCmd = "xdg-open";
16439
+ args = [serverUrl];
16440
+ break;
16441
+ }
16442
+ execFile4(openCmd, args, (error) => {
16443
+ if (error) {
16444
+ console.error("Failed to open browser:", error.message);
16445
+ console.error(`Open manually: ${serverUrl}`);
16446
+ }
16447
+ });
16448
+ }
16449
+ const shutdown = () => {
16450
+ console.error("\nShutting down dashboard server...");
16451
+ server.close(() => {
16452
+ process.exit(0);
16453
+ });
16454
+ setTimeout(() => process.exit(0), 3e3).unref();
16455
+ };
16456
+ process.on("SIGINT", shutdown);
16457
+ process.on("SIGTERM", shutdown);
16458
+ }
16459
+ var http, fs5, path5, VALID_ACTIONS, MAX_BODY_BYTES, MIME_TYPES;
16460
+ var init_dashboard_server = __esm({
16461
+ "src/commands/dashboard-server.ts"() {
16462
+ "use strict";
16463
+ http = __toESM(require("http"), 1);
16464
+ fs5 = __toESM(require("fs"), 1);
16465
+ path5 = __toESM(require("path"), 1);
16466
+ init_core();
16467
+ init_dashboard_data();
16468
+ init_dashboard_templates();
16469
+ VALID_ACTIONS = /* @__PURE__ */ new Set(["shelve", "unshelve", "snooze", "unsnooze"]);
16470
+ MAX_BODY_BYTES = 10240;
16471
+ MIME_TYPES = {
16472
+ ".html": "text/html",
16473
+ ".js": "application/javascript",
16474
+ ".css": "text/css",
16475
+ ".svg": "image/svg+xml",
16476
+ ".json": "application/json",
16477
+ ".png": "image/png",
16478
+ ".ico": "image/x-icon"
16479
+ };
16480
+ }
16481
+ });
16482
+
16535
16483
  // src/commands/dashboard.ts
16536
16484
  var dashboard_exports = {};
16537
16485
  __export(dashboard_exports, {
16538
16486
  runDashboard: () => runDashboard,
16487
+ serveDashboard: () => serveDashboard,
16539
16488
  writeDashboardFromState: () => writeDashboardFromState
16540
16489
  });
16541
16490
  async function runDashboard(options) {
@@ -16605,8 +16554,8 @@ async function runDashboard(options) {
16605
16554
  const issueResponses = commentedIssues.filter((i) => i.status === "new_response");
16606
16555
  const html = generateDashboardHtml(stats, monthlyMerged, monthlyClosed, monthlyOpened, digest, state, issueResponses);
16607
16556
  const dashboardPath = getDashboardPath();
16608
- fs5.writeFileSync(dashboardPath, html, { mode: 420 });
16609
- fs5.chmodSync(dashboardPath, 420);
16557
+ fs6.writeFileSync(dashboardPath, html, { mode: 420 });
16558
+ fs6.chmodSync(dashboardPath, 420);
16610
16559
  if (options.offline) {
16611
16560
  const lastUpdated = digest.generatedAt || state.lastDigestAt || state.lastRunAt;
16612
16561
  console.log(`
@@ -16641,15 +16590,56 @@ function writeDashboardFromState() {
16641
16590
  const stats = buildDashboardStats(digest, state);
16642
16591
  const html = generateDashboardHtml(stats, monthlyMerged, monthlyClosed, monthlyOpened, digest, state);
16643
16592
  const dashboardPath = getDashboardPath();
16644
- fs5.writeFileSync(dashboardPath, html, { mode: 420 });
16645
- fs5.chmodSync(dashboardPath, 420);
16593
+ fs6.writeFileSync(dashboardPath, html, { mode: 420 });
16594
+ fs6.chmodSync(dashboardPath, 420);
16646
16595
  return dashboardPath;
16647
16596
  }
16648
- var fs5, import_child_process2;
16597
+ function resolveAssetsDir() {
16598
+ const devPath = path6.resolve(__dirname, "../../dashboard/dist");
16599
+ if (fs6.existsSync(path6.join(devPath, "index.html"))) {
16600
+ return devPath;
16601
+ }
16602
+ const bundlePath = path6.resolve(path6.dirname(process.argv[1]), "../../dashboard/dist");
16603
+ if (fs6.existsSync(path6.join(bundlePath, "index.html"))) {
16604
+ return bundlePath;
16605
+ }
16606
+ try {
16607
+ const dashboardPkgPath = require.resolve("@oss-autopilot/dashboard/package.json");
16608
+ const dashboardDist = path6.join(path6.dirname(dashboardPkgPath), "dist");
16609
+ if (fs6.existsSync(path6.join(dashboardDist, "index.html"))) {
16610
+ return dashboardDist;
16611
+ }
16612
+ } catch (error) {
16613
+ const code = error.code;
16614
+ if (code !== "MODULE_NOT_FOUND") {
16615
+ console.error("Error resolving dashboard package:", error);
16616
+ }
16617
+ }
16618
+ return null;
16619
+ }
16620
+ async function serveDashboard(options) {
16621
+ const assetsDir = resolveAssetsDir();
16622
+ if (!assetsDir) {
16623
+ console.error("Could not find dashboard SPA assets.");
16624
+ console.error("Make sure packages/dashboard has been built:");
16625
+ console.error(" cd packages/dashboard && pnpm run build");
16626
+ process.exit(1);
16627
+ }
16628
+ const token = getGitHubToken();
16629
+ const { startDashboardServer: startDashboardServer2 } = await Promise.resolve().then(() => (init_dashboard_server(), dashboard_server_exports));
16630
+ await startDashboardServer2({
16631
+ port: options.port,
16632
+ assetsDir,
16633
+ token,
16634
+ open: options.open
16635
+ });
16636
+ }
16637
+ var fs6, path6, import_child_process2;
16649
16638
  var init_dashboard = __esm({
16650
16639
  "src/commands/dashboard.ts"() {
16651
16640
  "use strict";
16652
- fs5 = __toESM(require("fs"), 1);
16641
+ fs6 = __toESM(require("fs"), 1);
16642
+ path6 = __toESM(require("path"), 1);
16653
16643
  import_child_process2 = require("child_process");
16654
16644
  init_core();
16655
16645
  init_json();
@@ -16682,7 +16672,7 @@ function extractTitle(line) {
16682
16672
  cleaned = cleaned.replace(/\[[ xX]\]\s*/, "");
16683
16673
  cleaned = cleaned.replace(/~~/g, "");
16684
16674
  cleaned = cleaned.replace(/\b(Done|DONE|done)\b/g, "");
16685
- cleaned = cleaned.replace(/^[\s\-–—:]+/, "").replace(/[\s\-–—:]+$/, "");
16675
+ cleaned = cleaned.replace(/^[\s\-\u2013\u2014:]+/, "").replace(/[\s\-\u2013\u2014:]+$/, "");
16686
16676
  return cleaned.trim();
16687
16677
  }
16688
16678
  function isCompleted(line) {
@@ -16729,57 +16719,25 @@ function parseIssueList(content) {
16729
16719
  };
16730
16720
  }
16731
16721
  async function runParseList(options) {
16732
- const filePath = path5.resolve(options.filePath);
16733
- if (!fs6.existsSync(filePath)) {
16734
- if (options.json) {
16735
- outputJsonError(`File not found: ${filePath}`);
16736
- } else {
16737
- console.error(`Error: File not found: ${filePath}`);
16738
- }
16739
- process.exit(1);
16722
+ const filePath = path7.resolve(options.filePath);
16723
+ if (!fs7.existsSync(filePath)) {
16724
+ throw new Error(`File not found: ${filePath}`);
16740
16725
  }
16741
16726
  let content;
16742
16727
  try {
16743
- content = fs6.readFileSync(filePath, "utf-8");
16728
+ content = fs7.readFileSync(filePath, "utf-8");
16744
16729
  } catch (error) {
16745
16730
  const msg = error instanceof Error ? error.message : String(error);
16746
- if (options.json) {
16747
- outputJsonError(`Failed to read file: ${msg}`);
16748
- } else {
16749
- console.error(`Error: Failed to read file: ${msg}`);
16750
- }
16751
- process.exit(1);
16752
- }
16753
- const result = parseIssueList(content);
16754
- if (options.json) {
16755
- outputJson(result);
16756
- } else {
16757
- console.log(`
16758
- \u{1F4CB} Issue List: ${filePath}
16759
- `);
16760
- console.log(`Available: ${result.availableCount} | Completed: ${result.completedCount}
16761
- `);
16762
- if (result.available.length > 0) {
16763
- console.log("--- Available ---");
16764
- for (const item of result.available) {
16765
- console.log(` [${item.tier}] ${item.repo}#${item.number}: ${item.title}`);
16766
- }
16767
- }
16768
- if (result.completed.length > 0) {
16769
- console.log("\n--- Completed ---");
16770
- for (const item of result.completed) {
16771
- console.log(` [${item.tier}] ${item.repo}#${item.number}: ${item.title}`);
16772
- }
16773
- }
16731
+ throw new Error(`Failed to read file: ${msg}`, { cause: error });
16774
16732
  }
16733
+ return parseIssueList(content);
16775
16734
  }
16776
- var fs6, path5;
16735
+ var fs7, path7;
16777
16736
  var init_parse_list = __esm({
16778
16737
  "src/commands/parse-list.ts"() {
16779
16738
  "use strict";
16780
- fs6 = __toESM(require("fs"), 1);
16781
- path5 = __toESM(require("path"), 1);
16782
- init_json();
16739
+ fs7 = __toESM(require("fs"), 1);
16740
+ path7 = __toESM(require("path"), 1);
16783
16741
  }
16784
16742
  });
16785
16743
 
@@ -16789,25 +16747,25 @@ __export(check_integration_exports, {
16789
16747
  runCheckIntegration: () => runCheckIntegration
16790
16748
  });
16791
16749
  function getImportName(filePath) {
16792
- const ext = path6.extname(filePath);
16793
- const base = path6.basename(filePath, ext);
16750
+ const ext = path8.extname(filePath);
16751
+ const base = path8.basename(filePath, ext);
16794
16752
  if (base === "index") {
16795
- return path6.basename(path6.dirname(filePath));
16753
+ return path8.basename(path8.dirname(filePath));
16796
16754
  }
16797
16755
  return base;
16798
16756
  }
16799
16757
  function suggestEntryPoints(newFile, existingFiles) {
16800
- const dir = path6.dirname(newFile);
16758
+ const dir = path8.dirname(newFile);
16801
16759
  const suggestions = [];
16802
16760
  for (const ext of [".ts", ".tsx", ".js", ".jsx"]) {
16803
- const indexFile = path6.join(dir, `index${ext}`);
16761
+ const indexFile = path8.join(dir, `index${ext}`);
16804
16762
  if (existingFiles.includes(indexFile)) {
16805
16763
  suggestions.push(indexFile);
16806
16764
  }
16807
16765
  }
16808
- const parentDir = path6.dirname(dir);
16766
+ const parentDir = path8.dirname(dir);
16809
16767
  for (const ext of [".ts", ".tsx", ".js", ".jsx"]) {
16810
- const parentIndex = path6.join(parentDir, `index${ext}`);
16768
+ const parentIndex = path8.join(parentDir, `index${ext}`);
16811
16769
  if (existingFiles.includes(parentIndex)) {
16812
16770
  suggestions.push(parentIndex);
16813
16771
  }
@@ -16818,33 +16776,22 @@ async function runCheckIntegration(options) {
16818
16776
  const base = options.base;
16819
16777
  let newFiles;
16820
16778
  try {
16821
- const output2 = (0, import_child_process3.execFileSync)("git", ["diff", "--name-only", "--diff-filter=A", `${base}...HEAD`], {
16779
+ const output = (0, import_child_process3.execFileSync)("git", ["diff", "--name-only", "--diff-filter=A", `${base}...HEAD`], {
16822
16780
  encoding: "utf-8",
16823
16781
  timeout: 1e4
16824
16782
  }).trim();
16825
- newFiles = output2 ? output2.split("\n").filter(Boolean) : [];
16783
+ newFiles = output ? output.split("\n").filter(Boolean) : [];
16826
16784
  } catch (error) {
16827
16785
  const msg = error instanceof Error ? error.message : String(error);
16828
- if (options.json) {
16829
- outputJsonError(`Failed to run git diff: ${msg}`);
16830
- } else {
16831
- console.error(`Error: Failed to run git diff: ${msg}`);
16832
- }
16833
- process.exit(1);
16786
+ throw new Error(`Failed to run git diff: ${msg}`, { cause: error });
16834
16787
  }
16835
16788
  const codeFiles = newFiles.filter((f) => {
16836
- const ext = path6.extname(f);
16789
+ const ext = path8.extname(f);
16837
16790
  if (!CODE_EXTENSIONS.has(ext)) return false;
16838
16791
  return !IGNORED_PATTERNS.some((p) => p.test(f));
16839
16792
  });
16840
16793
  if (codeFiles.length === 0) {
16841
- const result = { newFiles: [], unreferencedCount: 0 };
16842
- if (options.json) {
16843
- outputJson(result);
16844
- } else {
16845
- console.log("\nNo new code files to check.");
16846
- }
16847
- return;
16794
+ return { newFiles: [], unreferencedCount: 0 };
16848
16795
  }
16849
16796
  let allFiles;
16850
16797
  try {
@@ -16882,7 +16829,7 @@ async function runCheckIntegration(options) {
16882
16829
  const exitCode = error && typeof error === "object" && "status" in error ? error.status : null;
16883
16830
  if (exitCode !== null && exitCode !== 1) {
16884
16831
  const msg = error instanceof Error ? error.message : String(error);
16885
- console.error(`Warning: git grep failed for "${pattern}": ${msg}`);
16832
+ debug("check-integration", `git grep failed for "${pattern}": ${msg}`);
16886
16833
  }
16887
16834
  }
16888
16835
  }
@@ -16899,36 +16846,14 @@ async function runCheckIntegration(options) {
16899
16846
  results.push(info);
16900
16847
  }
16901
16848
  const unreferencedCount = results.filter((r) => !r.isIntegrated).length;
16902
- const output = { newFiles: results, unreferencedCount };
16903
- if (options.json) {
16904
- outputJson(output);
16905
- } else {
16906
- console.log(`
16907
- \u{1F50D} Integration Check (base: ${base})
16908
- `);
16909
- console.log(`New files: ${results.length} | Unreferenced: ${unreferencedCount}
16910
- `);
16911
- for (const file of results) {
16912
- const status = file.isIntegrated ? "\u2705" : "\u26A0\uFE0F";
16913
- console.log(`${status} ${file.path}`);
16914
- if (file.isIntegrated) {
16915
- console.log(` Referenced by: ${file.referencedBy.join(", ")}`);
16916
- } else {
16917
- console.log(" Not referenced by any file");
16918
- if (file.suggestedEntryPoints && file.suggestedEntryPoints.length > 0) {
16919
- console.log(` Suggested entry points: ${file.suggestedEntryPoints.join(", ")}`);
16920
- }
16921
- }
16922
- }
16923
- }
16849
+ return { newFiles: results, unreferencedCount };
16924
16850
  }
16925
- var path6, import_child_process3, CODE_EXTENSIONS, IGNORED_PATTERNS;
16851
+ var path8, import_child_process3, CODE_EXTENSIONS, IGNORED_PATTERNS;
16926
16852
  var init_check_integration = __esm({
16927
16853
  "src/commands/check-integration.ts"() {
16928
16854
  "use strict";
16929
- path6 = __toESM(require("path"), 1);
16855
+ path8 = __toESM(require("path"), 1);
16930
16856
  import_child_process3 = require("child_process");
16931
- init_json();
16932
16857
  init_core();
16933
16858
  CODE_EXTENSIONS = /* @__PURE__ */ new Set([
16934
16859
  ".ts",
@@ -17005,7 +16930,7 @@ function getCurrentBranch(repoPath) {
17005
16930
  function scanForRepos(scanPaths) {
17006
16931
  const repos = {};
17007
16932
  for (const scanPath of scanPaths) {
17008
- if (!fs7.existsSync(scanPath)) continue;
16933
+ if (!fs8.existsSync(scanPath)) continue;
17009
16934
  let gitDirs;
17010
16935
  try {
17011
16936
  const output = (0, import_child_process4.execFileSync)("find", [scanPath, "-maxdepth", "4", "-name", ".git", "-type", "d"], {
@@ -17019,7 +16944,7 @@ function scanForRepos(scanPaths) {
17019
16944
  continue;
17020
16945
  }
17021
16946
  for (const gitDir of gitDirs) {
17022
- const repoPath = path7.dirname(gitDir);
16947
+ const repoPath = path9.dirname(gitDir);
17023
16948
  const remote = getGitHubRemote(repoPath);
17024
16949
  if (!remote) continue;
17025
16950
  const currentBranch = getCurrentBranch(repoPath);
@@ -17035,79 +16960,48 @@ function scanForRepos(scanPaths) {
17035
16960
  async function runLocalRepos(options) {
17036
16961
  const stateManager2 = getStateManager();
17037
16962
  const state = stateManager2.getState();
17038
- const scanPaths = options.paths?.map((p) => path7.resolve(p)) ?? state.config.localRepoScanPaths ?? DEFAULT_SCAN_PATHS.filter((p) => fs7.existsSync(p));
16963
+ const scanPaths = options.paths?.map((p) => path9.resolve(p)) ?? state.config.localRepoScanPaths ?? DEFAULT_SCAN_PATHS.filter((p) => fs8.existsSync(p));
17039
16964
  if (!options.scan && state.localRepoCache) {
17040
16965
  const cache = state.localRepoCache;
17041
- const result2 = {
16966
+ return {
17042
16967
  repos: cache.repos,
17043
16968
  scanPaths: cache.scanPaths,
17044
16969
  cachedAt: cache.cachedAt,
17045
16970
  fromCache: true
17046
16971
  };
17047
- if (options.json) {
17048
- outputJson(result2);
17049
- } else {
17050
- console.log(`
17051
- \u{1F4C1} Local Repos (cached ${cache.cachedAt})
17052
- `);
17053
- printRepos(cache.repos);
17054
- }
17055
- return;
17056
- }
17057
- if (!options.json) {
17058
- console.log(`
17059
- \u{1F50D} Scanning for local repos in ${scanPaths.length} directories...
17060
- `);
17061
16972
  }
17062
16973
  const repos = scanForRepos(scanPaths);
17063
- const repoCount = Object.keys(repos).length;
17064
16974
  const cachedAt = (/* @__PURE__ */ new Date()).toISOString();
17065
16975
  try {
17066
16976
  stateManager2.setLocalRepoCache({ repos, scanPaths, cachedAt });
17067
16977
  stateManager2.save();
17068
16978
  } catch (error) {
17069
16979
  const msg = error instanceof Error ? error.message : String(error);
17070
- console.error(`Warning: Failed to cache scan results: ${msg}`);
16980
+ debug("local-repos", `Failed to cache scan results: ${msg}`);
17071
16981
  }
17072
- const result = {
16982
+ return {
17073
16983
  repos,
17074
16984
  scanPaths,
17075
16985
  cachedAt,
17076
16986
  fromCache: false
17077
16987
  };
17078
- if (options.json) {
17079
- outputJson(result);
17080
- } else {
17081
- console.log(`Found ${repoCount} repos:
17082
- `);
17083
- printRepos(repos);
17084
- }
17085
- }
17086
- function printRepos(repos) {
17087
- const entries = Object.entries(repos).sort(([a], [b]) => a.localeCompare(b));
17088
- for (const [remote, info] of entries) {
17089
- const branch = info.currentBranch ? ` (${info.currentBranch})` : "";
17090
- console.log(` ${remote}${branch}`);
17091
- console.log(` ${info.path}`);
17092
- }
17093
16988
  }
17094
- var fs7, path7, os2, import_child_process4, DEFAULT_SCAN_PATHS;
16989
+ var fs8, path9, os2, import_child_process4, DEFAULT_SCAN_PATHS;
17095
16990
  var init_local_repos = __esm({
17096
16991
  "src/commands/local-repos.ts"() {
17097
16992
  "use strict";
17098
- fs7 = __toESM(require("fs"), 1);
17099
- path7 = __toESM(require("path"), 1);
16993
+ fs8 = __toESM(require("fs"), 1);
16994
+ path9 = __toESM(require("path"), 1);
17100
16995
  os2 = __toESM(require("os"), 1);
17101
16996
  import_child_process4 = require("child_process");
17102
16997
  init_core();
17103
- init_json();
17104
16998
  DEFAULT_SCAN_PATHS = [
17105
- path7.join(os2.homedir(), "Documents", "oss"),
17106
- path7.join(os2.homedir(), "dev"),
17107
- path7.join(os2.homedir(), "projects"),
17108
- path7.join(os2.homedir(), "src"),
17109
- path7.join(os2.homedir(), "code"),
17110
- path7.join(os2.homedir(), "repos")
16999
+ path9.join(os2.homedir(), "Documents", "oss"),
17000
+ path9.join(os2.homedir(), "dev"),
17001
+ path9.join(os2.homedir(), "projects"),
17002
+ path9.join(os2.homedir(), "src"),
17003
+ path9.join(os2.homedir(), "code"),
17004
+ path9.join(os2.homedir(), "repos")
17111
17005
  ];
17112
17006
  }
17113
17007
  });
@@ -17122,8 +17016,8 @@ __export(startup_exports, {
17122
17016
  });
17123
17017
  function getVersion() {
17124
17018
  try {
17125
- const pkgPath = path8.join(path8.dirname(process.argv[1]), "..", "package.json");
17126
- return JSON.parse(fs8.readFileSync(pkgPath, "utf-8")).version;
17019
+ const pkgPath = path10.join(path10.dirname(process.argv[1]), "..", "package.json");
17020
+ return JSON.parse(fs9.readFileSync(pkgPath, "utf-8")).version;
17127
17021
  } catch (error) {
17128
17022
  console.error("[STARTUP] Failed to detect CLI version:", error instanceof Error ? error.message : error);
17129
17023
  return "0.0.0";
@@ -17155,11 +17049,11 @@ function detectIssueList() {
17155
17049
  let issueListPath = "";
17156
17050
  let source = "auto-detected";
17157
17051
  const configPath = ".claude/oss-autopilot/config.md";
17158
- if (fs8.existsSync(configPath)) {
17052
+ if (fs9.existsSync(configPath)) {
17159
17053
  try {
17160
- const configContent = fs8.readFileSync(configPath, "utf-8");
17054
+ const configContent = fs9.readFileSync(configPath, "utf-8");
17161
17055
  const configuredPath = parseIssueListPathFromConfig(configContent);
17162
- if (configuredPath && fs8.existsSync(configuredPath)) {
17056
+ if (configuredPath && fs9.existsSync(configuredPath)) {
17163
17057
  issueListPath = configuredPath;
17164
17058
  source = "configured";
17165
17059
  }
@@ -17170,7 +17064,7 @@ function detectIssueList() {
17170
17064
  if (!issueListPath) {
17171
17065
  const probes = ["open-source/potential-issue-list.md", "oss/issue-list.md", "issues.md"];
17172
17066
  for (const probe of probes) {
17173
- if (fs8.existsSync(probe)) {
17067
+ if (fs9.existsSync(probe)) {
17174
17068
  issueListPath = probe;
17175
17069
  source = "auto-detected";
17176
17070
  break;
@@ -17179,7 +17073,7 @@ function detectIssueList() {
17179
17073
  }
17180
17074
  if (!issueListPath) return void 0;
17181
17075
  try {
17182
- const content = fs8.readFileSync(issueListPath, "utf-8");
17076
+ const content = fs9.readFileSync(issueListPath, "utf-8");
17183
17077
  const { availableCount, completedCount } = countIssueListItems(content);
17184
17078
  return { path: issueListPath, source, availableCount, completedCount };
17185
17079
  } catch (error) {
@@ -17200,82 +17094,52 @@ function openInBrowser(filePath) {
17200
17094
  }
17201
17095
  });
17202
17096
  }
17203
- async function runStartup(options) {
17097
+ async function runStartup() {
17204
17098
  const version = getVersion();
17205
17099
  const stateManager2 = getStateManager();
17206
17100
  if (!stateManager2.isSetupComplete()) {
17207
- if (options.json) {
17208
- outputJson({ version, setupComplete: false });
17209
- } else {
17210
- console.log("Setup incomplete. Run /setup-oss first.");
17211
- }
17212
- return;
17101
+ return { version, setupComplete: false };
17213
17102
  }
17214
17103
  const token = getGitHubToken();
17215
17104
  if (!token) {
17216
- if (options.json) {
17217
- outputJson({
17218
- version,
17219
- setupComplete: true,
17220
- authError: 'GitHub authentication required. Install GitHub CLI (https://cli.github.com/) and run "gh auth login", or set GITHUB_TOKEN.'
17221
- });
17222
- } else {
17223
- console.error("Error: GitHub authentication required.");
17224
- }
17225
- return;
17105
+ return {
17106
+ version,
17107
+ setupComplete: true,
17108
+ authError: 'GitHub authentication required. Install GitHub CLI (https://cli.github.com/) and run "gh auth login", or set GITHUB_TOKEN.'
17109
+ };
17226
17110
  }
17111
+ const daily = await executeDailyCheck(token);
17112
+ let dashboardPath;
17113
+ let dashboardOpened = false;
17227
17114
  try {
17228
- const daily = await executeDailyCheck(token);
17229
- let dashboardPath;
17230
- let dashboardOpened = false;
17231
- try {
17232
- dashboardPath = writeDashboardFromState();
17233
- if (daily.digest.summary.totalActivePRs > 0) {
17234
- openInBrowser(dashboardPath);
17235
- dashboardOpened = true;
17236
- }
17237
- } catch (error) {
17238
- console.error("[STARTUP] Dashboard generation failed:", error instanceof Error ? error.message : error);
17239
- }
17240
- if (dashboardOpened) {
17241
- daily.briefSummary += " | Dashboard opened in browser";
17242
- }
17243
- const issueList = detectIssueList();
17244
- if (options.json) {
17245
- outputJson({
17246
- version,
17247
- setupComplete: true,
17248
- daily,
17249
- dashboardPath,
17250
- issueList
17251
- });
17252
- } else {
17253
- console.log(`OSS Autopilot v${version}`);
17254
- console.log(daily.briefSummary);
17255
- if (dashboardPath) console.log(`Dashboard: ${dashboardPath}`);
17115
+ dashboardPath = writeDashboardFromState();
17116
+ if (daily.digest.summary.totalActivePRs > 0) {
17117
+ openInBrowser(dashboardPath);
17118
+ dashboardOpened = true;
17256
17119
  }
17257
17120
  } catch (error) {
17258
- const msg = error instanceof Error ? error.message : String(error);
17259
- if (options.json) {
17260
- outputJsonError(`Daily check failed: ${msg}`);
17261
- } else {
17262
- console.error(`[FATAL] Daily check failed: ${msg}`);
17263
- if (error instanceof Error && error.stack) {
17264
- console.error(error.stack);
17265
- }
17266
- }
17267
- process.exit(1);
17121
+ console.error("[STARTUP] Dashboard generation failed:", error instanceof Error ? error.message : error);
17122
+ }
17123
+ if (dashboardOpened) {
17124
+ daily.briefSummary += " | Dashboard opened in browser";
17268
17125
  }
17126
+ const issueList = detectIssueList();
17127
+ return {
17128
+ version,
17129
+ setupComplete: true,
17130
+ daily,
17131
+ dashboardPath,
17132
+ issueList
17133
+ };
17269
17134
  }
17270
- var fs8, path8, import_child_process5;
17135
+ var fs9, path10, import_child_process5;
17271
17136
  var init_startup = __esm({
17272
17137
  "src/commands/startup.ts"() {
17273
17138
  "use strict";
17274
- fs8 = __toESM(require("fs"), 1);
17275
- path8 = __toESM(require("path"), 1);
17139
+ fs9 = __toESM(require("fs"), 1);
17140
+ path10 = __toESM(require("path"), 1);
17276
17141
  import_child_process5 = require("child_process");
17277
17142
  init_core();
17278
- init_json();
17279
17143
  init_daily();
17280
17144
  init_dashboard();
17281
17145
  }
@@ -17290,44 +17154,28 @@ __export(shelve_exports, {
17290
17154
  });
17291
17155
  async function runShelve(options) {
17292
17156
  validateUrl(options.prUrl);
17293
- validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR", options.json);
17157
+ validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR");
17294
17158
  const stateManager2 = getStateManager();
17295
17159
  const added = stateManager2.shelvePR(options.prUrl);
17296
17160
  if (added) {
17297
17161
  stateManager2.save();
17298
17162
  }
17299
- if (options.json) {
17300
- outputJson({ shelved: added, url: options.prUrl });
17301
- } else if (added) {
17302
- console.log(`Shelved: ${options.prUrl}`);
17303
- console.log("This PR is now excluded from capacity and actionable issues.");
17304
- console.log("It will auto-unshelve if a maintainer engages.");
17305
- } else {
17306
- console.log("PR is already shelved.");
17307
- }
17163
+ return { shelved: added, url: options.prUrl };
17308
17164
  }
17309
17165
  async function runUnshelve(options) {
17310
17166
  validateUrl(options.prUrl);
17311
- validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR", options.json);
17167
+ validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR");
17312
17168
  const stateManager2 = getStateManager();
17313
17169
  const removed = stateManager2.unshelvePR(options.prUrl);
17314
17170
  if (removed) {
17315
17171
  stateManager2.save();
17316
17172
  }
17317
- if (options.json) {
17318
- outputJson({ unshelved: removed, url: options.prUrl });
17319
- } else if (removed) {
17320
- console.log(`Unshelved: ${options.prUrl}`);
17321
- console.log("This PR is now active again.");
17322
- } else {
17323
- console.log("PR was not shelved.");
17324
- }
17173
+ return { unshelved: removed, url: options.prUrl };
17325
17174
  }
17326
17175
  var init_shelve = __esm({
17327
17176
  "src/commands/shelve.ts"() {
17328
17177
  "use strict";
17329
17178
  init_core();
17330
- init_json();
17331
17179
  init_validation();
17332
17180
  }
17333
17181
  });
@@ -17341,44 +17189,28 @@ __export(dismiss_exports, {
17341
17189
  });
17342
17190
  async function runDismiss(options) {
17343
17191
  validateUrl(options.issueUrl);
17344
- validateGitHubUrl(options.issueUrl, ISSUE_URL_PATTERN, "issue", options.json);
17192
+ validateGitHubUrl(options.issueUrl, ISSUE_URL_PATTERN, "issue");
17345
17193
  const stateManager2 = getStateManager();
17346
17194
  const added = stateManager2.dismissIssue(options.issueUrl, (/* @__PURE__ */ new Date()).toISOString());
17347
17195
  if (added) {
17348
17196
  stateManager2.save();
17349
17197
  }
17350
- if (options.json) {
17351
- outputJson({ dismissed: added, url: options.issueUrl });
17352
- } else if (added) {
17353
- console.log(`Dismissed: ${options.issueUrl}`);
17354
- console.log("Issue reply notifications are now muted.");
17355
- console.log("New responses after this point will resurface automatically.");
17356
- } else {
17357
- console.log("Issue is already dismissed.");
17358
- }
17198
+ return { dismissed: added, url: options.issueUrl };
17359
17199
  }
17360
17200
  async function runUndismiss(options) {
17361
17201
  validateUrl(options.issueUrl);
17362
- validateGitHubUrl(options.issueUrl, ISSUE_URL_PATTERN, "issue", options.json);
17202
+ validateGitHubUrl(options.issueUrl, ISSUE_URL_PATTERN, "issue");
17363
17203
  const stateManager2 = getStateManager();
17364
17204
  const removed = stateManager2.undismissIssue(options.issueUrl);
17365
17205
  if (removed) {
17366
17206
  stateManager2.save();
17367
17207
  }
17368
- if (options.json) {
17369
- outputJson({ undismissed: removed, url: options.issueUrl });
17370
- } else if (removed) {
17371
- console.log(`Undismissed: ${options.issueUrl}`);
17372
- console.log("Issue reply notifications are active again.");
17373
- } else {
17374
- console.log("Issue was not dismissed.");
17375
- }
17208
+ return { undismissed: removed, url: options.issueUrl };
17376
17209
  }
17377
17210
  var init_dismiss = __esm({
17378
17211
  "src/commands/dismiss.ts"() {
17379
17212
  "use strict";
17380
17213
  init_core();
17381
- init_json();
17382
17214
  init_validation();
17383
17215
  }
17384
17216
  });
@@ -17391,77 +17223,41 @@ __export(snooze_exports, {
17391
17223
  });
17392
17224
  async function runSnooze(options) {
17393
17225
  validateUrl(options.prUrl);
17394
- validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR", options.json);
17226
+ validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR");
17395
17227
  validateMessage(options.reason);
17396
17228
  const days = options.days ?? DEFAULT_SNOOZE_DAYS;
17397
17229
  if (!Number.isFinite(days) || days <= 0) {
17398
- if (options.json) {
17399
- outputJsonError("Snooze duration must be a positive number of days.");
17400
- } else {
17401
- console.error("Error: Snooze duration must be a positive number of days.");
17402
- }
17403
- process.exit(1);
17230
+ throw new Error("Snooze duration must be a positive number of days.");
17404
17231
  }
17405
- try {
17406
- const stateManager2 = getStateManager();
17407
- const added = stateManager2.snoozePR(options.prUrl, options.reason, days);
17408
- if (added) {
17409
- stateManager2.save();
17410
- }
17411
- const snoozeInfo = stateManager2.getSnoozeInfo(options.prUrl);
17412
- if (options.json) {
17413
- outputJson({
17414
- snoozed: added,
17415
- url: options.prUrl,
17416
- days,
17417
- reason: options.reason,
17418
- expiresAt: snoozeInfo?.expiresAt
17419
- });
17420
- } else if (added) {
17421
- console.log(`Snoozed: ${options.prUrl}`);
17422
- console.log(`Reason: ${options.reason}`);
17423
- console.log(`Duration: ${days} day${days === 1 ? "" : "s"}`);
17424
- console.log(`Expires: ${snoozeInfo?.expiresAt ? new Date(snoozeInfo.expiresAt).toLocaleString() : "unknown"}`);
17425
- console.log("CI failure notifications are now muted for this PR.");
17426
- } else {
17427
- console.log("PR is already snoozed.");
17428
- if (snoozeInfo) {
17429
- console.log(`Expires: ${new Date(snoozeInfo.expiresAt).toLocaleString()}`);
17430
- }
17431
- }
17432
- } catch (error) {
17433
- const msg = error instanceof Error ? error.message : String(error);
17434
- if (options.json) {
17435
- outputJsonError(`Snooze failed: ${msg}`);
17436
- } else {
17437
- console.error(`Error: Snooze failed: ${msg}`);
17438
- }
17439
- process.exit(1);
17232
+ const stateManager2 = getStateManager();
17233
+ const added = stateManager2.snoozePR(options.prUrl, options.reason, days);
17234
+ if (added) {
17235
+ stateManager2.save();
17440
17236
  }
17237
+ const snoozeInfo = stateManager2.getSnoozeInfo(options.prUrl);
17238
+ return {
17239
+ snoozed: added,
17240
+ url: options.prUrl,
17241
+ days,
17242
+ reason: options.reason,
17243
+ expiresAt: snoozeInfo?.expiresAt
17244
+ };
17441
17245
  }
17442
17246
  async function runUnsnooze(options) {
17443
17247
  validateUrl(options.prUrl);
17444
- validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR", options.json);
17248
+ validateGitHubUrl(options.prUrl, PR_URL_PATTERN, "PR");
17445
17249
  const stateManager2 = getStateManager();
17446
17250
  const removed = stateManager2.unsnoozePR(options.prUrl);
17447
17251
  if (removed) {
17448
17252
  stateManager2.save();
17449
17253
  }
17450
- if (options.json) {
17451
- outputJson({ unsnoozed: removed, url: options.prUrl });
17452
- } else if (removed) {
17453
- console.log(`Unsnoozed: ${options.prUrl}`);
17454
- console.log("CI failure notifications are active again for this PR.");
17455
- } else {
17456
- console.log("PR was not snoozed.");
17457
- }
17254
+ return { unsnoozed: removed, url: options.prUrl };
17458
17255
  }
17459
17256
  var DEFAULT_SNOOZE_DAYS;
17460
17257
  var init_snooze = __esm({
17461
17258
  "src/commands/snooze.ts"() {
17462
17259
  "use strict";
17463
17260
  init_core();
17464
- init_json();
17465
17261
  init_validation();
17466
17262
  DEFAULT_SNOOZE_DAYS = 7;
17467
17263
  }
@@ -17486,12 +17282,30 @@ var {
17486
17282
 
17487
17283
  // src/cli.ts
17488
17284
  init_core();
17285
+ init_json();
17286
+ function printRepos(repos) {
17287
+ const entries = Object.entries(repos).sort(([a], [b]) => a.localeCompare(b));
17288
+ for (const [remote, info] of entries) {
17289
+ const branch = info.currentBranch ? ` (${info.currentBranch})` : "";
17290
+ console.log(` ${remote}${branch}`);
17291
+ console.log(` ${info.path}`);
17292
+ }
17293
+ }
17294
+ function handleCommandError(err, json) {
17295
+ const msg = err instanceof Error ? err.message : String(err);
17296
+ if (json) {
17297
+ outputJsonError(msg);
17298
+ } else {
17299
+ console.error(`Error: ${msg}`);
17300
+ }
17301
+ process.exit(1);
17302
+ }
17489
17303
  var VERSION10 = (() => {
17490
17304
  try {
17491
- const fs9 = require("fs");
17492
- const path9 = require("path");
17493
- const pkgPath = path9.join(path9.dirname(process.argv[1]), "..", "package.json");
17494
- return JSON.parse(fs9.readFileSync(pkgPath, "utf-8")).version;
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;
17495
17309
  } catch (_err) {
17496
17310
  return "0.0.0";
17497
17311
  }
@@ -17506,6 +17320,7 @@ var LOCAL_ONLY_COMMANDS = [
17506
17320
  "setup",
17507
17321
  "checkSetup",
17508
17322
  "dashboard",
17323
+ "serve",
17509
17324
  "parse-issue-list",
17510
17325
  "check-integration",
17511
17326
  "local-repos",
@@ -17520,106 +17335,571 @@ var LOCAL_ONLY_COMMANDS = [
17520
17335
  var program2 = new Command();
17521
17336
  program2.name("oss-autopilot").description("AI-powered autopilot for managing open source contributions").version(VERSION10).option("--debug", "Enable debug logging");
17522
17337
  program2.command("daily").description("Run daily check on all tracked PRs").option("--json", "Output as JSON").action(async (options) => {
17523
- const { runDaily: runDaily2 } = await Promise.resolve().then(() => (init_daily(), daily_exports));
17524
- await runDaily2({ json: options.json });
17338
+ try {
17339
+ if (options.json) {
17340
+ const { runDaily: runDaily2 } = await Promise.resolve().then(() => (init_daily(), daily_exports));
17341
+ const data = await runDaily2();
17342
+ outputJson(data);
17343
+ } else {
17344
+ const { runDailyForDisplay: runDailyForDisplay2, printDigest: printDigest2 } = await Promise.resolve().then(() => (init_daily(), daily_exports));
17345
+ const result = await runDailyForDisplay2();
17346
+ printDigest2(result.digest, result.capacity, result.commentedIssues);
17347
+ }
17348
+ } catch (err) {
17349
+ handleCommandError(err, options.json);
17350
+ }
17525
17351
  });
17526
17352
  program2.command("status").description("Show current status and stats").option("--json", "Output as JSON").option("--offline", "Use cached data only (no GitHub API calls)").action(async (options) => {
17527
- const { runStatus: runStatus2 } = await Promise.resolve().then(() => (init_status(), status_exports));
17528
- await runStatus2({ json: options.json, offline: options.offline });
17353
+ try {
17354
+ const { runStatus: runStatus2 } = await Promise.resolve().then(() => (init_status(), status_exports));
17355
+ const data = await runStatus2({ offline: options.offline });
17356
+ if (options.json) {
17357
+ outputJson(data);
17358
+ } else {
17359
+ console.log("\n\u{1F4CA} OSS Status\n");
17360
+ console.log(`Merged PRs: ${data.stats.mergedPRs}`);
17361
+ console.log(`Closed PRs: ${data.stats.closedPRs}`);
17362
+ console.log(`Merge Rate: ${data.stats.mergeRate}`);
17363
+ console.log(`Needs Response: ${data.stats.needsResponse}`);
17364
+ if (data.offline) {
17365
+ console.log(`
17366
+ Last Updated: ${data.lastUpdated || "Never"}`);
17367
+ console.log("(Offline mode: showing cached data)");
17368
+ } else {
17369
+ console.log(`
17370
+ Last Run: ${data.lastRunAt || "Never"}`);
17371
+ }
17372
+ console.log("\nRun with --json for structured output");
17373
+ }
17374
+ } catch (err) {
17375
+ handleCommandError(err, options.json);
17376
+ }
17529
17377
  });
17530
17378
  program2.command("search [count]").description("Search for new issues to work on").option("--json", "Output as JSON").action(async (count, options) => {
17531
- const { runSearch: runSearch2 } = await Promise.resolve().then(() => (init_search(), search_exports));
17532
- await runSearch2({ maxResults: parseInt(count) || 5, json: options.json });
17379
+ try {
17380
+ const { runSearch: runSearch2 } = await Promise.resolve().then(() => (init_search(), search_exports));
17381
+ if (!options.json) {
17382
+ console.log(`
17383
+ Searching for issues (max ${parseInt(count) || 5})...
17384
+ `);
17385
+ }
17386
+ const data = await runSearch2({ maxResults: parseInt(count) || 5 });
17387
+ if (options.json) {
17388
+ outputJson(data);
17389
+ } else {
17390
+ if (data.candidates.length === 0) {
17391
+ if (data.rateLimitWarning) {
17392
+ console.warn(`
17393
+ ${data.rateLimitWarning}
17394
+ `);
17395
+ } else {
17396
+ console.log("No matching issues found.");
17397
+ }
17398
+ return;
17399
+ }
17400
+ if (data.rateLimitWarning) {
17401
+ console.warn(`
17402
+ ${data.rateLimitWarning}
17403
+ `);
17404
+ }
17405
+ console.log(`Found ${data.candidates.length} candidates:
17406
+ `);
17407
+ for (const candidate of data.candidates) {
17408
+ const { issue, recommendation, reasonsToApprove, reasonsToSkip, viabilityScore } = candidate;
17409
+ console.log(`[${recommendation.toUpperCase()}] ${issue.repo}#${issue.number}: ${issue.title}`);
17410
+ console.log(` URL: ${issue.url}`);
17411
+ console.log(` Viability: ${viabilityScore}/100`);
17412
+ if (reasonsToApprove.length > 0) console.log(` Approve: ${reasonsToApprove.join(", ")}`);
17413
+ if (reasonsToSkip.length > 0) console.log(` Skip: ${reasonsToSkip.join(", ")}`);
17414
+ console.log("---");
17415
+ }
17416
+ }
17417
+ } catch (err) {
17418
+ handleCommandError(err, options.json);
17419
+ }
17533
17420
  });
17534
17421
  program2.command("vet <issue-url>").description("Vet a specific issue before working on it").option("--json", "Output as JSON").action(async (issueUrl, options) => {
17535
- const { runVet: runVet2 } = await Promise.resolve().then(() => (init_vet(), vet_exports));
17536
- await runVet2({ issueUrl, json: options.json });
17422
+ try {
17423
+ const { runVet: runVet2 } = await Promise.resolve().then(() => (init_vet(), vet_exports));
17424
+ const data = await runVet2({ issueUrl });
17425
+ if (options.json) {
17426
+ outputJson(data);
17427
+ } else {
17428
+ const { issue, recommendation, reasonsToApprove, reasonsToSkip } = data;
17429
+ console.log(`
17430
+ Vetting issue: ${issueUrl}
17431
+ `);
17432
+ console.log(`[${recommendation.toUpperCase()}] ${issue.repo}#${issue.number}: ${issue.title}`);
17433
+ console.log(` URL: ${issue.url}`);
17434
+ if (reasonsToApprove.length > 0) console.log(` Approve: ${reasonsToApprove.join(", ")}`);
17435
+ if (reasonsToSkip.length > 0) console.log(` Skip: ${reasonsToSkip.join(", ")}`);
17436
+ }
17437
+ } catch (err) {
17438
+ handleCommandError(err, options.json);
17439
+ }
17537
17440
  });
17538
17441
  program2.command("track <pr-url>").description("Add a PR to track").option("--json", "Output as JSON").action(async (prUrl, options) => {
17539
- const { runTrack: runTrack2 } = await Promise.resolve().then(() => (init_track(), track_exports));
17540
- await runTrack2({ prUrl, json: options.json });
17442
+ try {
17443
+ const { runTrack: runTrack2 } = await Promise.resolve().then(() => (init_track(), track_exports));
17444
+ const data = await runTrack2({ prUrl });
17445
+ if (options.json) {
17446
+ outputJson(data);
17447
+ } else {
17448
+ console.log(`
17449
+ PR: ${data.pr.repo}#${data.pr.number} - ${data.pr.title}`);
17450
+ console.log("Note: In v2, PRs are tracked automatically via the daily run.");
17451
+ }
17452
+ } catch (err) {
17453
+ handleCommandError(err, options.json);
17454
+ }
17541
17455
  });
17542
17456
  program2.command("untrack <pr-url>").description("Stop tracking a PR").option("--json", "Output as JSON").action(async (prUrl, options) => {
17543
- const { runUntrack: runUntrack2 } = await Promise.resolve().then(() => (init_track(), track_exports));
17544
- await runUntrack2({ prUrl, json: options.json });
17457
+ try {
17458
+ const { runUntrack: runUntrack2 } = await Promise.resolve().then(() => (init_track(), track_exports));
17459
+ const data = await runUntrack2({ prUrl });
17460
+ if (options.json) {
17461
+ outputJson(data);
17462
+ } else {
17463
+ console.log(
17464
+ "Note: In v2, PRs are fetched fresh on each daily run \u2014 there is no local tracking list to remove from."
17465
+ );
17466
+ console.log("Use `shelve` to temporarily hide a PR from the daily summary.");
17467
+ }
17468
+ } catch (err) {
17469
+ handleCommandError(err, options.json);
17470
+ }
17545
17471
  });
17546
17472
  program2.command("read [pr-url]").description("Mark PR comments as read").option("--all", "Mark all PRs as read").option("--json", "Output as JSON").action(async (prUrl, options) => {
17547
- const { runRead: runRead2 } = await Promise.resolve().then(() => (init_read(), read_exports));
17548
- await runRead2({ prUrl, all: options.all, json: options.json });
17473
+ try {
17474
+ const { runRead: runRead2 } = await Promise.resolve().then(() => (init_read(), read_exports));
17475
+ const data = await runRead2({ prUrl, all: options.all });
17476
+ if (options.json) {
17477
+ outputJson(data);
17478
+ } else {
17479
+ console.log("Note: In v2, PR read state is not tracked locally. PRs are fetched fresh on each daily run.");
17480
+ }
17481
+ } catch (err) {
17482
+ handleCommandError(err, options.json);
17483
+ }
17549
17484
  });
17550
17485
  program2.command("comments <pr-url>").description("Show all comments on a PR").option("--bots", "Include bot comments").option("--json", "Output as JSON").action(async (prUrl, options) => {
17551
- const { runComments: runComments2 } = await Promise.resolve().then(() => (init_comments(), comments_exports));
17552
- await runComments2({ prUrl, showBots: options.bots, json: options.json });
17486
+ try {
17487
+ const { runComments: runComments2 } = await Promise.resolve().then(() => (init_comments(), comments_exports));
17488
+ const data = await runComments2({ prUrl, showBots: options.bots });
17489
+ if (options.json) {
17490
+ outputJson(data);
17491
+ } else {
17492
+ console.log(`
17493
+ Fetching comments for: ${prUrl}
17494
+ `);
17495
+ console.log(`## ${data.pr.title}
17496
+ `);
17497
+ console.log(`**Status:** ${data.pr.state} | **Mergeable:** ${data.pr.mergeable ?? "checking..."}`);
17498
+ console.log(`**Branch:** ${data.pr.head} -> ${data.pr.base}`);
17499
+ console.log(`**URL:** ${data.pr.url}
17500
+ `);
17501
+ const REVIEW_STATE_LABELS = {
17502
+ APPROVED: "[Approved]",
17503
+ CHANGES_REQUESTED: "[Changes]"
17504
+ };
17505
+ if (data.reviews.length > 0) {
17506
+ console.log("### Reviews (newest first)\n");
17507
+ for (const review of data.reviews) {
17508
+ const state = REVIEW_STATE_LABELS[review.state] ?? "[Comment]";
17509
+ const time = review.submittedAt ? formatRelativeTime(review.submittedAt) : "";
17510
+ console.log(`${state} **@${review.user}** (${review.state}) - ${time}`);
17511
+ if (review.body) {
17512
+ console.log(`> ${review.body.split("\n").join("\n> ")}
17513
+ `);
17514
+ }
17515
+ }
17516
+ }
17517
+ if (data.reviewComments.length > 0) {
17518
+ console.log("### Inline Comments (newest first)\n");
17519
+ for (const comment of data.reviewComments) {
17520
+ const time = formatRelativeTime(comment.createdAt);
17521
+ console.log(`**@${comment.user}** on \`${comment.path}\` - ${time}`);
17522
+ console.log(`> ${comment.body.split("\n").join("\n> ")}`);
17523
+ console.log("");
17524
+ }
17525
+ }
17526
+ if (data.issueComments.length > 0) {
17527
+ console.log("### Discussion (newest first)\n");
17528
+ for (const comment of data.issueComments) {
17529
+ const time = formatRelativeTime(comment.createdAt);
17530
+ console.log(`**@${comment.user}** - ${time}`);
17531
+ console.log(`> ${comment.body?.split("\n").join("\n> ")}
17532
+ `);
17533
+ }
17534
+ }
17535
+ if (data.reviewComments.length === 0 && data.issueComments.length === 0 && data.reviews.length === 0) {
17536
+ console.log("No comments from other users.\n");
17537
+ }
17538
+ console.log("---");
17539
+ console.log(
17540
+ `**Summary:** ${data.summary.reviewCount} reviews, ${data.summary.inlineCommentCount} inline comments, ${data.summary.discussionCommentCount} discussion comments`
17541
+ );
17542
+ }
17543
+ } catch (err) {
17544
+ handleCommandError(err, options.json);
17545
+ }
17553
17546
  });
17554
17547
  program2.command("post <url> [message...]").description("Post a comment to a PR or issue").option("--stdin", "Read message from stdin").option("--json", "Output as JSON").action(async (url, messageParts, options) => {
17555
- const { runPost: runPost2 } = await Promise.resolve().then(() => (init_comments(), comments_exports));
17556
- const message = options.stdin ? void 0 : messageParts.join(" ");
17557
- await runPost2({ url, message, stdin: options.stdin, json: options.json });
17548
+ try {
17549
+ let message;
17550
+ if (options.stdin) {
17551
+ const chunks = [];
17552
+ for await (const chunk of process.stdin) {
17553
+ chunks.push(chunk);
17554
+ }
17555
+ message = Buffer.concat(chunks).toString("utf-8").trim();
17556
+ } else {
17557
+ message = messageParts.join(" ");
17558
+ }
17559
+ const { runPost: runPost2 } = await Promise.resolve().then(() => (init_comments(), comments_exports));
17560
+ const data = await runPost2({ url, message });
17561
+ if (options.json) {
17562
+ outputJson(data);
17563
+ } else {
17564
+ console.log(`Comment posted: ${data.commentUrl}`);
17565
+ }
17566
+ } catch (err) {
17567
+ handleCommandError(err, options.json);
17568
+ }
17558
17569
  });
17559
17570
  program2.command("claim <issue-url> [message...]").description("Claim an issue by posting a comment").option("--json", "Output as JSON").action(async (issueUrl, messageParts, options) => {
17560
- const { runClaim: runClaim2 } = await Promise.resolve().then(() => (init_comments(), comments_exports));
17561
- const message = messageParts.length > 0 ? messageParts.join(" ") : void 0;
17562
- await runClaim2({ issueUrl, message, json: options.json });
17571
+ try {
17572
+ const { runClaim: runClaim2 } = await Promise.resolve().then(() => (init_comments(), comments_exports));
17573
+ const message = messageParts.length > 0 ? messageParts.join(" ") : void 0;
17574
+ const data = await runClaim2({ issueUrl, message });
17575
+ if (options.json) {
17576
+ outputJson(data);
17577
+ } else {
17578
+ console.log(`Issue claimed: ${data.commentUrl}`);
17579
+ }
17580
+ } catch (err) {
17581
+ handleCommandError(err, options.json);
17582
+ }
17563
17583
  });
17564
17584
  program2.command("config [key] [value]").description("Show or update configuration").option("--json", "Output as JSON").action(async (key, value, options) => {
17565
- const { runConfig: runConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
17566
- await runConfig2({ key, value, json: options.json });
17585
+ try {
17586
+ const { runConfig: runConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
17587
+ const data = await runConfig2({ key, value });
17588
+ if (options.json) {
17589
+ outputJson(data);
17590
+ } else if ("config" in data) {
17591
+ console.log("\n\u2699\uFE0F Current Configuration:\n");
17592
+ console.log(JSON.stringify(data.config, null, 2));
17593
+ } else {
17594
+ console.log(`Set ${data.key} to: ${data.value}`);
17595
+ }
17596
+ } catch (err) {
17597
+ handleCommandError(err, options.json);
17598
+ }
17567
17599
  });
17568
17600
  program2.command("init <username>").description("Initialize with your GitHub username and import open PRs").option("--json", "Output as JSON").action(async (username, options) => {
17569
- const { runInit: runInit2 } = await Promise.resolve().then(() => (init_init(), init_exports));
17570
- await runInit2({ username, json: options.json });
17601
+ try {
17602
+ const { runInit: runInit2 } = await Promise.resolve().then(() => (init_init(), init_exports));
17603
+ const data = await runInit2({ username });
17604
+ if (options.json) {
17605
+ outputJson(data);
17606
+ } else {
17607
+ console.log(`
17608
+ Username set to @${data.username}.`);
17609
+ console.log("Run `oss-autopilot daily` to fetch your open PRs from GitHub.");
17610
+ }
17611
+ } catch (err) {
17612
+ handleCommandError(err, options.json);
17613
+ }
17571
17614
  });
17572
17615
  program2.command("setup").description("Interactive setup / configuration").option("--reset", "Re-run setup even if already complete").option("--set <settings...>", "Set specific values (key=value)").option("--json", "Output as JSON").action(async (options) => {
17573
- const { runSetup: runSetup2 } = await Promise.resolve().then(() => (init_setup(), setup_exports));
17574
- await runSetup2({ reset: options.reset, set: options.set, json: options.json });
17616
+ try {
17617
+ const { runSetup: runSetup2 } = await Promise.resolve().then(() => (init_setup(), setup_exports));
17618
+ const data = await runSetup2({ reset: options.reset, set: options.set });
17619
+ if (options.json) {
17620
+ outputJson(data);
17621
+ } else if ("success" in data) {
17622
+ for (const [key, value] of Object.entries(data.settings)) {
17623
+ console.log(`\u2713 ${key}: ${value}`);
17624
+ }
17625
+ if (data.warnings) {
17626
+ for (const w of data.warnings) {
17627
+ console.warn(w);
17628
+ }
17629
+ }
17630
+ } else if ("setupComplete" in data && data.setupComplete) {
17631
+ console.log("\n\u2699\uFE0F OSS Autopilot Setup\n");
17632
+ console.log("\u2713 Setup already complete!\n");
17633
+ console.log("Current settings:");
17634
+ console.log(` GitHub username: ${data.config.githubUsername || "(not set)"}`);
17635
+ console.log(` Max active PRs: ${data.config.maxActivePRs}`);
17636
+ console.log(` Dormant threshold: ${data.config.dormantThresholdDays} days`);
17637
+ console.log(` Approaching dormant: ${data.config.approachingDormantDays} days`);
17638
+ console.log(` Languages: ${data.config.languages.join(", ")}`);
17639
+ console.log(` Labels: ${data.config.labels.join(", ")}`);
17640
+ console.log(`
17641
+ Run 'setup --reset' to reconfigure.`);
17642
+ } else if ("setupRequired" in data) {
17643
+ console.log("\n\u2699\uFE0F OSS Autopilot Setup\n");
17644
+ console.log("SETUP_REQUIRED");
17645
+ console.log("---");
17646
+ console.log("Please configure the following settings:\n");
17647
+ for (const prompt of data.prompts) {
17648
+ console.log(`SETTING: ${prompt.setting}`);
17649
+ console.log(`PROMPT: ${prompt.prompt}`);
17650
+ const currentVal = Array.isArray(prompt.current) ? prompt.current.join(", ") : prompt.current;
17651
+ console.log(`CURRENT: ${currentVal ?? "(not set)"}`);
17652
+ if (prompt.required) console.log("REQUIRED: true");
17653
+ if (prompt.default !== void 0) {
17654
+ const defaultVal = Array.isArray(prompt.default) ? prompt.default.join(", ") : prompt.default;
17655
+ console.log(`DEFAULT: ${defaultVal}`);
17656
+ }
17657
+ if (prompt.type) console.log(`TYPE: ${prompt.type}`);
17658
+ console.log("");
17659
+ }
17660
+ console.log("---");
17661
+ console.log("END_SETUP_PROMPTS");
17662
+ }
17663
+ } catch (err) {
17664
+ handleCommandError(err, options.json);
17665
+ }
17575
17666
  });
17576
17667
  program2.command("checkSetup").description("Check if setup is complete").option("--json", "Output as JSON").action(async (options) => {
17577
- const { runCheckSetup: runCheckSetup2 } = await Promise.resolve().then(() => (init_setup(), setup_exports));
17578
- await runCheckSetup2({ json: options.json });
17668
+ try {
17669
+ const { runCheckSetup: runCheckSetup2 } = await Promise.resolve().then(() => (init_setup(), setup_exports));
17670
+ const data = await runCheckSetup2();
17671
+ if (options.json) {
17672
+ outputJson(data);
17673
+ } else if (data.setupComplete) {
17674
+ console.log("SETUP_COMPLETE");
17675
+ console.log(`username=${data.username}`);
17676
+ } else {
17677
+ console.log("SETUP_INCOMPLETE");
17678
+ }
17679
+ } catch (err) {
17680
+ handleCommandError(err, options.json);
17681
+ }
17682
+ });
17683
+ var dashboardCmd = program2.command("dashboard").description("Dashboard commands");
17684
+ dashboardCmd.command("serve").description("Start interactive dashboard server").option("--port <port>", "Port to listen on", "3000").option("--no-open", "Do not open browser automatically").action(async (options) => {
17685
+ const port = parseInt(options.port, 10);
17686
+ if (isNaN(port) || port < 1 || port > 65535) {
17687
+ console.error(`Invalid port number: "${options.port}". Must be an integer between 1 and 65535.`);
17688
+ process.exit(1);
17689
+ }
17690
+ const { serveDashboard: serveDashboard2 } = await Promise.resolve().then(() => (init_dashboard(), dashboard_exports));
17691
+ await serveDashboard2({ port, open: options.open });
17579
17692
  });
17580
- program2.command("dashboard").description("Generate HTML stats dashboard").option("--open", "Open in browser").option("--json", "Output as JSON").option("--offline", "Use cached data only (no GitHub API calls)").action(async (options) => {
17693
+ dashboardCmd.option("--open", "Open in browser").option("--json", "Output as JSON").option("--offline", "Use cached data only (no GitHub API calls)").action(async (options) => {
17581
17694
  const { runDashboard: runDashboard2 } = await Promise.resolve().then(() => (init_dashboard(), dashboard_exports));
17582
17695
  await runDashboard2({ open: options.open, json: options.json, offline: options.offline });
17583
17696
  });
17584
17697
  program2.command("parse-issue-list <path>").description("Parse a markdown issue list into structured JSON").option("--json", "Output as JSON").action(async (filePath, options) => {
17585
- const { runParseList: runParseList2 } = await Promise.resolve().then(() => (init_parse_list(), parse_list_exports));
17586
- await runParseList2({ filePath, json: options.json });
17698
+ try {
17699
+ const { runParseList: runParseList2 } = await Promise.resolve().then(() => (init_parse_list(), parse_list_exports));
17700
+ const data = await runParseList2({ filePath });
17701
+ if (options.json) {
17702
+ outputJson(data);
17703
+ } else {
17704
+ const path11 = await import("path");
17705
+ const resolvedPath = path11.resolve(filePath);
17706
+ console.log(`
17707
+ \u{1F4CB} Issue List: ${resolvedPath}
17708
+ `);
17709
+ console.log(`Available: ${data.availableCount} | Completed: ${data.completedCount}
17710
+ `);
17711
+ if (data.available.length > 0) {
17712
+ console.log("--- Available ---");
17713
+ for (const item of data.available) {
17714
+ console.log(` [${item.tier}] ${item.repo}#${item.number}: ${item.title}`);
17715
+ }
17716
+ }
17717
+ if (data.completed.length > 0) {
17718
+ console.log("\n--- Completed ---");
17719
+ for (const item of data.completed) {
17720
+ console.log(` [${item.tier}] ${item.repo}#${item.number}: ${item.title}`);
17721
+ }
17722
+ }
17723
+ }
17724
+ } catch (err) {
17725
+ handleCommandError(err, options.json);
17726
+ }
17587
17727
  });
17588
17728
  program2.command("check-integration").description("Detect new files not referenced by the codebase").option("--base <branch>", "Base branch to compare against", "main").option("--json", "Output as JSON").action(async (options) => {
17589
- const { runCheckIntegration: runCheckIntegration2 } = await Promise.resolve().then(() => (init_check_integration(), check_integration_exports));
17590
- await runCheckIntegration2({ base: options.base, json: options.json });
17729
+ try {
17730
+ const { runCheckIntegration: runCheckIntegration2 } = await Promise.resolve().then(() => (init_check_integration(), check_integration_exports));
17731
+ const data = await runCheckIntegration2({ base: options.base });
17732
+ if (options.json) {
17733
+ outputJson(data);
17734
+ } else if (data.newFiles.length === 0) {
17735
+ console.log("\nNo new code files to check.");
17736
+ } else {
17737
+ console.log(`
17738
+ \u{1F50D} Integration Check (base: ${options.base})
17739
+ `);
17740
+ console.log(`New files: ${data.newFiles.length} | Unreferenced: ${data.unreferencedCount}
17741
+ `);
17742
+ for (const file of data.newFiles) {
17743
+ const status = file.isIntegrated ? "\u2705" : "\u26A0\uFE0F";
17744
+ console.log(`${status} ${file.path}`);
17745
+ if (file.isIntegrated) {
17746
+ console.log(` Referenced by: ${file.referencedBy.join(", ")}`);
17747
+ } else {
17748
+ console.log(" Not referenced by any file");
17749
+ if (file.suggestedEntryPoints && file.suggestedEntryPoints.length > 0) {
17750
+ console.log(` Suggested entry points: ${file.suggestedEntryPoints.join(", ")}`);
17751
+ }
17752
+ }
17753
+ }
17754
+ }
17755
+ } catch (err) {
17756
+ handleCommandError(err, options.json);
17757
+ }
17591
17758
  });
17592
17759
  program2.command("local-repos").description("Scan filesystem for local git clones").option("--scan", "Force re-scan (ignores cache)").option("--paths <dirs...>", "Directories to scan").option("--json", "Output as JSON").action(async (options) => {
17593
- const { runLocalRepos: runLocalRepos2 } = await Promise.resolve().then(() => (init_local_repos(), local_repos_exports));
17594
- await runLocalRepos2({ scan: options.scan, paths: options.paths, json: options.json });
17760
+ try {
17761
+ const { runLocalRepos: runLocalRepos2 } = await Promise.resolve().then(() => (init_local_repos(), local_repos_exports));
17762
+ const data = await runLocalRepos2({ scan: options.scan, paths: options.paths });
17763
+ if (options.json) {
17764
+ outputJson(data);
17765
+ } else if (data.fromCache) {
17766
+ console.log(`
17767
+ \u{1F4C1} Local Repos (cached ${data.cachedAt})
17768
+ `);
17769
+ printRepos(data.repos);
17770
+ } else {
17771
+ console.log(`Found ${Object.keys(data.repos).length} repos:
17772
+ `);
17773
+ printRepos(data.repos);
17774
+ }
17775
+ } catch (err) {
17776
+ handleCommandError(err, options.json);
17777
+ }
17595
17778
  });
17596
17779
  program2.command("startup").description("Run all pre-flight checks and daily fetch in one call").option("--json", "Output as JSON").action(async (options) => {
17597
- const { runStartup: runStartup2 } = await Promise.resolve().then(() => (init_startup(), startup_exports));
17598
- await runStartup2({ json: options.json });
17780
+ try {
17781
+ const { runStartup: runStartup2 } = await Promise.resolve().then(() => (init_startup(), startup_exports));
17782
+ const data = await runStartup2();
17783
+ if (options.json) {
17784
+ outputJson(data);
17785
+ } else {
17786
+ if (!data.setupComplete) {
17787
+ console.log("Setup incomplete. Run /setup-oss first.");
17788
+ } else if (data.authError) {
17789
+ console.error(`Error: ${data.authError}`);
17790
+ } else {
17791
+ console.log(`OSS Autopilot v${data.version}`);
17792
+ console.log(data.daily?.briefSummary ?? "");
17793
+ if (data.dashboardPath) console.log(`Dashboard: ${data.dashboardPath}`);
17794
+ }
17795
+ }
17796
+ } catch (err) {
17797
+ handleCommandError(err, options.json);
17798
+ }
17599
17799
  });
17600
17800
  program2.command("shelve <pr-url>").description("Shelve a PR (exclude from capacity and actionable issues)").option("--json", "Output as JSON").action(async (prUrl, options) => {
17601
- const { runShelve: runShelve2 } = await Promise.resolve().then(() => (init_shelve(), shelve_exports));
17602
- await runShelve2({ prUrl, json: options.json });
17801
+ try {
17802
+ const { runShelve: runShelve2 } = await Promise.resolve().then(() => (init_shelve(), shelve_exports));
17803
+ const data = await runShelve2({ prUrl });
17804
+ if (options.json) {
17805
+ outputJson(data);
17806
+ } else if (data.shelved) {
17807
+ console.log(`Shelved: ${prUrl}`);
17808
+ console.log("This PR is now excluded from capacity and actionable issues.");
17809
+ console.log("It will auto-unshelve if a maintainer engages.");
17810
+ } else {
17811
+ console.log("PR is already shelved.");
17812
+ }
17813
+ } catch (err) {
17814
+ handleCommandError(err, options.json);
17815
+ }
17603
17816
  });
17604
17817
  program2.command("unshelve <pr-url>").description("Unshelve a PR (include in capacity and actionable issues again)").option("--json", "Output as JSON").action(async (prUrl, options) => {
17605
- const { runUnshelve: runUnshelve2 } = await Promise.resolve().then(() => (init_shelve(), shelve_exports));
17606
- await runUnshelve2({ prUrl, json: options.json });
17818
+ try {
17819
+ const { runUnshelve: runUnshelve2 } = await Promise.resolve().then(() => (init_shelve(), shelve_exports));
17820
+ const data = await runUnshelve2({ prUrl });
17821
+ if (options.json) {
17822
+ outputJson(data);
17823
+ } else if (data.unshelved) {
17824
+ console.log(`Unshelved: ${prUrl}`);
17825
+ console.log("This PR is now active again.");
17826
+ } else {
17827
+ console.log("PR was not shelved.");
17828
+ }
17829
+ } catch (err) {
17830
+ handleCommandError(err, options.json);
17831
+ }
17607
17832
  });
17608
17833
  program2.command("dismiss <issue-url>").description("Dismiss issue reply notifications (resurfaces on new activity)").option("--json", "Output as JSON").action(async (issueUrl, options) => {
17609
- const { runDismiss: runDismiss2 } = await Promise.resolve().then(() => (init_dismiss(), dismiss_exports));
17610
- await runDismiss2({ issueUrl, json: options.json });
17834
+ try {
17835
+ const { runDismiss: runDismiss2 } = await Promise.resolve().then(() => (init_dismiss(), dismiss_exports));
17836
+ const data = await runDismiss2({ issueUrl });
17837
+ if (options.json) {
17838
+ outputJson(data);
17839
+ } else if (data.dismissed) {
17840
+ console.log(`Dismissed: ${issueUrl}`);
17841
+ console.log("Issue reply notifications are now muted.");
17842
+ console.log("New responses after this point will resurface automatically.");
17843
+ } else {
17844
+ console.log("Issue is already dismissed.");
17845
+ }
17846
+ } catch (err) {
17847
+ handleCommandError(err, options.json);
17848
+ }
17611
17849
  });
17612
17850
  program2.command("undismiss <issue-url>").description("Undismiss an issue (re-enable reply notifications)").option("--json", "Output as JSON").action(async (issueUrl, options) => {
17613
- const { runUndismiss: runUndismiss2 } = await Promise.resolve().then(() => (init_dismiss(), dismiss_exports));
17614
- await runUndismiss2({ issueUrl, json: options.json });
17851
+ try {
17852
+ const { runUndismiss: runUndismiss2 } = await Promise.resolve().then(() => (init_dismiss(), dismiss_exports));
17853
+ const data = await runUndismiss2({ issueUrl });
17854
+ if (options.json) {
17855
+ outputJson(data);
17856
+ } else if (data.undismissed) {
17857
+ console.log(`Undismissed: ${issueUrl}`);
17858
+ console.log("Issue reply notifications are active again.");
17859
+ } else {
17860
+ console.log("Issue was not dismissed.");
17861
+ }
17862
+ } catch (err) {
17863
+ handleCommandError(err, options.json);
17864
+ }
17615
17865
  });
17616
17866
  program2.command("snooze <pr-url>").description("Snooze CI failure notifications for a PR").requiredOption("--reason <reason>", 'Reason for snoozing (e.g., "upstream infrastructure issue")').option("--days <days>", "Number of days to snooze (default: 7)", "7").option("--json", "Output as JSON").action(async (prUrl, options) => {
17617
- const { runSnooze: runSnooze2 } = await Promise.resolve().then(() => (init_snooze(), snooze_exports));
17618
- await runSnooze2({ prUrl, reason: options.reason, days: parseInt(options.days, 10), json: options.json });
17867
+ try {
17868
+ const { runSnooze: runSnooze2 } = await Promise.resolve().then(() => (init_snooze(), snooze_exports));
17869
+ const data = await runSnooze2({ prUrl, reason: options.reason, days: parseInt(options.days, 10) });
17870
+ if (options.json) {
17871
+ outputJson(data);
17872
+ } else if (data.snoozed) {
17873
+ console.log(`Snoozed: ${prUrl}`);
17874
+ console.log(`Reason: ${data.reason}`);
17875
+ console.log(`Duration: ${data.days} day${data.days === 1 ? "" : "s"}`);
17876
+ console.log(`Expires: ${data.expiresAt ? new Date(data.expiresAt).toLocaleString() : "unknown"}`);
17877
+ console.log("CI failure notifications are now muted for this PR.");
17878
+ } else {
17879
+ console.log("PR is already snoozed.");
17880
+ if (data.expiresAt) {
17881
+ console.log(`Expires: ${new Date(data.expiresAt).toLocaleString()}`);
17882
+ }
17883
+ }
17884
+ } catch (err) {
17885
+ handleCommandError(err, options.json);
17886
+ }
17619
17887
  });
17620
17888
  program2.command("unsnooze <pr-url>").description("Unsnooze a PR (re-enable CI failure notifications)").option("--json", "Output as JSON").action(async (prUrl, options) => {
17621
- const { runUnsnooze: runUnsnooze2 } = await Promise.resolve().then(() => (init_snooze(), snooze_exports));
17622
- await runUnsnooze2({ prUrl, json: options.json });
17889
+ try {
17890
+ const { runUnsnooze: runUnsnooze2 } = await Promise.resolve().then(() => (init_snooze(), snooze_exports));
17891
+ const data = await runUnsnooze2({ prUrl });
17892
+ if (options.json) {
17893
+ outputJson(data);
17894
+ } else if (data.unsnoozed) {
17895
+ console.log(`Unsnoozed: ${prUrl}`);
17896
+ console.log("CI failure notifications are active again for this PR.");
17897
+ } else {
17898
+ console.log("PR was not snoozed.");
17899
+ }
17900
+ } catch (err) {
17901
+ handleCommandError(err, options.json);
17902
+ }
17623
17903
  });
17624
17904
  program2.hook("preAction", async (thisCommand, actionCommand) => {
17625
17905
  const globalOpts = thisCommand.opts();