@oss-scout/core 0.11.0 → 1.1.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.
- package/dist/cli.bundle.cjs +89 -66
- package/dist/cli.js +302 -436
- package/dist/commands/command-scout.d.ts +21 -0
- package/dist/commands/command-scout.js +21 -0
- package/dist/commands/config.js +10 -128
- package/dist/commands/features.js +15 -28
- package/dist/commands/results.d.ts +13 -2
- package/dist/commands/results.js +29 -2
- package/dist/commands/search.d.ts +4 -0
- package/dist/commands/search.js +65 -70
- package/dist/commands/setup.d.ts +2 -0
- package/dist/commands/setup.js +35 -6
- package/dist/commands/skip.d.ts +4 -0
- package/dist/commands/skip.js +45 -55
- package/dist/commands/sync.d.ts +10 -0
- package/dist/commands/sync.js +10 -0
- package/dist/commands/vet-list.js +3 -19
- package/dist/commands/vet.js +18 -25
- package/dist/commands/with-scout.d.ts +32 -0
- package/dist/commands/with-scout.js +41 -0
- package/dist/core/anti-llm-policy.js +5 -33
- package/dist/core/bootstrap.d.ts +2 -2
- package/dist/core/bootstrap.js +5 -9
- package/dist/core/errors.d.ts +10 -0
- package/dist/core/errors.js +20 -5
- package/dist/core/feature-discovery.d.ts +13 -1
- package/dist/core/feature-discovery.js +104 -81
- package/dist/core/gist-state-store.d.ts +13 -12
- package/dist/core/gist-state-store.js +128 -53
- package/dist/core/http-cache.d.ts +32 -2
- package/dist/core/http-cache.js +74 -19
- package/dist/core/issue-discovery.d.ts +12 -1
- package/dist/core/issue-discovery.js +94 -67
- package/dist/core/issue-eligibility.d.ts +11 -4
- package/dist/core/issue-eligibility.js +124 -69
- package/dist/core/issue-graphql.d.ts +58 -0
- package/dist/core/issue-graphql.js +108 -0
- package/dist/core/issue-vetting.d.ts +115 -9
- package/dist/core/issue-vetting.js +246 -109
- package/dist/core/local-state.d.ts +6 -2
- package/dist/core/local-state.js +23 -5
- package/dist/core/logger.d.ts +12 -4
- package/dist/core/logger.js +33 -7
- package/dist/core/personalization.d.ts +30 -10
- package/dist/core/personalization.js +64 -24
- package/dist/core/preference-fields.d.ts +47 -0
- package/dist/core/preference-fields.js +180 -0
- package/dist/core/probe-repo-file.d.ts +47 -0
- package/dist/core/probe-repo-file.js +57 -0
- package/dist/core/repo-health.js +40 -32
- package/dist/core/roadmap.js +26 -22
- package/dist/core/schemas.d.ts +148 -26
- package/dist/core/schemas.js +83 -17
- package/dist/core/search-budget.d.ts +9 -0
- package/dist/core/search-budget.js +36 -3
- package/dist/core/search-phases.d.ts +4 -21
- package/dist/core/search-phases.js +37 -89
- package/dist/core/types.d.ts +151 -38
- package/dist/core/utils.js +60 -26
- package/dist/formatters/human.d.ts +60 -0
- package/dist/formatters/human.js +199 -0
- package/dist/formatters/markdown.d.ts +10 -0
- package/dist/formatters/markdown.js +31 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.js +8 -0
- package/dist/scout.d.ts +75 -12
- package/dist/scout.js +265 -26
- package/package.json +1 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human-readable (non-JSON) output formatters for the oss-scout CLI.
|
|
3
|
+
*
|
|
4
|
+
* Each renderer is a pure function that returns the exact multi-line string the
|
|
5
|
+
* CLI used to emit via a sequence of `console.log` calls. The caller does a
|
|
6
|
+
* single `console.log(renderX(...))`, which appends the one trailing newline
|
|
7
|
+
* that the final `console.log` in the old inline block produced.
|
|
8
|
+
*
|
|
9
|
+
* To stay byte-identical: every old `console.log(line)` becomes one entry in a
|
|
10
|
+
* lines array, a bare `console.log()` (blank line) becomes an empty entry, and
|
|
11
|
+
* the array is joined with "\n". The caller's own `console.log` supplies the
|
|
12
|
+
* last newline. STDERR output (the search rate-limit warning) is deliberately
|
|
13
|
+
* NOT folded in here — it stays a `console.error` in the caller.
|
|
14
|
+
*/
|
|
15
|
+
import type { SearchOutput } from "../commands/search.js";
|
|
16
|
+
import type { FeaturesOutput } from "../commands/features.js";
|
|
17
|
+
import type { SavedCandidate } from "../core/schemas.js";
|
|
18
|
+
import type { VetListResult } from "../core/types.js";
|
|
19
|
+
import type { VetOutput } from "../commands/vet.js";
|
|
20
|
+
/** Emoji for a vetting recommendation, shared by the search and vet renderers. */
|
|
21
|
+
export declare function recommendationIcon(recommendation: "approve" | "skip" | "needs_review"): string;
|
|
22
|
+
/**
|
|
23
|
+
* Render the human-readable `search` output: the "Found N issue candidates"
|
|
24
|
+
* block with per-candidate icon, personalization and stalled tags, and the
|
|
25
|
+
* optional repoScore line. The trailing rate-limit warning is NOT included
|
|
26
|
+
* here; it goes to stderr in the caller.
|
|
27
|
+
*/
|
|
28
|
+
export declare function renderSearch(results: SearchOutput): string;
|
|
29
|
+
/**
|
|
30
|
+
* Render the human-readable `features` output: the optional message, the
|
|
31
|
+
* "Feature opportunities" header, the anchor repos line, and the Quick wins /
|
|
32
|
+
* Bigger bets sections. Returns "" when there is nothing to print beyond an
|
|
33
|
+
* absent message (caller guards against logging a blank line).
|
|
34
|
+
*/
|
|
35
|
+
export declare function renderFeatures(result: FeaturesOutput, options: {
|
|
36
|
+
broad?: boolean;
|
|
37
|
+
}): string;
|
|
38
|
+
/** The empty-state message printed by `results` when nothing is saved. */
|
|
39
|
+
export declare const RESULTS_EMPTY_MESSAGE = "\nNo saved results. Run `oss-scout search` to find issues.\n";
|
|
40
|
+
/**
|
|
41
|
+
* Render the human-readable `results` table: the "Saved results" header and a
|
|
42
|
+
* Score / Repo / Issue / Recommendation / Title row per saved candidate.
|
|
43
|
+
* Callers handle the empty state (RESULTS_EMPTY_MESSAGE) separately.
|
|
44
|
+
*/
|
|
45
|
+
export declare function renderResults(results: SavedCandidate[]): string;
|
|
46
|
+
/** The empty-state message printed by `vet-list` when there is nothing to vet. */
|
|
47
|
+
export declare const VET_LIST_EMPTY_MESSAGE = "\nNo saved results to vet. Run `oss-scout search` first.\n";
|
|
48
|
+
/**
|
|
49
|
+
* Render the human-readable `vet-list` output: the "Vet-list results (N)"
|
|
50
|
+
* block with a per-row status icon, the "Changes since last check"
|
|
51
|
+
* transitions block, the summary line, and the optional pruned-count line.
|
|
52
|
+
* Callers handle the empty state (VET_LIST_EMPTY_MESSAGE) separately.
|
|
53
|
+
*/
|
|
54
|
+
export declare function renderVetList(result: VetListResult): string;
|
|
55
|
+
/**
|
|
56
|
+
* Render the human-readable single-issue `vet` output: the recommendation
|
|
57
|
+
* header, the reasons to approve / skip, and the project-health block. The
|
|
58
|
+
* checkFailed branch (#158) is preserved exactly.
|
|
59
|
+
*/
|
|
60
|
+
export declare function renderVet(result: VetOutput): string;
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human-readable (non-JSON) output formatters for the oss-scout CLI.
|
|
3
|
+
*
|
|
4
|
+
* Each renderer is a pure function that returns the exact multi-line string the
|
|
5
|
+
* CLI used to emit via a sequence of `console.log` calls. The caller does a
|
|
6
|
+
* single `console.log(renderX(...))`, which appends the one trailing newline
|
|
7
|
+
* that the final `console.log` in the old inline block produced.
|
|
8
|
+
*
|
|
9
|
+
* To stay byte-identical: every old `console.log(line)` becomes one entry in a
|
|
10
|
+
* lines array, a bare `console.log()` (blank line) becomes an empty entry, and
|
|
11
|
+
* the array is joined with "\n". The caller's own `console.log` supplies the
|
|
12
|
+
* last newline. STDERR output (the search rate-limit warning) is deliberately
|
|
13
|
+
* NOT folded in here — it stays a `console.error` in the caller.
|
|
14
|
+
*/
|
|
15
|
+
/** Emoji for a vetting recommendation, shared by the search and vet renderers. */
|
|
16
|
+
export function recommendationIcon(recommendation) {
|
|
17
|
+
if (recommendation === "approve")
|
|
18
|
+
return "✅";
|
|
19
|
+
if (recommendation === "skip")
|
|
20
|
+
return "❌";
|
|
21
|
+
return "⚠️";
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Render the human-readable `search` output: the "Found N issue candidates"
|
|
25
|
+
* block with per-candidate icon, personalization and stalled tags, and the
|
|
26
|
+
* optional repoScore line. The trailing rate-limit warning is NOT included
|
|
27
|
+
* here; it goes to stderr in the caller.
|
|
28
|
+
*/
|
|
29
|
+
export function renderSearch(results) {
|
|
30
|
+
const lines = [];
|
|
31
|
+
lines.push(`\nFound ${results.candidates.length} issue candidates:\n`);
|
|
32
|
+
for (const c of results.candidates) {
|
|
33
|
+
const icon = recommendationIcon(c.recommendation);
|
|
34
|
+
const stalledTag = c.linkedPR?.isStalled
|
|
35
|
+
? " (stalled PR, revive opportunity)"
|
|
36
|
+
: "";
|
|
37
|
+
// Personalization tag (#1244). A candidate is either boosted (matched a
|
|
38
|
+
// preference) or a diversity slot (matched none and filled a reserved
|
|
39
|
+
// slot); never both.
|
|
40
|
+
let personalizationTag = "";
|
|
41
|
+
if (c.boostReasons && c.boostReasons.length > 0) {
|
|
42
|
+
// Net score can be negative when avoidRepos applied (#168).
|
|
43
|
+
const verb = (c.boostScore ?? 0) >= 0 ? "boosted" : "deprioritized";
|
|
44
|
+
personalizationTag = ` [${verb}: ${c.boostReasons.join("; ")}]`;
|
|
45
|
+
}
|
|
46
|
+
else if (c.diversitySlot) {
|
|
47
|
+
personalizationTag = " [diversity slot]";
|
|
48
|
+
}
|
|
49
|
+
lines.push(` ${icon} ${c.issue.repo}#${c.issue.number} [${c.viabilityScore}/100]${personalizationTag}${stalledTag}`);
|
|
50
|
+
lines.push(` ${c.issue.title}`);
|
|
51
|
+
lines.push(` ${c.issue.url}`);
|
|
52
|
+
if (c.repoScore) {
|
|
53
|
+
lines.push(` Repo: ${c.repoScore.score}/10, ${c.repoScore.mergedPRCount} merged PRs`);
|
|
54
|
+
}
|
|
55
|
+
lines.push("");
|
|
56
|
+
}
|
|
57
|
+
return lines.join("\n");
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Render the human-readable `features` output: the optional message, the
|
|
61
|
+
* "Feature opportunities" header, the anchor repos line, and the Quick wins /
|
|
62
|
+
* Bigger bets sections. Returns "" when there is nothing to print beyond an
|
|
63
|
+
* absent message (caller guards against logging a blank line).
|
|
64
|
+
*/
|
|
65
|
+
export function renderFeatures(result, options) {
|
|
66
|
+
const lines = [];
|
|
67
|
+
const total = result.quickWins.length + result.biggerBets.length;
|
|
68
|
+
if (result.message) {
|
|
69
|
+
lines.push(`\n${result.message}\n`);
|
|
70
|
+
}
|
|
71
|
+
if (total === 0)
|
|
72
|
+
return lines.join("\n");
|
|
73
|
+
const headerScope = options.broad
|
|
74
|
+
? "across the ecosystem"
|
|
75
|
+
: "in your anchor repos";
|
|
76
|
+
lines.push(`\n🎯 Feature opportunities ${headerScope} (${result.quickWins.length} quick wins + ${result.biggerBets.length} bigger bets)\n`);
|
|
77
|
+
if (!options.broad) {
|
|
78
|
+
lines.push(`Anchor repos: ${result.anchorRepos.join(", ")}\n`);
|
|
79
|
+
}
|
|
80
|
+
if (result.quickWins.length) {
|
|
81
|
+
lines.push("── Quick wins ─────────────────────────────────────────");
|
|
82
|
+
for (const c of result.quickWins) {
|
|
83
|
+
const stalledTag = c.linkedPR?.isStalled
|
|
84
|
+
? " (stalled PR, revive opportunity)"
|
|
85
|
+
: "";
|
|
86
|
+
lines.push(` ${c.issue.repo}#${c.issue.number} [${c.viabilityScore}/100] ${c.issue.title}${stalledTag}`);
|
|
87
|
+
lines.push(` ${c.issue.url}`);
|
|
88
|
+
}
|
|
89
|
+
lines.push("");
|
|
90
|
+
}
|
|
91
|
+
if (result.biggerBets.length) {
|
|
92
|
+
lines.push("── Bigger bets ────────────────────────────────────────");
|
|
93
|
+
for (const c of result.biggerBets) {
|
|
94
|
+
const stalledTag = c.linkedPR?.isStalled
|
|
95
|
+
? " (stalled PR, revive opportunity)"
|
|
96
|
+
: "";
|
|
97
|
+
lines.push(` ${c.issue.repo}#${c.issue.number} [${c.viabilityScore}/100] ${c.issue.title}${stalledTag}`);
|
|
98
|
+
lines.push(` ${c.issue.url}`);
|
|
99
|
+
}
|
|
100
|
+
lines.push("");
|
|
101
|
+
}
|
|
102
|
+
return lines.join("\n");
|
|
103
|
+
}
|
|
104
|
+
/** The empty-state message printed by `results` when nothing is saved. */
|
|
105
|
+
export const RESULTS_EMPTY_MESSAGE = "\nNo saved results. Run `oss-scout search` to find issues.\n";
|
|
106
|
+
/**
|
|
107
|
+
* Render the human-readable `results` table: the "Saved results" header and a
|
|
108
|
+
* Score / Repo / Issue / Recommendation / Title row per saved candidate.
|
|
109
|
+
* Callers handle the empty state (RESULTS_EMPTY_MESSAGE) separately.
|
|
110
|
+
*/
|
|
111
|
+
export function renderResults(results) {
|
|
112
|
+
const lines = [];
|
|
113
|
+
lines.push(`\nSaved results (${results.length}):\n`);
|
|
114
|
+
lines.push(" Score Repo Issue Recommendation Title");
|
|
115
|
+
lines.push(" ───── ──────────────────────────────── ────── ────────────── ─────");
|
|
116
|
+
for (const r of results) {
|
|
117
|
+
const score = String(r.viabilityScore).padStart(3);
|
|
118
|
+
const repo = r.repo.padEnd(32).slice(0, 32);
|
|
119
|
+
const issue = `#${r.number}`.padEnd(6);
|
|
120
|
+
const rec = r.recommendation.padEnd(14);
|
|
121
|
+
const title = r.title.length > 50 ? r.title.slice(0, 47) + "..." : r.title;
|
|
122
|
+
lines.push(` ${score} ${repo} ${issue} ${rec} ${title}`);
|
|
123
|
+
}
|
|
124
|
+
lines.push("");
|
|
125
|
+
return lines.join("\n");
|
|
126
|
+
}
|
|
127
|
+
/** The empty-state message printed by `vet-list` when there is nothing to vet. */
|
|
128
|
+
export const VET_LIST_EMPTY_MESSAGE = "\nNo saved results to vet. Run `oss-scout search` first.\n";
|
|
129
|
+
/** Icon for a vet-list entry's availability status. */
|
|
130
|
+
function vetListStatusIcon(status) {
|
|
131
|
+
return status === "still_available"
|
|
132
|
+
? "✅"
|
|
133
|
+
: status === "claimed"
|
|
134
|
+
? "🔒"
|
|
135
|
+
: status === "has_pr"
|
|
136
|
+
? "🔀"
|
|
137
|
+
: status === "closed"
|
|
138
|
+
? "🚫"
|
|
139
|
+
: "❌";
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Render the human-readable `vet-list` output: the "Vet-list results (N)"
|
|
143
|
+
* block with a per-row status icon, the "Changes since last check"
|
|
144
|
+
* transitions block, the summary line, and the optional pruned-count line.
|
|
145
|
+
* Callers handle the empty state (VET_LIST_EMPTY_MESSAGE) separately.
|
|
146
|
+
*/
|
|
147
|
+
export function renderVetList(result) {
|
|
148
|
+
const lines = [];
|
|
149
|
+
lines.push(`\nVet-list results (${result.summary.total}):\n`);
|
|
150
|
+
for (const r of result.results) {
|
|
151
|
+
const icon = vetListStatusIcon(r.status);
|
|
152
|
+
const score = r.ok ? ` [${r.viabilityScore}/100]` : "";
|
|
153
|
+
lines.push(` ${icon} ${r.repo}#${r.number} — ${r.status}${score}`);
|
|
154
|
+
lines.push(` ${r.title}`);
|
|
155
|
+
}
|
|
156
|
+
if (result.transitions.length > 0) {
|
|
157
|
+
lines.push(`\n🔔 Changes since last check (${result.transitions.length}):`);
|
|
158
|
+
for (const t of result.transitions) {
|
|
159
|
+
lines.push(` ${t.repo}#${t.number}: ${t.from} → ${t.to}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
lines.push(`\nSummary: ${result.summary.stillAvailable} available, ${result.summary.claimed} claimed, ${result.summary.hasPR} has PR, ${result.summary.closed} closed, ${result.summary.errors} errors`);
|
|
163
|
+
if (result.prunedCount != null) {
|
|
164
|
+
lines.push(`Pruned ${result.prunedCount} unavailable issues from saved results.`);
|
|
165
|
+
}
|
|
166
|
+
lines.push("");
|
|
167
|
+
return lines.join("\n");
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Render the human-readable single-issue `vet` output: the recommendation
|
|
171
|
+
* header, the reasons to approve / skip, and the project-health block. The
|
|
172
|
+
* checkFailed branch (#158) is preserved exactly.
|
|
173
|
+
*/
|
|
174
|
+
export function renderVet(result) {
|
|
175
|
+
const lines = [];
|
|
176
|
+
const icon = recommendationIcon(result.recommendation);
|
|
177
|
+
lines.push(`\n${icon} ${result.issue.repo}#${result.issue.number}: ${result.recommendation.toUpperCase()}`);
|
|
178
|
+
lines.push(` ${result.issue.title}`);
|
|
179
|
+
lines.push(` ${result.issue.url}\n`);
|
|
180
|
+
if (result.reasonsToApprove.length > 0) {
|
|
181
|
+
lines.push("Reasons to approve:");
|
|
182
|
+
for (const r of result.reasonsToApprove)
|
|
183
|
+
lines.push(` + ${r}`);
|
|
184
|
+
}
|
|
185
|
+
if (result.reasonsToSkip.length > 0) {
|
|
186
|
+
lines.push("Reasons to skip:");
|
|
187
|
+
for (const r of result.reasonsToSkip)
|
|
188
|
+
lines.push(` - ${r}`);
|
|
189
|
+
}
|
|
190
|
+
if (result.projectHealth.checkFailed) {
|
|
191
|
+
lines.push(`\nProject health: unknown (check failed: ${result.projectHealth.failureReason})`);
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
lines.push(`\nProject health: ${result.projectHealth.isActive ? "Active" : "Inactive"}`);
|
|
195
|
+
lines.push(` Last commit: ${result.projectHealth.daysSinceLastCommit} days ago`);
|
|
196
|
+
lines.push(` CI status: ${result.projectHealth.ciStatus}`);
|
|
197
|
+
}
|
|
198
|
+
return lines.join("\n");
|
|
199
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown output formatter (#170) — renders saved results as a table for
|
|
3
|
+
* digests, notes export, and scheduled GitHub-issue summaries.
|
|
4
|
+
*/
|
|
5
|
+
import type { SavedCandidate } from "../core/schemas.js";
|
|
6
|
+
/**
|
|
7
|
+
* Render saved results as a GitHub-flavored markdown table, sorted by
|
|
8
|
+
* viability score descending. Returns a friendly message when empty.
|
|
9
|
+
*/
|
|
10
|
+
export declare function formatResultsMarkdown(results: SavedCandidate[]): string;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown output formatter (#170) — renders saved results as a table for
|
|
3
|
+
* digests, notes export, and scheduled GitHub-issue summaries.
|
|
4
|
+
*/
|
|
5
|
+
/** Escape pipe and newline so a title can't break the markdown table. */
|
|
6
|
+
function cell(value) {
|
|
7
|
+
return value.replace(/\r?\n/g, " ").replace(/\|/g, "\\|").trim();
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Render saved results as a GitHub-flavored markdown table, sorted by
|
|
11
|
+
* viability score descending. Returns a friendly message when empty.
|
|
12
|
+
*/
|
|
13
|
+
export function formatResultsMarkdown(results) {
|
|
14
|
+
if (results.length === 0) {
|
|
15
|
+
return "_No saved results._";
|
|
16
|
+
}
|
|
17
|
+
const sorted = [...results].sort((a, b) => b.viabilityScore - a.viabilityScore);
|
|
18
|
+
const header = "| Score | Repo | Issue | Recommendation | Title |";
|
|
19
|
+
const divider = "| ----- | ---- | ----- | -------------- | ----- |";
|
|
20
|
+
const rows = sorted.map((r) => {
|
|
21
|
+
const issueLink = `[#${r.number}](${r.issueUrl})`;
|
|
22
|
+
return `| ${r.viabilityScore} | ${cell(r.repo)} | ${issueLink} | ${cell(r.recommendation)} | ${cell(r.title)} |`;
|
|
23
|
+
});
|
|
24
|
+
return [
|
|
25
|
+
`## oss-scout results (${results.length})`,
|
|
26
|
+
"",
|
|
27
|
+
header,
|
|
28
|
+
divider,
|
|
29
|
+
...rows,
|
|
30
|
+
].join("\n");
|
|
31
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -15,13 +15,17 @@
|
|
|
15
15
|
* @packageDocumentation
|
|
16
16
|
*/
|
|
17
17
|
export { createScout, OssScout } from "./scout.js";
|
|
18
|
-
export type { ScoutConfig, SearchOptions, SearchResult, IssueCandidate, MergedPRRecord, ClosedPRRecord, OpenPRRecord, RepoScoreUpdate, ProjectHealth, SearchPriority, CheckResult, AntiLLMPolicyResult, AntiLLMPolicySourceFile, VetListOptions, VetListResult, VetListEntry, VetListSummary, } from "./core/types.js";
|
|
18
|
+
export type { ScoutConfig, SearchOptions, SearchResult, IssueCandidate, MergedPRRecord, ClosedPRRecord, OpenPRRecord, RepoScoreUpdate, ProjectHealth, ProjectHealthData, ProjectHealthFailure, SearchPriority, CheckResult, AntiLLMPolicyResult, AntiLLMPolicySourceFile, VetListOptions, VetListResult, VetListEntry, VetListEntryBase, VetListSummary, SyncResult, } from "./core/types.js";
|
|
19
19
|
export type { ScoutState, ScoutPreferences, RepoScore, RepoSignals, IssueVettingResult, LinkedPR, ContributionGuidelines, TrackedIssue, IssueScope, ProjectCategory, StoredMergedPR, StoredClosedPR, StoredOpenPR, SearchStrategy, SkippedIssue, Horizon, } from "./core/schemas.js";
|
|
20
20
|
export { ScoutStateSchema, ScoutPreferencesSchema, RepoScoreSchema, IssueScopeSchema, ProjectCategorySchema, SearchStrategySchema, SkippedIssueSchema, HorizonSchema, } from "./core/schemas.js";
|
|
21
|
+
export { applyPreferenceField, FIELD_CONFIGS, PREFERENCE_KEYS, SORTED_PREFERENCE_KEYS, assertFieldConfigsCover, updateArray, type FieldConfig, } from "./core/preference-fields.js";
|
|
21
22
|
export { requireGitHubToken, getGitHubToken } from "./core/utils.js";
|
|
22
23
|
export { IssueDiscovery } from "./core/issue-discovery.js";
|
|
23
|
-
export { IssueVetter, type ScoutStateReader, type FeatureSignals, } from "./core/issue-vetting.js";
|
|
24
|
+
export { IssueVetter, type ScoutStateReader, type ScoutStateWriter, type SLMConfig, type FeatureSignals, } from "./core/issue-vetting.js";
|
|
24
25
|
export { scanForAntiLLMPolicy, ANTI_LLM_KEYWORDS, } from "./core/anti-llm-policy.js";
|
|
26
|
+
export { bootstrapScout, type BootstrapResult } from "./core/bootstrap.js";
|
|
27
|
+
export { setLogLevel, getLogLevel, enableDebug, type LogLevel, } from "./core/logger.js";
|
|
25
28
|
export { discoverFeatures, resolveAnchorRepos, classifyHorizon, splitByHorizon, ANCHOR_THRESHOLD, FEATURE_LABELS, NO_ANCHORS_MESSAGE, NO_RESULTS_MESSAGE, type FeatureCandidate, type FeatureSearchResult, type DiscoverFeaturesOptions, } from "./core/feature-discovery.js";
|
|
26
29
|
export { isLinkedPRStalled, STALLED_PR_THRESHOLD_DAYS, } from "./core/linked-pr.js";
|
|
27
30
|
export { fetchRoadmapIssueRefs, parseRoadmapIssueRefs, } from "./core/roadmap.js";
|
|
31
|
+
export { ISSUE_URL_PATTERN, validateGitHubUrl, validateUrl, } from "./commands/validation.js";
|
package/dist/index.js
CHANGED
|
@@ -18,15 +18,23 @@
|
|
|
18
18
|
export { createScout, OssScout } from "./scout.js";
|
|
19
19
|
// Schemas (for consumers who need runtime validation)
|
|
20
20
|
export { ScoutStateSchema, ScoutPreferencesSchema, RepoScoreSchema, IssueScopeSchema, ProjectCategorySchema, SearchStrategySchema, SkippedIssueSchema, HorizonSchema, } from "./core/schemas.js";
|
|
21
|
+
// Preference-field metadata + parsing (shared by the CLI and the MCP server)
|
|
22
|
+
export { applyPreferenceField, FIELD_CONFIGS, PREFERENCE_KEYS, SORTED_PREFERENCE_KEYS, assertFieldConfigsCover, updateArray, } from "./core/preference-fields.js";
|
|
21
23
|
// Utilities
|
|
22
24
|
export { requireGitHubToken, getGitHubToken } from "./core/utils.js";
|
|
23
25
|
// Internal classes (for advanced use)
|
|
24
26
|
export { IssueDiscovery } from "./core/issue-discovery.js";
|
|
25
27
|
export { IssueVetter, } from "./core/issue-vetting.js";
|
|
26
28
|
export { scanForAntiLLMPolicy, ANTI_LLM_KEYWORDS, } from "./core/anti-llm-policy.js";
|
|
29
|
+
// Bootstrap (seed state from GitHub) — usable by library/MCP hosts (#156)
|
|
30
|
+
export { bootstrapScout } from "./core/bootstrap.js";
|
|
31
|
+
// Log-level control for library hosts (#156)
|
|
32
|
+
export { setLogLevel, getLogLevel, enableDebug, } from "./core/logger.js";
|
|
27
33
|
// Feature discovery API
|
|
28
34
|
export { discoverFeatures, resolveAnchorRepos, classifyHorizon, splitByHorizon, ANCHOR_THRESHOLD, FEATURE_LABELS, NO_ANCHORS_MESSAGE, NO_RESULTS_MESSAGE, } from "./core/feature-discovery.js";
|
|
29
35
|
// Linked-PR helpers (#97)
|
|
30
36
|
export { isLinkedPRStalled, STALLED_PR_THRESHOLD_DAYS, } from "./core/linked-pr.js";
|
|
31
37
|
// Roadmap scraping (#95)
|
|
32
38
|
export { fetchRoadmapIssueRefs, parseRoadmapIssueRefs, } from "./core/roadmap.js";
|
|
39
|
+
// Issue-URL validation (shared by the CLI and the MCP server)
|
|
40
|
+
export { ISSUE_URL_PATTERN, validateGitHubUrl, validateUrl, } from "./commands/validation.js";
|
package/dist/scout.d.ts
CHANGED
|
@@ -4,11 +4,19 @@
|
|
|
4
4
|
* Provides personalized issue discovery, vetting, and scoring.
|
|
5
5
|
* Implements ScoutStateReader to bridge state with the search engine.
|
|
6
6
|
*/
|
|
7
|
-
import type { ScoutStateReader } from "./core/issue-vetting.js";
|
|
7
|
+
import type { ScoutStateReader, ScoutStateWriter, SLMConfig } from "./core/issue-vetting.js";
|
|
8
8
|
import { type FeatureSearchResult } from "./core/feature-discovery.js";
|
|
9
9
|
import type { ScoutState, ScoutPreferences, RepoScore, SavedCandidate, SkippedIssue, Horizon } from "./core/schemas.js";
|
|
10
|
-
import type { ScoutConfig, SearchOptions, SearchResult, IssueCandidate, MergedPRRecord, ClosedPRRecord, OpenPRRecord, RepoScoreUpdate, ProjectCategory, VetListOptions, VetListResult } from "./core/types.js";
|
|
10
|
+
import type { ScoutConfig, SearchOptions, SearchResult, IssueCandidate, MergedPRRecord, ClosedPRRecord, OpenPRRecord, RepoScoreUpdate, ProjectCategory, SyncResult, VetListOptions, VetListResult } from "./core/types.js";
|
|
11
11
|
import { GistStateStore } from "./core/gist-state-store.js";
|
|
12
|
+
import type { GistOctokitLike } from "./core/gist-state-store.js";
|
|
13
|
+
import type { Octokit } from "@octokit/rest";
|
|
14
|
+
/**
|
|
15
|
+
* Wrap a real Octokit instance as GistOctokitLike without unsafe double casts.
|
|
16
|
+
* Exported (not via the package index) so the response-narrowing logic — the
|
|
17
|
+
* "no id" guards and the files/list mapping — is unit-testable (#162).
|
|
18
|
+
*/
|
|
19
|
+
export declare function toGistOctokit(octokit: Octokit): GistOctokitLike;
|
|
12
20
|
/**
|
|
13
21
|
* Create an OssScout instance.
|
|
14
22
|
*
|
|
@@ -37,17 +45,42 @@ export declare function createScout(config: ScoutConfig): Promise<OssScout>;
|
|
|
37
45
|
* Implements ScoutStateReader so the search engine can read state
|
|
38
46
|
* without knowing about the persistence layer.
|
|
39
47
|
*/
|
|
40
|
-
export declare class OssScout implements ScoutStateReader {
|
|
48
|
+
export declare class OssScout implements ScoutStateReader, ScoutStateWriter {
|
|
41
49
|
private githubToken;
|
|
42
50
|
private gistStore;
|
|
43
51
|
private state;
|
|
44
52
|
private dirty;
|
|
45
|
-
|
|
53
|
+
/** When true, checkpoint() also writes ~/.oss-scout/state.json. */
|
|
54
|
+
private persistLocal;
|
|
55
|
+
constructor(githubToken: string, initialState: ScoutState, gistStore?: GistStateStore | null, opts?: {
|
|
56
|
+
persistLocal?: boolean;
|
|
57
|
+
});
|
|
58
|
+
/**
|
|
59
|
+
* Drop stale disk-cache entries. Called at the top of every cache-burning
|
|
60
|
+
* entry point (search, features, vetList); without it ~/.oss-scout/cache
|
|
61
|
+
* grows without bound. evictStale never throws (fs errors degrade to warn).
|
|
62
|
+
*/
|
|
63
|
+
private evictStaleCacheEntries;
|
|
46
64
|
/**
|
|
47
65
|
* Multi-strategy issue search. Returns scored, sorted candidates.
|
|
48
66
|
* Automatically culls expired skip entries and filters skipped issues.
|
|
49
67
|
*/
|
|
50
68
|
search(options?: SearchOptions): Promise<SearchResult>;
|
|
69
|
+
/**
|
|
70
|
+
* Populate the `hasActiveMaintainers` repo-score signal from a freshly
|
|
71
|
+
* computed projectHealth (#167). It was initialized false and never set, so
|
|
72
|
+
* calculateScore's +1 active-maintainers weight was inert; `isActive`
|
|
73
|
+
* (recent commit activity) is a real, already-computed proxy.
|
|
74
|
+
*
|
|
75
|
+
* `isResponsive` and `avgResponseDays` are deliberately NOT set here, and
|
|
76
|
+
* `isResponsive` no longer carries a score weight at all (#167): real
|
|
77
|
+
* responsiveness needs an actual response-time measurement (extra API calls)
|
|
78
|
+
* that is out of scope, and `hasActiveMaintainers` already covers the
|
|
79
|
+
* activity proxy. `hasHostileComments` stays a host-settable capability (it
|
|
80
|
+
* needs comment sentiment, out of scope). A failed health check is skipped so
|
|
81
|
+
* its neutral-default fields don't pollute the score.
|
|
82
|
+
*/
|
|
83
|
+
private updateRepoSignalsFromHealth;
|
|
51
84
|
/**
|
|
52
85
|
* Vet a single issue URL for claimability.
|
|
53
86
|
*/
|
|
@@ -83,15 +116,21 @@ export declare class OssScout implements ScoutStateReader {
|
|
|
83
116
|
getReposWithOpenPRs(): string[];
|
|
84
117
|
getStarredRepos(): string[];
|
|
85
118
|
getProjectCategories(): ProjectCategory[];
|
|
119
|
+
/** Configured GitHub username (used to classify own vs competing PRs, #166). */
|
|
120
|
+
getGitHubUsername(): string;
|
|
86
121
|
getRepoScore(repo: string): number | null;
|
|
87
122
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
123
|
+
* Number of the user's PRs closed without merge in this repo (#125).
|
|
124
|
+
* Prefers the tracked repo score; falls back to counting closedPRs so the
|
|
125
|
+
* scoring penalty works even before a score record exists.
|
|
90
126
|
*/
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
127
|
+
getClosedWithoutMergeCount(repo: string): number;
|
|
128
|
+
/**
|
|
129
|
+
* SLM pre-triage config read from preferences (oss-autopilot#1122). Returns
|
|
130
|
+
* `null` when no `slmTriageModel` is configured — the vetter skips the SLM
|
|
131
|
+
* call entirely (#158).
|
|
132
|
+
*/
|
|
133
|
+
getSLMTriageConfig(): SLMConfig | null;
|
|
95
134
|
/** Get current preferences (read-only). */
|
|
96
135
|
getPreferences(): Readonly<ScoutPreferences>;
|
|
97
136
|
/** Get repo score record for a specific repository. */
|
|
@@ -110,6 +149,17 @@ export declare class OssScout implements ScoutStateReader {
|
|
|
110
149
|
* Open PRs signal active engagement even when nothing is merged yet.
|
|
111
150
|
*/
|
|
112
151
|
recordOpenPR(pr: OpenPRRecord): void;
|
|
152
|
+
/**
|
|
153
|
+
* Reconcile tracked open PRs against their current GitHub state (#164).
|
|
154
|
+
*
|
|
155
|
+
* `state.openPRs` was append-only — nothing transitioned an open PR to
|
|
156
|
+
* merged/closed, so getReposWithOpenPRs() over-reported forever once a PR
|
|
157
|
+
* merged. This checks each open PR, records merges/closures (which updates
|
|
158
|
+
* the repo score), prunes resolved entries, and checkpoints. Cheaper than a
|
|
159
|
+
* full bootstrap, so a host can call it on daily startup. Transient errors
|
|
160
|
+
* leave the entry in place; auth/rate-limit failures propagate.
|
|
161
|
+
*/
|
|
162
|
+
syncOpenPRs(): Promise<SyncResult>;
|
|
113
163
|
/**
|
|
114
164
|
* Update repo score with observed signals.
|
|
115
165
|
*/
|
|
@@ -138,6 +188,12 @@ export declare class OssScout implements ScoutStateReader {
|
|
|
138
188
|
* Clear all saved results.
|
|
139
189
|
*/
|
|
140
190
|
clearResults(): void;
|
|
191
|
+
/**
|
|
192
|
+
* Record deletion tombstones (#117) so a later gist merge does not
|
|
193
|
+
* resurrect these URLs from another machine's copy. A re-add with a newer
|
|
194
|
+
* timestamp overrides the tombstone in mergeStates.
|
|
195
|
+
*/
|
|
196
|
+
private addTombstones;
|
|
141
197
|
/**
|
|
142
198
|
* Skip an issue — excludes it from future searches. Auto-culled after 90 days.
|
|
143
199
|
*/
|
|
@@ -178,9 +234,16 @@ export declare class OssScout implements ScoutStateReader {
|
|
|
178
234
|
getState(): Readonly<ScoutState>;
|
|
179
235
|
private updateRepoScoreFromPRs;
|
|
180
236
|
/**
|
|
181
|
-
* Calculate repo score
|
|
237
|
+
* Calculate repo score from observed data.
|
|
182
238
|
* base 5, +1 per merged PR (max +3), -1 per closed-without-merge (max -3),
|
|
183
|
-
* +1
|
|
239
|
+
* +1 active maintainers, -2 hostile comments, clamped to [1, 10].
|
|
240
|
+
*
|
|
241
|
+
* `isResponsive` is intentionally NOT scored (#167): nothing in oss-scout
|
|
242
|
+
* ever computes it, so awarding +1 for it was dead weight that the
|
|
243
|
+
* now-computed `hasActiveMaintainers` signal already covers as the activity
|
|
244
|
+
* proxy. The field is retained on RepoSignals for backward compatibility but
|
|
245
|
+
* no longer affects the score. With the current signals the reachable range
|
|
246
|
+
* is [1, 9]; the upper clamp stays as defensive hygiene.
|
|
184
247
|
*/
|
|
185
248
|
private calculateScore;
|
|
186
249
|
}
|