@oss-autopilot/core 1.11.0 → 1.12.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.
@@ -10,6 +10,7 @@ import { getStateManager, PRMonitor, IssueConversationMonitor, requireGitHubToke
10
10
  import { errorMessage, isRateLimitOrAuthError } from '../core/errors.js';
11
11
  import { warn } from '../core/logger.js';
12
12
  import { emptyPRCountsResult } from '../core/github-stats.js';
13
+ import { createAutopilotScout } from './scout-bridge.js';
13
14
  import { updateMonthlyAnalytics } from './dashboard-data.js';
14
15
  import { deduplicateDigest, compactActionableIssues, compactRepoGroups, } from '../formatters/json.js';
15
16
  const MODULE = 'daily';
@@ -444,6 +445,22 @@ async function executeDailyCheckInternal(token) {
444
445
  catch (error) {
445
446
  warn(MODULE, `Failed to persist monthly analytics: ${errorMessage(error)}`);
446
447
  }
448
+ // Phase 3.5: Feed merged/closed PRs to oss-scout for cross-tool state sync.
449
+ if (recentlyMergedPRs.length > 0 || recentlyClosedPRs.length > 0) {
450
+ try {
451
+ const scout = await createAutopilotScout();
452
+ for (const pr of recentlyMergedPRs) {
453
+ scout.recordMergedPR({ url: pr.url, title: pr.title, mergedAt: pr.mergedAt, repo: pr.repo });
454
+ }
455
+ for (const pr of recentlyClosedPRs) {
456
+ scout.recordClosedPR({ url: pr.url, title: pr.title, closedAt: pr.closedAt, repo: pr.repo });
457
+ }
458
+ await scout.checkpoint();
459
+ }
460
+ catch (error) {
461
+ warn(MODULE, `Failed to sync PR data to oss-scout: ${errorMessage(error)}`);
462
+ }
463
+ }
447
464
  // Capture lastDigestAt BEFORE Phase 4 overwrites it with the current run's timestamp.
448
465
  // Used by collectActionableIssues to determine which PRs are "new" (created since last digest).
449
466
  const previousLastDigestAt = getStateManager().getState().lastDigestAt;
@@ -25,6 +25,8 @@ export { runStatus } from './status.js';
25
25
  export { runSearch } from './search.js';
26
26
  /** Vet a single GitHub issue for claimability (open, unassigned, no linked PRs, repo health). */
27
27
  export { runVet } from './vet.js';
28
+ /** Re-vet all available issues in a curated issue list for freshness. */
29
+ export { runVetList } from './vet-list.js';
28
30
  /** Add a PR to tracking state. */
29
31
  export { runTrack } from './track.js';
30
32
  /** Remove a PR from tracking state. */
@@ -71,7 +73,7 @@ export { runDetectFormatters } from './detect-formatters.js';
71
73
  export { runLocalRepos } from './local-repos.js';
72
74
  export type { ErrorCode } from '../formatters/json.js';
73
75
  export type { DailyOutput, SearchOutput, StartupOutput, StatusOutput, TrackOutput } from '../formatters/json.js';
74
- export type { VetOutput, CommentsOutput, PostOutput, ClaimOutput } from '../formatters/json.js';
76
+ export type { VetOutput, CommentsOutput, PostOutput, ClaimOutput, VetListOutput, VetListItemStatus, } from '../formatters/json.js';
75
77
  export type { ConfigOutput, DetectFormattersOutput, ParseIssueListOutput, ParsedIssueItem, CheckIntegrationOutput, LocalReposOutput, } from '../formatters/json.js';
76
78
  export type { ReadOutput } from './read.js';
77
79
  export type { ShelveOutput, UnshelveOutput } from './shelve.js';
@@ -26,6 +26,8 @@ export { runStatus } from './status.js';
26
26
  export { runSearch } from './search.js';
27
27
  /** Vet a single GitHub issue for claimability (open, unassigned, no linked PRs, repo health). */
28
28
  export { runVet } from './vet.js';
29
+ /** Re-vet all available issues in a curated issue list for freshness. */
30
+ export { runVetList } from './vet-list.js';
29
31
  // ── PR Management ───────────────────────────────────────────────────────────
30
32
  /** Add a PR to tracking state. */
31
33
  export { runTrack } from './track.js';
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Bridge between oss-autopilot's AgentState and oss-scout's OssScout API.
3
+ * Maps state fields and creates scout instances for search/vet commands.
4
+ */
5
+ import { type OssScout, type ScoutState } from '@oss-scout/core';
6
+ /**
7
+ * Build a ScoutState from the current AgentState.
8
+ * Maps oss-autopilot's config and state fields to oss-scout's state format.
9
+ */
10
+ export declare function buildScoutState(): ScoutState;
11
+ /**
12
+ * Create an OssScout instance backed by the current AgentState.
13
+ * Uses 'provided' persistence so scout reads from oss-autopilot's state.
14
+ */
15
+ export declare function createAutopilotScout(): Promise<OssScout>;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Bridge between oss-autopilot's AgentState and oss-scout's OssScout API.
3
+ * Maps state fields and creates scout instances for search/vet commands.
4
+ */
5
+ import { createScout } from '@oss-scout/core';
6
+ import { getStateManager, requireGitHubToken } from '../core/index.js';
7
+ /**
8
+ * Build a ScoutState from the current AgentState.
9
+ * Maps oss-autopilot's config and state fields to oss-scout's state format.
10
+ */
11
+ export function buildScoutState() {
12
+ const state = getStateManager().getState();
13
+ const { config } = state;
14
+ return {
15
+ version: 1,
16
+ preferences: {
17
+ githubUsername: config.githubUsername,
18
+ languages: config.languages,
19
+ labels: config.labels,
20
+ scope: config.scope,
21
+ excludeRepos: config.excludeRepos,
22
+ excludeOrgs: config.excludeOrgs ?? [],
23
+ aiPolicyBlocklist: config.aiPolicyBlocklist,
24
+ projectCategories: config.projectCategories ?? [],
25
+ minStars: config.minStars,
26
+ maxIssueAgeDays: config.maxIssueAgeDays,
27
+ includeDocIssues: config.includeDocIssues,
28
+ minRepoScoreThreshold: config.minRepoScoreThreshold,
29
+ interPhaseDelayMs: 30000,
30
+ broadPhaseDelayMs: 90000,
31
+ skipBroadWhenSufficientResults: 15,
32
+ persistence: config.persistence,
33
+ },
34
+ repoScores: state.repoScores,
35
+ starredRepos: config.starredRepos,
36
+ starredReposLastFetched: config.starredReposLastFetched,
37
+ mergedPRs: (state.mergedPRs ?? []).map((pr) => ({
38
+ url: pr.url,
39
+ title: pr.title,
40
+ mergedAt: pr.mergedAt,
41
+ })),
42
+ closedPRs: (state.closedPRs ?? []).map((pr) => ({
43
+ url: pr.url,
44
+ title: pr.title,
45
+ closedAt: pr.closedAt,
46
+ })),
47
+ savedResults: [],
48
+ skippedIssues: [],
49
+ lastRunAt: state.lastRunAt,
50
+ };
51
+ }
52
+ /**
53
+ * Create an OssScout instance backed by the current AgentState.
54
+ * Uses 'provided' persistence so scout reads from oss-autopilot's state.
55
+ */
56
+ export async function createAutopilotScout() {
57
+ const token = requireGitHubToken();
58
+ return createScout({
59
+ githubToken: token,
60
+ persistence: 'provided',
61
+ initialState: buildScoutState(),
62
+ });
63
+ }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Search command
3
- * Searches for new issues to work on
3
+ * Searches for new issues to work on via @oss-scout/core
4
4
  */
5
5
  import { type SearchOutput } from '../formatters/json.js';
6
6
  export { type SearchOutput } from '../formatters/json.js';
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * Search command
3
- * Searches for new issues to work on
3
+ * Searches for new issues to work on via @oss-scout/core
4
4
  */
5
- import { IssueDiscovery, requireGitHubToken, getStateManager, DEFAULT_CONFIG } from '../core/index.js';
5
+ import { createAutopilotScout } from './scout-bridge.js';
6
+ import { getStateManager } from '../core/index.js';
6
7
  /**
7
8
  * Search GitHub for contributable issues using multi-phase discovery.
8
9
  *
@@ -22,15 +23,11 @@ import { IssueDiscovery, requireGitHubToken, getStateManager, DEFAULT_CONFIG } f
22
23
  * ```
23
24
  */
24
25
  export async function runSearch(options) {
25
- const token = requireGitHubToken();
26
- const discovery = new IssueDiscovery(token);
27
- const candidates = await discovery.searchIssues({ maxResults: options.maxResults });
26
+ const scout = await createAutopilotScout();
27
+ const result = await scout.search({ maxResults: options.maxResults });
28
28
  const stateManager = getStateManager();
29
- const { config } = stateManager.getState();
30
- const excludedRepos = config.excludeRepos || [];
31
- const aiPolicyBlocklist = config.aiPolicyBlocklist ?? DEFAULT_CONFIG.aiPolicyBlocklist ?? [];
32
29
  const searchOutput = {
33
- candidates: candidates.map((c) => {
30
+ candidates: result.candidates.map((c) => {
34
31
  const repoScoreRecord = stateManager.getRepoScore(c.issue.repo);
35
32
  return {
36
33
  issue: {
@@ -57,11 +54,11 @@ export async function runSearch(options) {
57
54
  : undefined,
58
55
  };
59
56
  }),
60
- excludedRepos,
61
- aiPolicyBlocklist,
57
+ excludedRepos: result.excludedRepos,
58
+ aiPolicyBlocklist: result.aiPolicyBlocklist,
62
59
  };
63
- if (discovery.rateLimitWarning) {
64
- searchOutput.rateLimitWarning = discovery.rateLimitWarning;
60
+ if (result.rateLimitWarning) {
61
+ searchOutput.rateLimitWarning = result.rateLimitWarning;
65
62
  }
66
63
  return searchOutput;
67
64
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Vet-list command (#764)
3
- * Re-vets all available issues in a curated issue list file.
3
+ * Re-vets all available issues in a curated issue list file via @oss-scout/core.
4
4
  */
5
5
  import { type VetListOutput, type VetOutput, type VetListItemStatus } from '../formatters/json.js';
6
6
  interface VetListOptions {
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Vet-list command (#764)
3
- * Re-vets all available issues in a curated issue list file.
3
+ * Re-vets all available issues in a curated issue list file via @oss-scout/core.
4
4
  */
5
5
  import * as fs from 'fs';
6
- import { IssueDiscovery, requireGitHubToken } from '../core/index.js';
6
+ import { createAutopilotScout } from './scout-bridge.js';
7
7
  import { runParseList, pruneIssueList } from './parse-list.js';
8
8
  import { detectIssueList } from './startup.js';
9
9
  /**
@@ -34,7 +34,6 @@ export function classifyListStatus(vetResult) {
34
34
  * @returns Consolidated vetting results with list status for each issue
35
35
  */
36
36
  export async function runVetList(options = {}) {
37
- const token = requireGitHubToken();
38
37
  const concurrency = options.concurrency ?? 5;
39
38
  // 1. Find and parse the issue list
40
39
  let issueListPath = options.issueListPath;
@@ -53,7 +52,7 @@ export async function runVetList(options = {}) {
53
52
  };
54
53
  }
55
54
  // 2. Vet each available issue in parallel with concurrency limit
56
- const discovery = new IssueDiscovery(token);
55
+ const scout = await createAutopilotScout();
57
56
  const results = [];
58
57
  // Simple concurrency limiter
59
58
  const items = parsed.available;
@@ -62,7 +61,7 @@ export async function runVetList(options = {}) {
62
61
  while (index < items.length) {
63
62
  const item = items[index++];
64
63
  try {
65
- const candidate = await discovery.vetIssue(item.url);
64
+ const candidate = await scout.vetIssue(item.url);
66
65
  const vetResult = {
67
66
  issue: {
68
67
  repo: candidate.issue.repo,
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Vet command
3
- * Vets a specific issue before working on it
3
+ * Vets a specific issue before working on it via @oss-scout/core
4
4
  */
5
5
  import { type VetOutput } from '../formatters/json.js';
6
6
  export { type VetOutput } from '../formatters/json.js';
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Vet command
3
- * Vets a specific issue before working on it
3
+ * Vets a specific issue before working on it via @oss-scout/core
4
4
  */
5
- import { IssueDiscovery, requireGitHubToken } from '../core/index.js';
5
+ import { createAutopilotScout } from './scout-bridge.js';
6
6
  import { ISSUE_URL_PATTERN, validateGitHubUrl, validateUrl } from './validation.js';
7
7
  /**
8
8
  * Vet a specific GitHub issue for claimability and project health.
@@ -15,9 +15,8 @@ import { ISSUE_URL_PATTERN, validateGitHubUrl, validateUrl } from './validation.
15
15
  export async function runVet(options) {
16
16
  validateUrl(options.issueUrl);
17
17
  validateGitHubUrl(options.issueUrl, ISSUE_URL_PATTERN, 'issue');
18
- const token = requireGitHubToken();
19
- const discovery = new IssueDiscovery(token);
20
- const candidate = await discovery.vetIssue(options.issueUrl);
18
+ const scout = await createAutopilotScout();
19
+ const candidate = await scout.vetIssue(options.issueUrl);
21
20
  return {
22
21
  issue: {
23
22
  repo: candidate.issue.repo,
@@ -5,8 +5,6 @@
5
5
  export { StateManager, getStateManager, getStateManagerAsync, resetStateManager, type Stats } from './state.js';
6
6
  export { GistStateStore } from './gist-state-store.js';
7
7
  export { PRMonitor, type PRCheckFailure, type FetchPRsResult, computeDisplayLabel, classifyCICheck, classifyFailingChecks, } from './pr-monitor.js';
8
- export { IssueDiscovery } from './issue-discovery.js';
9
- export { isDocOnlyIssue, applyPerRepoCap, DOC_ONLY_LABELS } from './issue-filtering.js';
10
8
  export { IssueConversationMonitor } from './issue-conversation.js';
11
9
  export { isBotAuthor, isAcknowledgmentComment } from './comment-utils.js';
12
10
  export { getOctokit, checkRateLimit, type RateLimitInfo } from './github.js';
@@ -5,8 +5,7 @@
5
5
  export { StateManager, getStateManager, getStateManagerAsync, resetStateManager } from './state.js';
6
6
  export { GistStateStore } from './gist-state-store.js';
7
7
  export { PRMonitor, computeDisplayLabel, classifyCICheck, classifyFailingChecks, } from './pr-monitor.js';
8
- export { IssueDiscovery } from './issue-discovery.js';
9
- export { isDocOnlyIssue, applyPerRepoCap, DOC_ONLY_LABELS } from './issue-filtering.js';
8
+ // Search/vetting now delegated to @oss-scout/core via commands/scout-bridge.ts
10
9
  export { IssueConversationMonitor } from './issue-conversation.js';
11
10
  export { isBotAuthor, isAcknowledgmentComment } from './comment-utils.js';
12
11
  export { getOctokit, checkRateLimit } from './github.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oss-autopilot/core",
3
- "version": "1.11.0",
3
+ "version": "1.12.1",
4
4
  "description": "CLI and core library for managing open source contributions",
5
5
  "type": "module",
6
6
  "bin": {
@@ -50,6 +50,7 @@
50
50
  "dependencies": {
51
51
  "@octokit/plugin-throttling": "^11.0.3",
52
52
  "@octokit/rest": "^22.0.1",
53
+ "@oss-scout/core": "^0.4.0",
53
54
  "commander": "^14.0.3",
54
55
  "zod": "^4.3.6"
55
56
  },
@@ -1,19 +0,0 @@
1
- /**
2
- * Category Mapping — static mappings from project categories to GitHub topics and organizations.
3
- *
4
- * Used by issue discovery to prioritize repos matching user's category preferences.
5
- */
6
- import type { ProjectCategory } from './types.js';
7
- /** GitHub topics associated with each project category, used for `topic:` search queries. */
8
- export declare const CATEGORY_TOPICS: Record<ProjectCategory, string[]>;
9
- /** Well-known GitHub organizations associated with each project category. */
10
- export declare const CATEGORY_ORGS: Record<ProjectCategory, string[]>;
11
- /**
12
- * Check if a repo belongs to any of the given categories based on its owner matching a category org.
13
- * Comparison is case-insensitive.
14
- */
15
- export declare function repoBelongsToCategory(repoFullName: string, categories: ProjectCategory[]): boolean;
16
- /**
17
- * Get deduplicated GitHub topics for the given categories, for use in `topic:` search queries.
18
- */
19
- export declare function getTopicsForCategories(categories: ProjectCategory[]): string[];
@@ -1,58 +0,0 @@
1
- /**
2
- * Category Mapping — static mappings from project categories to GitHub topics and organizations.
3
- *
4
- * Used by issue discovery to prioritize repos matching user's category preferences.
5
- */
6
- /** GitHub topics associated with each project category, used for `topic:` search queries. */
7
- export const CATEGORY_TOPICS = {
8
- nonprofit: ['nonprofit', 'social-good', 'humanitarian', 'charity', 'social-impact', 'civic-tech'],
9
- devtools: ['developer-tools', 'devtools', 'cli', 'sdk', 'linter', 'formatter', 'build-tool'],
10
- infrastructure: ['infrastructure', 'cloud', 'kubernetes', 'docker', 'devops', 'monitoring', 'observability'],
11
- 'web-frameworks': ['web-framework', 'frontend', 'backend', 'fullstack', 'nextjs', 'react', 'vue'],
12
- 'data-ml': ['machine-learning', 'data-science', 'deep-learning', 'nlp', 'data-pipeline', 'analytics'],
13
- education: ['education', 'learning', 'tutorial', 'courseware', 'edtech', 'teaching'],
14
- };
15
- /** Well-known GitHub organizations associated with each project category. */
16
- export const CATEGORY_ORGS = {
17
- nonprofit: ['code-for-america', 'opengovfoundation', 'ushahidi', 'hotosm', 'openfn', 'democracyearth'],
18
- devtools: ['eslint', 'prettier', 'vitejs', 'biomejs', 'oxc-project', 'ast-grep', 'turbot'],
19
- infrastructure: ['kubernetes', 'hashicorp', 'grafana', 'prometheus', 'open-telemetry', 'envoyproxy', 'cncf'],
20
- 'web-frameworks': ['vercel', 'remix-run', 'sveltejs', 'nuxt', 'astro', 'redwoodjs', 'blitz-js'],
21
- 'data-ml': ['huggingface', 'mlflow', 'apache', 'dbt-labs', 'dagster-io', 'prefecthq', 'langchain-ai'],
22
- education: ['freeCodeCamp', 'TheOdinProject', 'exercism', 'codecademy', 'oppia', 'Khan'],
23
- };
24
- /**
25
- * Check if a repo belongs to any of the given categories based on its owner matching a category org.
26
- * Comparison is case-insensitive.
27
- */
28
- export function repoBelongsToCategory(repoFullName, categories) {
29
- if (categories.length === 0)
30
- return false;
31
- const owner = repoFullName.split('/')[0]?.toLowerCase();
32
- if (!owner)
33
- return false;
34
- for (const category of categories) {
35
- const orgs = CATEGORY_ORGS[category];
36
- if (!orgs)
37
- continue; // Guard against invalid categories from hand-edited state.json
38
- if (orgs.some((org) => org.toLowerCase() === owner)) {
39
- return true;
40
- }
41
- }
42
- return false;
43
- }
44
- /**
45
- * Get deduplicated GitHub topics for the given categories, for use in `topic:` search queries.
46
- */
47
- export function getTopicsForCategories(categories) {
48
- const topics = new Set();
49
- for (const category of categories) {
50
- const categoryTopics = CATEGORY_TOPICS[category];
51
- if (!categoryTopics)
52
- continue; // Guard against invalid categories from hand-edited state.json
53
- for (const topic of categoryTopics) {
54
- topics.add(topic);
55
- }
56
- }
57
- return [...topics];
58
- }
@@ -1,94 +0,0 @@
1
- /**
2
- * Issue Discovery — orchestrates multi-phase issue search across GitHub.
3
- *
4
- * Delegates filtering, scoring, vetting, and search infrastructure to focused modules (#356, #621):
5
- * - issue-filtering.ts — spam detection, doc-only filtering, per-repo caps
6
- * - issue-scoring.ts — viability scores, repo quality bonuses
7
- * - issue-vetting.ts — vetting orchestration, recommendation + viability scoring
8
- * - issue-eligibility.ts — PR existence, claim detection, requirements analysis
9
- * - repo-health.ts — project health checks, contribution guidelines
10
- * - search-phases.ts — search helpers, caching, batched repo search
11
- */
12
- import { type IssueCandidate } from './types.js';
13
- /**
14
- * Multi-phase issue discovery engine that searches GitHub for contributable issues.
15
- *
16
- * Search phases (in priority order):
17
- * 0. Repos where user has merged PRs (highest merge probability)
18
- * 0.5. Preferred organizations
19
- * 1. Starred repos
20
- * 2. General label-filtered search
21
- * 3. Actively maintained repos
22
- *
23
- * Each candidate is vetted for claimability and scored 0-100 for viability.
24
- */
25
- export declare class IssueDiscovery {
26
- private octokit;
27
- private stateManager;
28
- private githubToken;
29
- private vetter;
30
- /** Set after searchIssues() runs if rate limits affected the search (low pre-flight quota or mid-search rate limit hits). */
31
- rateLimitWarning: string | null;
32
- /** @param githubToken - GitHub personal access token or token from `gh auth token` */
33
- constructor(githubToken: string);
34
- /**
35
- * Fetch the authenticated user's starred repositories from GitHub.
36
- * Updates the state manager with the list and timestamp.
37
- * @returns Array of starred repo names in "owner/repo" format
38
- */
39
- fetchStarredRepos(): Promise<string[]>;
40
- /**
41
- * Get starred repos, fetching from GitHub if cache is stale.
42
- * @returns Array of starred repo names in "owner/repo" format
43
- */
44
- getStarredReposWithRefresh(): Promise<string[]>;
45
- /**
46
- * Search for issues matching our criteria.
47
- * Searches in priority order: merged-PR repos first (no label filter), then preferred
48
- * organizations, then starred repos, then general search, then actively maintained repos.
49
- * Filters out issues from low-scoring and excluded repos.
50
- *
51
- * @param options - Search configuration
52
- * @param options.languages - Programming languages to filter by
53
- * @param options.labels - Issue labels to search for
54
- * @param options.maxResults - Maximum candidates to return (default: 10)
55
- * @returns Scored and sorted issue candidates
56
- * @throws {ValidationError} If no candidates found and no rate limits prevented the search
57
- *
58
- * @example
59
- * ```typescript
60
- * import { IssueDiscovery, requireGitHubToken } from '@oss-autopilot/core';
61
- *
62
- * const discovery = new IssueDiscovery(requireGitHubToken());
63
- * const candidates = await discovery.searchIssues({ maxResults: 5 });
64
- * for (const c of candidates) {
65
- * console.log(`${c.issue.repo}#${c.issue.number}: ${c.viabilityScore}/100`);
66
- * }
67
- * ```
68
- */
69
- searchIssues(options?: {
70
- languages?: string[];
71
- labels?: string[];
72
- maxResults?: number;
73
- }): Promise<IssueCandidate[]>;
74
- /**
75
- * Vet a specific issue for claimability and project health.
76
- * @param issueUrl - Full GitHub issue URL
77
- * @returns The vetted issue candidate with recommendation and scores
78
- * @throws {ValidationError} If the URL is invalid or the issue cannot be fetched
79
- */
80
- vetIssue(issueUrl: string): Promise<IssueCandidate>;
81
- /**
82
- * Save search results to ~/.oss-autopilot/found-issues.md.
83
- * Results are sorted by viability score (highest first).
84
- * @param candidates - Issue candidates to save
85
- * @returns Absolute path to the written file
86
- */
87
- saveSearchResults(candidates: IssueCandidate[]): string;
88
- /**
89
- * Format issue candidate as a markdown display string.
90
- * @param candidate - The issue candidate to format
91
- * @returns Multi-line markdown string with vetting details
92
- */
93
- formatCandidate(candidate: IssueCandidate): string;
94
- }