@oss-autopilot/core 3.0.1 → 3.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.
@@ -2,7 +2,18 @@
2
2
  * Bridge between oss-autopilot's AgentState and oss-scout's OssScout API.
3
3
  * Maps state fields and creates scout instances for search/vet commands.
4
4
  */
5
- import { type OssScout, type ScoutState } from '@oss-scout/core';
5
+ import { type LinkedPR as ScoutLinkedPR, type OssScout, type ScoutState } from '@oss-scout/core';
6
+ import type { LinkedPR } from '../core/linked-pr-classification.js';
7
+ /**
8
+ * Convert scout 0.6.0's `LinkedPR` (separate `state` + `merged`) into the
9
+ * shape `classifyLinkedPR` expects (`state` already folded with `merged`).
10
+ *
11
+ * Scout exposes the raw GitHub fields verbatim, but the classifier was
12
+ * written before scout surfaced this data and uses a tri-state
13
+ * `'open' | 'closed' | 'merged'` enum. Folding `merged` into the state
14
+ * preserves the function's existing contract + tests.
15
+ */
16
+ export declare function adaptScoutLinkedPR(scoutLinkedPR: ScoutLinkedPR | null | undefined): LinkedPR | null;
6
17
  /**
7
18
  * Build a ScoutState from the current AgentState.
8
19
  * Maps oss-autopilot's config and state fields to oss-scout's state format.
@@ -5,6 +5,23 @@
5
5
  import { createScout } from '@oss-scout/core';
6
6
  import { getStateManager, requireGitHubToken } from '../core/index.js';
7
7
  import { loadSkippedIssues } from './skip-file-parser.js';
8
+ /**
9
+ * Convert scout 0.6.0's `LinkedPR` (separate `state` + `merged`) into the
10
+ * shape `classifyLinkedPR` expects (`state` already folded with `merged`).
11
+ *
12
+ * Scout exposes the raw GitHub fields verbatim, but the classifier was
13
+ * written before scout surfaced this data and uses a tri-state
14
+ * `'open' | 'closed' | 'merged'` enum. Folding `merged` into the state
15
+ * preserves the function's existing contract + tests.
16
+ */
17
+ export function adaptScoutLinkedPR(scoutLinkedPR) {
18
+ if (!scoutLinkedPR)
19
+ return null;
20
+ return {
21
+ author: { login: scoutLinkedPR.author },
22
+ state: scoutLinkedPR.merged ? 'merged' : scoutLinkedPR.state,
23
+ };
24
+ }
8
25
  /**
9
26
  * Build a ScoutState from the current AgentState.
10
27
  * Maps oss-autopilot's config and state fields to oss-scout's state format.
@@ -31,6 +48,8 @@ export function buildScoutState() {
31
48
  broadPhaseDelayMs: 90000,
32
49
  skipBroadWhenSufficientResults: 15,
33
50
  persistence: config.persistence,
51
+ slmTriageModel: config.slmTriageModel,
52
+ slmTriageHost: config.slmTriageHost,
34
53
  },
35
54
  repoScores: state.repoScores,
36
55
  starredRepos: config.starredRepos,
@@ -3,11 +3,11 @@
3
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 { createAutopilotScout } from './scout-bridge.js';
6
+ import { adaptScoutLinkedPR, createAutopilotScout } from './scout-bridge.js';
7
7
  import { runParseList, pruneIssueList } from './parse-list.js';
8
8
  import { detectIssueList } from './startup.js';
9
9
  import { computeSuccessGrade, gradeFromCandidate } from '../core/issue-grading.js';
10
- import { getStateManager } from '../core/index.js';
10
+ import { getStateManager, classifyLinkedPR } from '../core/index.js';
11
11
  const UNKNOWN_GRADE = computeSuccessGrade({ avgResponseDays: null, mergeRate: null, daysSinceLastCommit: null });
12
12
  const KNOWN_SKIP_REASONS = new Set([
13
13
  'issue_closed',
@@ -117,6 +117,11 @@ export async function runVetList(options = {}) {
117
117
  projectHealth: candidate.projectHealth,
118
118
  getRepoScore: (repo) => getStateManager().getRepoScore(repo),
119
119
  });
120
+ const userLogin = getStateManager().getState().config.githubUsername;
121
+ const linkedPRClassification = classifyLinkedPR({
122
+ linkedPR: adaptScoutLinkedPR(candidate.vettingResult.linkedPR),
123
+ userLogin,
124
+ });
120
125
  const vetResult = {
121
126
  issue: {
122
127
  repo: candidate.issue.repo,
@@ -130,6 +135,9 @@ export async function runVetList(options = {}) {
130
135
  reasonsToSkip: candidate.reasonsToSkip,
131
136
  projectHealth: candidate.projectHealth,
132
137
  vettingResult: candidate.vettingResult,
138
+ antiLLMPolicy: candidate.antiLLMPolicy,
139
+ linkedPRClassification,
140
+ slmTriage: candidate.slmTriage ?? null,
133
141
  grade,
134
142
  };
135
143
  results.push({
@@ -5,7 +5,8 @@
5
5
  import { createAutopilotScout } from './scout-bridge.js';
6
6
  import { ISSUE_URL_PATTERN, validateGitHubUrl, validateUrl } from './validation.js';
7
7
  import { gradeFromCandidate } from '../core/issue-grading.js';
8
- import { getStateManager } from '../core/index.js';
8
+ import { getStateManager, classifyLinkedPR } from '../core/index.js';
9
+ import { adaptScoutLinkedPR } from './scout-bridge.js';
9
10
  /**
10
11
  * Vet a specific GitHub issue for claimability and project health.
11
12
  *
@@ -24,6 +25,11 @@ export async function runVet(options) {
24
25
  projectHealth: candidate.projectHealth,
25
26
  getRepoScore: (repo) => getStateManager().getRepoScore(repo),
26
27
  });
28
+ const userLogin = getStateManager().getState().config.githubUsername;
29
+ const linkedPRClassification = classifyLinkedPR({
30
+ linkedPR: adaptScoutLinkedPR(candidate.vettingResult.linkedPR),
31
+ userLogin,
32
+ });
27
33
  return {
28
34
  issue: {
29
35
  repo: candidate.issue.repo,
@@ -37,6 +43,9 @@ export async function runVet(options) {
37
43
  reasonsToSkip: candidate.reasonsToSkip,
38
44
  projectHealth: candidate.projectHealth,
39
45
  vettingResult: candidate.vettingResult,
46
+ antiLLMPolicy: candidate.antiLLMPolicy,
47
+ linkedPRClassification,
48
+ slmTriage: candidate.slmTriage ?? null,
40
49
  grade,
41
50
  };
42
51
  }
@@ -241,6 +241,8 @@ export declare const AgentConfigSchema: z.ZodObject<{
241
241
  }>>;
242
242
  diffToolCustomCommand: z.ZodOptional<z.ZodString>;
243
243
  autoFormatBeforePush: z.ZodDefault<z.ZodBoolean>;
244
+ slmTriageModel: z.ZodDefault<z.ZodString>;
245
+ slmTriageHost: z.ZodDefault<z.ZodString>;
244
246
  }, z.core.$strip>;
245
247
  export declare const LocalRepoCacheSchema: z.ZodObject<{
246
248
  repos: z.ZodRecord<z.ZodString, z.ZodObject<{
@@ -399,6 +401,8 @@ export declare const AgentStateSchema: z.ZodObject<{
399
401
  }>>;
400
402
  diffToolCustomCommand: z.ZodOptional<z.ZodString>;
401
403
  autoFormatBeforePush: z.ZodDefault<z.ZodBoolean>;
404
+ slmTriageModel: z.ZodDefault<z.ZodString>;
405
+ slmTriageHost: z.ZodDefault<z.ZodString>;
402
406
  }, z.core.$strip>>;
403
407
  lastRunAt: z.ZodDefault<z.ZodString>;
404
408
  lastDigestAt: z.ZodOptional<z.ZodString>;
@@ -151,6 +151,19 @@ export const AgentConfigSchema = z.object({
151
151
  * the hook does nothing on every push unless the user explicitly enables it.
152
152
  */
153
153
  autoFormatBeforePush: z.boolean().default(false),
154
+ /**
155
+ * Optional Ollama model for SLM pre-triage during issue vetting (#1122).
156
+ * Empty disables the feature. Recommended: `gemma4:e4b` (default for
157
+ * capable hardware), `gemma4:e2b` or `qwen3:1.7b` for low-RAM machines.
158
+ * Threaded through to scout via the bridge in `scout-bridge.ts`.
159
+ */
160
+ slmTriageModel: z.string().default(''),
161
+ /**
162
+ * Optional Ollama HTTP host override. Defaults to `http://127.0.0.1:11434`
163
+ * when empty. Useful when Ollama runs on a different machine on the
164
+ * local network.
165
+ */
166
+ slmTriageHost: z.string().default(''),
154
167
  });
155
168
  // ── 6. Cache schemas ─────────────────────────────────────────────────
156
169
  export const LocalRepoCacheSchema = z.object({
@@ -725,6 +725,34 @@ export interface VetOutput {
725
725
  reasonsToSkip: string[];
726
726
  projectHealth: unknown;
727
727
  vettingResult: unknown;
728
+ /**
729
+ * Result of scout's anti-LLM policy scan over CONTRIBUTING.md /
730
+ * CODE_OF_CONDUCT.md / README.md (#979). When `matched` is true the issue
731
+ * should be skipped — the project explicitly disallows AI-generated
732
+ * contributions.
733
+ */
734
+ antiLLMPolicy?: {
735
+ matched: boolean;
736
+ matchedKeywords: string[];
737
+ sourceFile: string | null;
738
+ };
739
+ /**
740
+ * Classification of the issue's first linked PR (#978). `'none'` when
741
+ * no linked PR exists. The other buckets distinguish whether the user
742
+ * already has work in flight vs. a competing contributor.
743
+ */
744
+ linkedPRClassification?: 'none' | 'user_open' | 'user_closed' | 'user_merged' | 'other_open' | 'other_closed' | 'other_merged';
745
+ /**
746
+ * Optional SLM pre-triage classification (#1122). Populated when the
747
+ * user has set `slmTriageModel` and a local Ollama instance answered
748
+ * within the timeout. `null` otherwise.
749
+ */
750
+ slmTriage?: {
751
+ decision: 'pursue' | 'investigate' | 'skip';
752
+ confidence: 'high' | 'medium' | 'low';
753
+ reasons: string[];
754
+ modelVersion: string;
755
+ } | null;
728
756
  /** Success-likelihood grade (#858): predicts whether a PR will merge. */
729
757
  grade: {
730
758
  letter: 'A' | 'B' | 'C' | 'F';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oss-autopilot/core",
3
- "version": "3.0.1",
3
+ "version": "3.1.0",
4
4
  "description": "CLI and core library for managing open source contributions",
5
5
  "type": "module",
6
6
  "bin": {
@@ -54,18 +54,18 @@
54
54
  "dependencies": {
55
55
  "@octokit/plugin-throttling": "^11.0.3",
56
56
  "@octokit/rest": "^22.0.1",
57
- "@oss-scout/core": "^0.5.0",
57
+ "@oss-scout/core": "^0.7.1",
58
58
  "commander": "^14.0.3",
59
59
  "zod": "^4.3.6"
60
60
  },
61
61
  "devDependencies": {
62
- "@types/node": "^25.5.0",
63
- "@vitest/coverage-v8": "^4.1.0",
64
- "esbuild": "^0.27.4",
62
+ "@types/node": "^25.6.0",
63
+ "@vitest/coverage-v8": "^4.1.5",
64
+ "esbuild": "^0.28.0",
65
65
  "tsx": "^4.21.0",
66
- "typedoc": "^0.28.17",
66
+ "typedoc": "^0.28.19",
67
67
  "typescript": "^5.9.3",
68
- "vitest": "^4.1.0"
68
+ "vitest": "^4.1.5"
69
69
  },
70
70
  "scripts": {
71
71
  "build": "tsc",