@oss-autopilot/core 0.41.0 → 0.42.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/cli.bundle.cjs +1552 -1318
  2. package/dist/cli.js +593 -69
  3. package/dist/commands/check-integration.d.ts +3 -3
  4. package/dist/commands/check-integration.js +10 -43
  5. package/dist/commands/comments.d.ts +6 -9
  6. package/dist/commands/comments.js +102 -252
  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 +33 -45
  11. package/dist/commands/dashboard-data.js +7 -6
  12. package/dist/commands/dashboard-server.d.ts +14 -0
  13. package/dist/commands/dashboard-server.js +362 -0
  14. package/dist/commands/dashboard.d.ts +5 -0
  15. package/dist/commands/dashboard.js +51 -1
  16. package/dist/commands/dismiss.d.ts +13 -5
  17. package/dist/commands/dismiss.js +4 -24
  18. package/dist/commands/index.d.ts +33 -0
  19. package/dist/commands/index.js +22 -0
  20. package/dist/commands/init.d.ts +5 -4
  21. package/dist/commands/init.js +4 -14
  22. package/dist/commands/local-repos.d.ts +4 -5
  23. package/dist/commands/local-repos.js +6 -33
  24. package/dist/commands/parse-list.d.ts +3 -4
  25. package/dist/commands/parse-list.js +8 -39
  26. package/dist/commands/read.d.ts +11 -5
  27. package/dist/commands/read.js +4 -18
  28. package/dist/commands/search.d.ts +3 -3
  29. package/dist/commands/search.js +39 -65
  30. package/dist/commands/setup.d.ts +34 -5
  31. package/dist/commands/setup.js +75 -166
  32. package/dist/commands/shelve.d.ts +13 -5
  33. package/dist/commands/shelve.js +4 -24
  34. package/dist/commands/snooze.d.ts +15 -9
  35. package/dist/commands/snooze.js +16 -59
  36. package/dist/commands/startup.d.ts +11 -6
  37. package/dist/commands/startup.js +44 -82
  38. package/dist/commands/status.d.ts +3 -3
  39. package/dist/commands/status.js +10 -29
  40. package/dist/commands/track.d.ts +10 -9
  41. package/dist/commands/track.js +17 -39
  42. package/dist/commands/validation.d.ts +2 -2
  43. package/dist/commands/validation.js +7 -15
  44. package/dist/commands/vet.d.ts +3 -3
  45. package/dist/commands/vet.js +16 -26
  46. package/dist/core/errors.d.ts +9 -0
  47. package/dist/core/errors.js +17 -0
  48. package/dist/core/github-stats.d.ts +14 -21
  49. package/dist/core/github-stats.js +84 -138
  50. package/dist/core/http-cache.d.ts +6 -0
  51. package/dist/core/http-cache.js +16 -4
  52. package/dist/core/index.d.ts +2 -1
  53. package/dist/core/index.js +2 -1
  54. package/dist/core/issue-conversation.js +4 -4
  55. package/dist/core/issue-discovery.js +14 -14
  56. package/dist/core/issue-vetting.js +17 -17
  57. package/dist/core/pr-monitor.d.ts +6 -20
  58. package/dist/core/pr-monitor.js +11 -52
  59. package/dist/core/state.js +4 -5
  60. package/dist/core/utils.d.ts +11 -0
  61. package/dist/core/utils.js +21 -0
  62. package/dist/formatters/json.d.ts +58 -0
  63. package/package.json +5 -1
@@ -7,22 +7,11 @@
7
7
  * `node cli.bundle.cjs startup --json` call, reducing UI noise in Claude Code.
8
8
  */
9
9
  import * as fs from 'fs';
10
- import * as path from 'path';
11
10
  import { execFile } from 'child_process';
12
- import { getStateManager, getGitHubToken } from '../core/index.js';
13
- import { outputJson, outputJsonError } from '../formatters/json.js';
11
+ import { getStateManager, getGitHubToken, getCLIVersion } from '../core/index.js';
12
+ import { errorMessage } from '../core/errors.js';
14
13
  import { executeDailyCheck } from './daily.js';
15
14
  import { writeDashboardFromState } from './dashboard.js';
16
- function getVersion() {
17
- try {
18
- const pkgPath = path.join(path.dirname(process.argv[1]), '..', 'package.json');
19
- return JSON.parse(fs.readFileSync(pkgPath, 'utf-8')).version;
20
- }
21
- catch (error) {
22
- console.error('[STARTUP] Failed to detect CLI version:', error instanceof Error ? error.message : error);
23
- return '0.0.0';
24
- }
25
- }
26
15
  /**
27
16
  * Parse issueListPath from a config file's YAML frontmatter.
28
17
  * Returns the path string or undefined if not found.
@@ -76,7 +65,7 @@ export function detectIssueList() {
76
65
  }
77
66
  }
78
67
  catch (error) {
79
- console.error('[STARTUP] Failed to read config:', error instanceof Error ? error.message : error);
68
+ console.error('[STARTUP] Failed to read config:', errorMessage(error));
80
69
  }
81
70
  }
82
71
  // 2. Probe known paths
@@ -99,7 +88,7 @@ export function detectIssueList() {
99
88
  return { path: issueListPath, source, availableCount, completedCount };
100
89
  }
101
90
  catch (error) {
102
- console.error(`[STARTUP] Failed to read issue list at ${issueListPath}:`, error instanceof Error ? error.message : error);
91
+ console.error(`[STARTUP] Failed to read issue list at ${issueListPath}:`, errorMessage(error));
103
92
  return { path: issueListPath, source, availableCount: 0, completedCount: 0 };
104
93
  }
105
94
  }
@@ -113,85 +102,58 @@ function openInBrowser(filePath) {
113
102
  }
114
103
  });
115
104
  }
116
- export async function runStartup(options) {
117
- const version = getVersion();
105
+ /**
106
+ * Run startup checks and return structured output.
107
+ * Returns StartupOutput with one of three shapes:
108
+ * 1. Setup incomplete: { version, setupComplete: false }
109
+ * 2. Auth failure: { version, setupComplete: true, authError: "..." }
110
+ * 3. Success: { version, setupComplete: true, daily, dashboardPath?, issueList? }
111
+ *
112
+ * Errors from the daily check propagate to the caller.
113
+ */
114
+ export async function runStartup() {
115
+ const version = getCLIVersion();
118
116
  const stateManager = getStateManager();
119
117
  // 1. Check setup
120
118
  if (!stateManager.isSetupComplete()) {
121
- if (options.json) {
122
- outputJson({ version, setupComplete: false });
123
- }
124
- else {
125
- console.log('Setup incomplete. Run /setup-oss first.');
126
- }
127
- return;
119
+ return { version, setupComplete: false };
128
120
  }
129
121
  // 2. Check auth
130
122
  const token = getGitHubToken();
131
123
  if (!token) {
132
- if (options.json) {
133
- outputJson({
134
- version,
135
- setupComplete: true,
136
- authError: 'GitHub authentication required. Install GitHub CLI (https://cli.github.com/) and run "gh auth login", or set GITHUB_TOKEN.',
137
- });
138
- }
139
- else {
140
- console.error('Error: GitHub authentication required.');
141
- }
142
- return;
124
+ return {
125
+ version,
126
+ setupComplete: true,
127
+ authError: 'GitHub authentication required. Install GitHub CLI (https://cli.github.com/) and run "gh auth login", or set GITHUB_TOKEN.',
128
+ };
143
129
  }
144
130
  // 3. Run daily check
131
+ const daily = await executeDailyCheck(token);
132
+ // 4. Generate dashboard from state (just saved by daily)
133
+ // Skip opening on first run (0 PRs) — the welcome flow handles onboarding
134
+ let dashboardPath;
135
+ let dashboardOpened = false;
145
136
  try {
146
- const daily = await executeDailyCheck(token);
147
- // 4. Generate dashboard from state (just saved by daily)
148
- // Skip opening on first run (0 PRs) — the welcome flow handles onboarding
149
- let dashboardPath;
150
- let dashboardOpened = false;
151
- try {
152
- dashboardPath = writeDashboardFromState();
153
- if (daily.digest.summary.totalActivePRs > 0) {
154
- openInBrowser(dashboardPath);
155
- dashboardOpened = true;
156
- }
157
- }
158
- catch (error) {
159
- console.error('[STARTUP] Dashboard generation failed:', error instanceof Error ? error.message : error);
160
- }
161
- // Append dashboard status to brief summary (only startup opens the browser, not daily)
162
- if (dashboardOpened) {
163
- daily.briefSummary += ' | Dashboard opened in browser';
164
- }
165
- // 5. Detect issue list
166
- const issueList = detectIssueList();
167
- // 6. Output
168
- if (options.json) {
169
- outputJson({
170
- version,
171
- setupComplete: true,
172
- daily,
173
- dashboardPath,
174
- issueList,
175
- });
176
- }
177
- else {
178
- console.log(`OSS Autopilot v${version}`);
179
- console.log(daily.briefSummary);
180
- if (dashboardPath)
181
- console.log(`Dashboard: ${dashboardPath}`);
137
+ dashboardPath = writeDashboardFromState();
138
+ if (daily.digest.summary.totalActivePRs > 0) {
139
+ openInBrowser(dashboardPath);
140
+ dashboardOpened = true;
182
141
  }
183
142
  }
184
143
  catch (error) {
185
- const msg = error instanceof Error ? error.message : String(error);
186
- if (options.json) {
187
- outputJsonError(`Daily check failed: ${msg}`);
188
- }
189
- else {
190
- console.error(`[FATAL] Daily check failed: ${msg}`);
191
- if (error instanceof Error && error.stack) {
192
- console.error(error.stack);
193
- }
194
- }
195
- process.exit(1);
144
+ console.error('[STARTUP] Dashboard generation failed:', errorMessage(error));
145
+ }
146
+ // Append dashboard status to brief summary (only startup opens the browser, not daily)
147
+ if (dashboardOpened) {
148
+ daily.briefSummary += ' | Dashboard opened in browser';
196
149
  }
150
+ // 5. Detect issue list
151
+ const issueList = detectIssueList();
152
+ return {
153
+ version,
154
+ setupComplete: true,
155
+ daily,
156
+ dashboardPath,
157
+ issueList,
158
+ };
197
159
  }
@@ -2,9 +2,9 @@
2
2
  * Status command
3
3
  * Shows current status and stats
4
4
  */
5
+ import type { StatusOutput } from '../formatters/json.js';
5
6
  interface StatusOptions {
6
- json?: boolean;
7
7
  offline?: boolean;
8
8
  }
9
- export declare function runStatus(options: StatusOptions): Promise<void>;
10
- export {};
9
+ export type { StatusOutput };
10
+ export declare function runStatus(options: StatusOptions): Promise<StatusOutput>;
@@ -3,7 +3,6 @@
3
3
  * Shows current status and stats
4
4
  */
5
5
  import { getStateManager } from '../core/index.js';
6
- import { outputJson } from '../formatters/json.js';
7
6
  export async function runStatus(options) {
8
7
  const stateManager = getStateManager();
9
8
  const stats = stateManager.getStats();
@@ -11,33 +10,15 @@ export async function runStatus(options) {
11
10
  // Status always reads from local state (no API calls), so offline mode
12
11
  // simply adds metadata about cache freshness.
13
12
  const lastUpdated = state.lastDigestAt || state.lastRunAt;
14
- if (options.json) {
15
- // Extract only the stats we want to output (exclude totalTracked)
16
- const { totalTracked: _totalTracked, ...outputStats } = stats;
17
- const output = {
18
- stats: outputStats,
19
- lastRunAt: state.lastRunAt,
20
- };
21
- if (options.offline) {
22
- output.offline = true;
23
- output.lastUpdated = lastUpdated;
24
- }
25
- outputJson(output);
26
- }
27
- else {
28
- // Simple console output
29
- console.log('\nšŸ“Š OSS Status\n');
30
- console.log(`Merged PRs: ${stats.mergedPRs}`);
31
- console.log(`Closed PRs: ${stats.closedPRs}`);
32
- console.log(`Merge Rate: ${stats.mergeRate}`);
33
- console.log(`Needs Response: ${stats.needsResponse}`);
34
- if (options.offline) {
35
- console.log(`\nLast Updated: ${lastUpdated || 'Never'}`);
36
- console.log('(Offline mode: showing cached data)');
37
- }
38
- else {
39
- console.log(`\nLast Run: ${state.lastRunAt || 'Never'}`);
40
- }
41
- console.log('\nRun with --json for structured output');
13
+ // Extract only the stats we want to output (exclude totalTracked)
14
+ const { totalTracked: _totalTracked, ...outputStats } = stats;
15
+ const output = {
16
+ stats: outputStats,
17
+ lastRunAt: state.lastRunAt,
18
+ };
19
+ if (options.offline) {
20
+ output.offline = true;
21
+ output.lastUpdated = lastUpdated;
42
22
  }
23
+ return output;
43
24
  }
@@ -3,14 +3,15 @@
3
3
  * In v2, PRs are fetched fresh from GitHub on each `daily` run.
4
4
  * These commands are preserved for backward compatibility.
5
5
  */
6
- interface TrackOptions {
7
- prUrl: string;
8
- json?: boolean;
6
+ import type { TrackOutput } from '../formatters/json.js';
7
+ export interface UntrackOutput {
8
+ removed: boolean;
9
+ url: string;
10
+ message: string;
9
11
  }
10
- interface UntrackOptions {
12
+ export declare function runTrack(options: {
11
13
  prUrl: string;
12
- json?: boolean;
13
- }
14
- export declare function runTrack(options: TrackOptions): Promise<void>;
15
- export declare function runUntrack(options: UntrackOptions): Promise<void>;
16
- export {};
14
+ }): Promise<TrackOutput>;
15
+ export declare function runUntrack(options: {
16
+ prUrl: string;
17
+ }): Promise<UntrackOutput>;
@@ -3,57 +3,35 @@
3
3
  * In v2, PRs are fetched fresh from GitHub on each `daily` run.
4
4
  * These commands are preserved for backward compatibility.
5
5
  */
6
- import { getOctokit, getGitHubToken } from '../core/index.js';
7
- import { outputJson, outputJsonError } from '../formatters/json.js';
6
+ import { getOctokit, requireGitHubToken } from '../core/index.js';
8
7
  import { validateUrl, PR_URL_PATTERN, validateGitHubUrl } from './validation.js';
9
8
  import { parseGitHubUrl } from '../core/utils.js';
10
9
  export async function runTrack(options) {
11
10
  validateUrl(options.prUrl);
12
- validateGitHubUrl(options.prUrl, PR_URL_PATTERN, 'PR', options.json);
13
- // Token is guaranteed by the preAction hook in cli.ts for non-LOCAL_ONLY_COMMANDS.
14
- const token = getGitHubToken();
11
+ validateGitHubUrl(options.prUrl, PR_URL_PATTERN, 'PR');
12
+ const token = requireGitHubToken();
15
13
  const octokit = getOctokit(token);
16
14
  const parsed = parseGitHubUrl(options.prUrl);
17
15
  if (!parsed || parsed.type !== 'pull') {
18
- if (options.json) {
19
- outputJsonError(`Invalid PR URL: ${options.prUrl}`);
20
- }
21
- else {
22
- console.error(`Error: Invalid PR URL: ${options.prUrl}`);
23
- }
24
- process.exit(1);
16
+ throw new Error(`Invalid PR URL: ${options.prUrl}`);
25
17
  }
26
18
  const { owner, repo, number } = parsed;
27
- if (!options.json) {
28
- console.log(`\nšŸ“Œ Fetching PR: ${options.prUrl}\n`);
29
- }
30
19
  const { data: ghPR } = await octokit.pulls.get({ owner, repo, pull_number: number });
31
- const pr = {
32
- repo: `${owner}/${repo}`,
33
- number,
34
- title: ghPR.title,
35
- url: options.prUrl,
20
+ return {
21
+ pr: {
22
+ repo: `${owner}/${repo}`,
23
+ number,
24
+ title: ghPR.title,
25
+ url: options.prUrl,
26
+ },
36
27
  };
37
- if (options.json) {
38
- outputJson({ pr });
39
- }
40
- else {
41
- console.log(`PR: ${pr.repo}#${pr.number} - ${pr.title}`);
42
- console.log('Note: In v2, PRs are tracked automatically via the daily run.');
43
- }
44
28
  }
45
29
  export async function runUntrack(options) {
46
30
  validateUrl(options.prUrl);
47
- validateGitHubUrl(options.prUrl, PR_URL_PATTERN, 'PR', options.json);
48
- if (options.json) {
49
- outputJson({
50
- removed: false,
51
- url: options.prUrl,
52
- message: 'In v2, PRs are fetched fresh on each daily run — there is no local tracking list to remove from.',
53
- });
54
- }
55
- else {
56
- console.log('Note: In v2, PRs are fetched fresh on each daily run — there is no local tracking list to remove from.');
57
- console.log('Use `shelve` to temporarily hide a PR from the daily summary.');
58
- }
31
+ validateGitHubUrl(options.prUrl, PR_URL_PATTERN, 'PR');
32
+ return {
33
+ removed: false,
34
+ url: options.prUrl,
35
+ message: 'In v2, PRs are fetched fresh on each daily run — there is no local tracking list to remove from.',
36
+ };
59
37
  }
@@ -6,9 +6,9 @@ export declare const PR_URL_PATTERN: RegExp;
6
6
  /** Matches GitHub issue URLs: https://github.com/owner/repo/issues/123 */
7
7
  export declare const ISSUE_URL_PATTERN: RegExp;
8
8
  /**
9
- * Validate a GitHub URL against a pattern. Exits with error if invalid.
9
+ * Validate a GitHub URL against a pattern. Throws if invalid.
10
10
  */
11
- export declare function validateGitHubUrl(url: string, pattern: RegExp, entityType: 'PR' | 'issue', json?: boolean): void;
11
+ export declare function validateGitHubUrl(url: string, pattern: RegExp, entityType: 'PR' | 'issue'): void;
12
12
  /**
13
13
  * Validate that a URL does not exceed the maximum allowed length.
14
14
  * Returns the URL if valid, throws if too long.
@@ -2,7 +2,6 @@
2
2
  * Shared validation patterns and helpers for CLI commands.
3
3
  */
4
4
  import { ValidationError } from '../core/errors.js';
5
- import { outputJsonError } from '../formatters/json.js';
6
5
  /** Matches GitHub PR URLs: https://github.com/owner/repo/pull/123 */
7
6
  export const PR_URL_PATTERN = /^https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+$/;
8
7
  /** Matches GitHub issue URLs: https://github.com/owner/repo/issues/123 */
@@ -16,20 +15,13 @@ const MAX_MESSAGE_LENGTH = 1000;
16
15
  /** Pattern for valid GitHub repository identifiers */
17
16
  const REPO_PATTERN = /^[a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+$/;
18
17
  /**
19
- * Validate a GitHub URL against a pattern. Exits with error if invalid.
18
+ * Validate a GitHub URL against a pattern. Throws if invalid.
20
19
  */
21
- export function validateGitHubUrl(url, pattern, entityType, json) {
20
+ export function validateGitHubUrl(url, pattern, entityType) {
22
21
  if (pattern.test(url))
23
22
  return;
24
23
  const example = entityType === 'PR' ? 'https://github.com/owner/repo/pull/123' : 'https://github.com/owner/repo/issues/123';
25
- const msg = `Invalid ${entityType} URL: ${url}. Expected format: ${example}`;
26
- if (json) {
27
- outputJsonError(msg);
28
- }
29
- else {
30
- console.error(`Error: ${msg}`);
31
- }
32
- process.exit(1);
24
+ throw new ValidationError(`Invalid ${entityType} URL: ${url}. Expected format: ${example}`);
33
25
  }
34
26
  /**
35
27
  * Validate that a URL does not exceed the maximum allowed length.
@@ -37,7 +29,7 @@ export function validateGitHubUrl(url, pattern, entityType, json) {
37
29
  */
38
30
  export function validateUrl(url) {
39
31
  if (url.length > MAX_URL_LENGTH) {
40
- throw new Error(`URL exceeds maximum length of ${MAX_URL_LENGTH} characters`);
32
+ throw new ValidationError(`URL exceeds maximum length of ${MAX_URL_LENGTH} characters`);
41
33
  }
42
34
  return url;
43
35
  }
@@ -47,7 +39,7 @@ export function validateUrl(url) {
47
39
  */
48
40
  export function validatePRNumber(num) {
49
41
  if (!Number.isInteger(num) || num < 1 || num > MAX_PR_NUMBER) {
50
- throw new Error(`PR number must be a positive integer up to ${MAX_PR_NUMBER}`);
42
+ throw new ValidationError(`PR number must be a positive integer up to ${MAX_PR_NUMBER}`);
51
43
  }
52
44
  return num;
53
45
  }
@@ -57,7 +49,7 @@ export function validatePRNumber(num) {
57
49
  */
58
50
  export function validateMessage(message) {
59
51
  if (message.length > MAX_MESSAGE_LENGTH) {
60
- throw new Error(`Message exceeds maximum length of ${MAX_MESSAGE_LENGTH} characters`);
52
+ throw new ValidationError(`Message exceeds maximum length of ${MAX_MESSAGE_LENGTH} characters`);
61
53
  }
62
54
  return message;
63
55
  }
@@ -67,7 +59,7 @@ export function validateMessage(message) {
67
59
  */
68
60
  export function validateRepoIdentifier(repo) {
69
61
  if (!REPO_PATTERN.test(repo)) {
70
- throw new Error(`Invalid repository format: "${repo}". Expected "owner/repo".`);
62
+ throw new ValidationError(`Invalid repository format: "${repo}". Expected "owner/repo".`);
71
63
  }
72
64
  return repo;
73
65
  }
@@ -2,9 +2,9 @@
2
2
  * Vet command
3
3
  * Vets a specific issue before working on it
4
4
  */
5
+ import { type VetOutput } from '../formatters/json.js';
6
+ export { type VetOutput } from '../formatters/json.js';
5
7
  interface VetOptions {
6
8
  issueUrl: string;
7
- json?: boolean;
8
9
  }
9
- export declare function runVet(options: VetOptions): Promise<void>;
10
- export {};
10
+ export declare function runVet(options: VetOptions): Promise<VetOutput>;
@@ -2,35 +2,25 @@
2
2
  * Vet command
3
3
  * Vets a specific issue before working on it
4
4
  */
5
- import { IssueDiscovery, getGitHubToken } from '../core/index.js';
6
- import { outputJson } from '../formatters/json.js';
5
+ import { IssueDiscovery, requireGitHubToken } from '../core/index.js';
7
6
  import { validateUrl } from './validation.js';
8
7
  export async function runVet(options) {
9
8
  validateUrl(options.issueUrl);
10
- // Token is guaranteed by the preAction hook in cli.ts for non-LOCAL_ONLY_COMMANDS.
11
- const token = getGitHubToken();
9
+ const token = requireGitHubToken();
12
10
  const discovery = new IssueDiscovery(token);
13
- if (!options.json) {
14
- console.log(`\nšŸ” Vetting issue: ${options.issueUrl}\n`);
15
- }
16
11
  const candidate = await discovery.vetIssue(options.issueUrl);
17
- if (options.json) {
18
- outputJson({
19
- issue: {
20
- repo: candidate.issue.repo,
21
- number: candidate.issue.number,
22
- title: candidate.issue.title,
23
- url: candidate.issue.url,
24
- labels: candidate.issue.labels,
25
- },
26
- recommendation: candidate.recommendation,
27
- reasonsToApprove: candidate.reasonsToApprove,
28
- reasonsToSkip: candidate.reasonsToSkip,
29
- projectHealth: candidate.projectHealth,
30
- vettingResult: candidate.vettingResult,
31
- });
32
- }
33
- else {
34
- console.log(discovery.formatCandidate(candidate));
35
- }
12
+ return {
13
+ issue: {
14
+ repo: candidate.issue.repo,
15
+ number: candidate.issue.number,
16
+ title: candidate.issue.title,
17
+ url: candidate.issue.url,
18
+ labels: candidate.issue.labels,
19
+ },
20
+ recommendation: candidate.recommendation,
21
+ reasonsToApprove: candidate.reasonsToApprove,
22
+ reasonsToSkip: candidate.reasonsToSkip,
23
+ projectHealth: candidate.projectHealth,
24
+ vettingResult: candidate.vettingResult,
25
+ };
36
26
  }
@@ -22,3 +22,12 @@ export declare class ConfigurationError extends OssAutopilotError {
22
22
  export declare class ValidationError extends OssAutopilotError {
23
23
  constructor(message: string);
24
24
  }
25
+ /**
26
+ * Extract a human-readable message from an unknown error value.
27
+ */
28
+ export declare function errorMessage(e: unknown): string;
29
+ /**
30
+ * Safely extract an HTTP status code from an unknown error (e.g. Octokit errors).
31
+ * Returns undefined if the error doesn't have a numeric `status` property.
32
+ */
33
+ export declare function getHttpStatusCode(error: unknown): number | undefined;
@@ -32,3 +32,20 @@ export class ValidationError extends OssAutopilotError {
32
32
  this.name = 'ValidationError';
33
33
  }
34
34
  }
35
+ /**
36
+ * Extract a human-readable message from an unknown error value.
37
+ */
38
+ export function errorMessage(e) {
39
+ return e instanceof Error ? e.message : String(e);
40
+ }
41
+ /**
42
+ * Safely extract an HTTP status code from an unknown error (e.g. Octokit errors).
43
+ * Returns undefined if the error doesn't have a numeric `status` property.
44
+ */
45
+ export function getHttpStatusCode(error) {
46
+ if (error && typeof error === 'object' && 'status' in error) {
47
+ const status = error.status;
48
+ return typeof status === 'number' && Number.isFinite(status) ? status : undefined;
49
+ }
50
+ return undefined;
51
+ }
@@ -4,35 +4,28 @@
4
4
  */
5
5
  import { Octokit } from '@octokit/rest';
6
6
  import { ClosedPR, MergedPR } from './types.js';
7
+ /** TTL for cached PR count results (1 hour). */
8
+ export declare const PR_COUNTS_CACHE_TTL_MS: number;
9
+ /** Return type shared by both merged and closed PR count functions. */
10
+ export interface PRCountsResult<R> {
11
+ repos: Map<string, R>;
12
+ monthlyCounts: Record<string, number>;
13
+ monthlyOpenedCounts: Record<string, number>;
14
+ dailyActivityCounts: Record<string, number>;
15
+ }
7
16
  /**
8
17
  * Fetch merged PR counts and latest merge dates per repository for the configured user.
9
18
  * Also builds a monthly histogram of all merges for the contribution timeline.
10
19
  */
11
- export declare function fetchUserMergedPRCounts(octokit: Octokit, githubUsername: string): Promise<{
12
- repos: Map<string, {
13
- count: number;
14
- lastMergedAt: string;
15
- }>;
16
- monthlyCounts: Record<string, number>;
17
- monthlyOpenedCounts: Record<string, number>;
18
- dailyActivityCounts: Record<string, number>;
19
- }>;
20
+ export declare function fetchUserMergedPRCounts(octokit: Octokit, githubUsername: string): Promise<PRCountsResult<{
21
+ count: number;
22
+ lastMergedAt: string;
23
+ }>>;
20
24
  /**
21
25
  * Fetch closed-without-merge PR counts per repository for the configured user.
22
26
  * Used to populate closedWithoutMergeCount in repo scores for accurate merge rate.
23
27
  */
24
- export declare function fetchUserClosedPRCounts(octokit: Octokit, githubUsername: string): Promise<{
25
- repos: Map<string, number>;
26
- monthlyCounts: Record<string, number>;
27
- monthlyOpenedCounts: Record<string, number>;
28
- dailyActivityCounts: Record<string, number>;
29
- }>;
30
- /**
31
- * Fetch GitHub star counts for a list of repositories.
32
- * Used to populate stargazersCount in repo scores for dashboard filtering by minStars.
33
- * Fetches concurrently with per-repo error isolation (missing/private repos are skipped).
34
- */
35
- export declare function fetchRepoStarCounts(octokit: Octokit, repos: string[]): Promise<Map<string, number>>;
28
+ export declare function fetchUserClosedPRCounts(octokit: Octokit, githubUsername: string): Promise<PRCountsResult<number>>;
36
29
  /**
37
30
  * Shared helper: search for recent PRs and filter out own repos, excluded repos/orgs.
38
31
  * Returns parsed search results that pass all filters.