@oss-autopilot/core 1.15.1 → 1.15.2
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-registry.js +32 -0
- package/dist/cli.bundle.cjs +63 -59
- package/dist/commands/daily.d.ts +9 -0
- package/dist/commands/daily.js +4 -1
- package/dist/commands/scout-bridge.js +2 -1
- package/dist/commands/skip-add.d.ts +25 -0
- package/dist/commands/skip-add.js +48 -0
- package/dist/commands/skip-file-parser.d.ts +25 -0
- package/dist/commands/skip-file-parser.js +92 -0
- package/package.json +1 -1
package/dist/commands/daily.d.ts
CHANGED
|
@@ -33,6 +33,15 @@ export interface DailyCheckResult {
|
|
|
33
33
|
repoGroups: RepoGroup[];
|
|
34
34
|
failures: PRCheckFailure[];
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Convert a full DailyCheckResult to the compact DailyOutput for JSON serialization (#287).
|
|
38
|
+
* Deduplicates PR objects: category arrays become PR URL references,
|
|
39
|
+
* full objects live only in digest.openPRs. Reduces JSON payload size ~60-70%.
|
|
40
|
+
*
|
|
41
|
+
* Exported for the `daily --json` contract test (#986), which pins this
|
|
42
|
+
* shape transformation without spinning up the full fetch pipeline.
|
|
43
|
+
*/
|
|
44
|
+
export declare function toDailyOutput(result: DailyCheckResult): DailyOutput;
|
|
36
45
|
/**
|
|
37
46
|
* Core daily check logic, extracted for reuse by the startup command.
|
|
38
47
|
* Fetches all open PRs, updates state, and returns structured output.
|
package/dist/commands/daily.js
CHANGED
|
@@ -370,8 +370,11 @@ function generateDigestOutput(digest, activePRs, shelvedPRs, commentedIssues, fa
|
|
|
370
370
|
* Convert a full DailyCheckResult to the compact DailyOutput for JSON serialization (#287).
|
|
371
371
|
* Deduplicates PR objects: category arrays become PR URL references,
|
|
372
372
|
* full objects live only in digest.openPRs. Reduces JSON payload size ~60-70%.
|
|
373
|
+
*
|
|
374
|
+
* Exported for the `daily --json` contract test (#986), which pins this
|
|
375
|
+
* shape transformation without spinning up the full fetch pipeline.
|
|
373
376
|
*/
|
|
374
|
-
function toDailyOutput(result) {
|
|
377
|
+
export function toDailyOutput(result) {
|
|
375
378
|
return {
|
|
376
379
|
digest: deduplicateDigest(result.digest),
|
|
377
380
|
capacity: result.capacity,
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { createScout } from '@oss-scout/core';
|
|
6
6
|
import { getStateManager, requireGitHubToken } from '../core/index.js';
|
|
7
|
+
import { loadSkippedIssues } from './skip-file-parser.js';
|
|
7
8
|
/**
|
|
8
9
|
* Build a ScoutState from the current AgentState.
|
|
9
10
|
* Maps oss-autopilot's config and state fields to oss-scout's state format.
|
|
@@ -52,7 +53,7 @@ export function buildScoutState() {
|
|
|
52
53
|
openedAt: pr.createdAt,
|
|
53
54
|
})),
|
|
54
55
|
savedResults: [],
|
|
55
|
-
skippedIssues:
|
|
56
|
+
skippedIssues: loadSkippedIssues(config.skippedIssuesPath),
|
|
56
57
|
lastRunAt: state.lastRunAt,
|
|
57
58
|
};
|
|
58
59
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface SkipAddOptions {
|
|
2
|
+
issueUrl: string;
|
|
3
|
+
/** Override the configured `skippedIssuesPath`. */
|
|
4
|
+
skipFilePath?: string;
|
|
5
|
+
/** Injected for deterministic tests. Defaults to `new Date()`. */
|
|
6
|
+
now?: Date;
|
|
7
|
+
}
|
|
8
|
+
export interface SkipAddOutput {
|
|
9
|
+
added: boolean;
|
|
10
|
+
alreadyPresent: boolean;
|
|
11
|
+
url: string;
|
|
12
|
+
path: string;
|
|
13
|
+
/** Populated only when an entry was appended. */
|
|
14
|
+
date?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Append an issue URL to the skipped-issues file.
|
|
18
|
+
*
|
|
19
|
+
* Creates the file with the standard header if it doesn't exist. If the URL
|
|
20
|
+
* is already present (per the skip-file-parser's view of the file), this is
|
|
21
|
+
* a no-op and returns `alreadyPresent: true`.
|
|
22
|
+
*
|
|
23
|
+
* @throws when no path can be resolved or the URL isn't a GitHub issue/PR URL.
|
|
24
|
+
*/
|
|
25
|
+
export declare function runSkipAdd(options: SkipAddOptions): SkipAddOutput;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import { loadSkippedIssues } from './skip-file-parser.js';
|
|
3
|
+
import { getStateManager } from '../core/index.js';
|
|
4
|
+
// Keep in sync with GITHUB_URL_RE in skip-file-parser.ts.
|
|
5
|
+
const GITHUB_URL_RE = /^https:\/\/github\.com\/([^/]+\/[^/]+)\/(?:issues|pull)\/(\d+)(?:[/?#].*)?$/;
|
|
6
|
+
const FILE_HEADER = '# Skipped Issues — auto-culled after 90 days\n# Format: YYYY-MM-DD URL\n\n';
|
|
7
|
+
function formatUtcDate(d) {
|
|
8
|
+
return d.toISOString().slice(0, 10);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Append an issue URL to the skipped-issues file.
|
|
12
|
+
*
|
|
13
|
+
* Creates the file with the standard header if it doesn't exist. If the URL
|
|
14
|
+
* is already present (per the skip-file-parser's view of the file), this is
|
|
15
|
+
* a no-op and returns `alreadyPresent: true`.
|
|
16
|
+
*
|
|
17
|
+
* @throws when no path can be resolved or the URL isn't a GitHub issue/PR URL.
|
|
18
|
+
*/
|
|
19
|
+
export function runSkipAdd(options) {
|
|
20
|
+
const skipFilePath = options.skipFilePath ?? getStateManager().getState().config.skippedIssuesPath;
|
|
21
|
+
if (!skipFilePath) {
|
|
22
|
+
throw new Error('No skipped-issues path configured. Set one via `oss-autopilot config --set skippedIssuesPath=<path>` or pass --path.');
|
|
23
|
+
}
|
|
24
|
+
if (!GITHUB_URL_RE.test(options.issueUrl)) {
|
|
25
|
+
throw new Error(`Invalid GitHub issue or PR URL: ${options.issueUrl}`);
|
|
26
|
+
}
|
|
27
|
+
const existing = loadSkippedIssues(skipFilePath);
|
|
28
|
+
if (existing.some((entry) => entry.url === options.issueUrl)) {
|
|
29
|
+
return {
|
|
30
|
+
added: false,
|
|
31
|
+
alreadyPresent: true,
|
|
32
|
+
url: options.issueUrl,
|
|
33
|
+
path: skipFilePath,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (!fs.existsSync(skipFilePath)) {
|
|
37
|
+
fs.writeFileSync(skipFilePath, FILE_HEADER);
|
|
38
|
+
}
|
|
39
|
+
const date = formatUtcDate(options.now ?? new Date());
|
|
40
|
+
fs.appendFileSync(skipFilePath, `${date} ${options.issueUrl}\n`);
|
|
41
|
+
return {
|
|
42
|
+
added: true,
|
|
43
|
+
alreadyPresent: false,
|
|
44
|
+
url: options.issueUrl,
|
|
45
|
+
path: skipFilePath,
|
|
46
|
+
date,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser for the skipped-issues markdown file (#989).
|
|
3
|
+
*
|
|
4
|
+
* The file format is one entry per line:
|
|
5
|
+
* 2026-04-15 https://github.com/owner/repo/issues/123
|
|
6
|
+
* Lines starting with `#` and blank lines are ignored.
|
|
7
|
+
*
|
|
8
|
+
* Produces SkippedIssue entries that plug directly into oss-scout's ScoutState
|
|
9
|
+
* so the search engine filters already-skipped URLs out of results.
|
|
10
|
+
*/
|
|
11
|
+
import type { SkippedIssue } from '@oss-scout/core';
|
|
12
|
+
/**
|
|
13
|
+
* Parse the raw text of a skipped-issues file into SkippedIssue entries.
|
|
14
|
+
* Pure function — no I/O. Malformed lines are warned and skipped; the rest
|
|
15
|
+
* pass through unchanged.
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseSkippedIssuesContent(content: string): SkippedIssue[];
|
|
18
|
+
/**
|
|
19
|
+
* Read the skipped-issues file from disk and parse it.
|
|
20
|
+
* Returns `[]` when:
|
|
21
|
+
* - `path` is undefined or empty,
|
|
22
|
+
* - the file does not exist,
|
|
23
|
+
* - the file cannot be read (a warning is logged).
|
|
24
|
+
*/
|
|
25
|
+
export declare function loadSkippedIssues(path: string | undefined): SkippedIssue[];
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser for the skipped-issues markdown file (#989).
|
|
3
|
+
*
|
|
4
|
+
* The file format is one entry per line:
|
|
5
|
+
* 2026-04-15 https://github.com/owner/repo/issues/123
|
|
6
|
+
* Lines starting with `#` and blank lines are ignored.
|
|
7
|
+
*
|
|
8
|
+
* Produces SkippedIssue entries that plug directly into oss-scout's ScoutState
|
|
9
|
+
* so the search engine filters already-skipped URLs out of results.
|
|
10
|
+
*/
|
|
11
|
+
import * as fs from 'fs';
|
|
12
|
+
import { warn } from '../core/logger.js';
|
|
13
|
+
import { errorMessage } from '../core/errors.js';
|
|
14
|
+
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
15
|
+
const GITHUB_URL_RE = /^https:\/\/github\.com\/([^/]+\/[^/]+)\/(?:issues|pull)\/(\d+)(?:[/?#].*)?$/;
|
|
16
|
+
/**
|
|
17
|
+
* Parse the raw text of a skipped-issues file into SkippedIssue entries.
|
|
18
|
+
* Pure function — no I/O. Malformed lines are warned and skipped; the rest
|
|
19
|
+
* pass through unchanged.
|
|
20
|
+
*/
|
|
21
|
+
export function parseSkippedIssuesContent(content) {
|
|
22
|
+
const results = [];
|
|
23
|
+
const lines = content.split(/\r?\n/);
|
|
24
|
+
for (let i = 0; i < lines.length; i++) {
|
|
25
|
+
const lineNumber = i + 1;
|
|
26
|
+
const line = lines[i].trim();
|
|
27
|
+
if (line === '' || line.startsWith('#'))
|
|
28
|
+
continue;
|
|
29
|
+
// Split on first whitespace run: "YYYY-MM-DD <url>"
|
|
30
|
+
const match = line.match(/^(\S+)\s+(\S+)\s*$/);
|
|
31
|
+
if (!match) {
|
|
32
|
+
warn('skip-file-parser', `Line ${lineNumber}: malformed (expected "<date> <url>"): ${line}`);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const [, dateStr, url] = match;
|
|
36
|
+
if (!DATE_RE.test(dateStr)) {
|
|
37
|
+
warn('skip-file-parser', `Line ${lineNumber}: invalid date format (expected YYYY-MM-DD): ${line}`);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const dateMs = Date.parse(`${dateStr}T00:00:00.000Z`);
|
|
41
|
+
if (Number.isNaN(dateMs)) {
|
|
42
|
+
warn('skip-file-parser', `Line ${lineNumber}: unparseable date: ${line}`);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
// Guard against JS Date normalization — Date.parse silently shifts
|
|
46
|
+
// invalid calendar dates (e.g. 2026-02-30 → 2026-03-02). Without this
|
|
47
|
+
// round-trip check the entry would be stored under the wrong date and
|
|
48
|
+
// scout's 90-day cull would run against a shifted value.
|
|
49
|
+
const roundTrip = new Date(dateMs).toISOString().slice(0, 10);
|
|
50
|
+
if (roundTrip !== dateStr) {
|
|
51
|
+
warn('skip-file-parser', `Line ${lineNumber}: invalid calendar date ${dateStr} (would be normalized to ${roundTrip}): ${line}`);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const urlMatch = url.match(GITHUB_URL_RE);
|
|
55
|
+
if (!urlMatch) {
|
|
56
|
+
warn('skip-file-parser', `Line ${lineNumber}: non-GitHub-issue URL: ${line}`);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const [, repo, numberStr] = urlMatch;
|
|
60
|
+
const number = Number.parseInt(numberStr, 10);
|
|
61
|
+
results.push({
|
|
62
|
+
url,
|
|
63
|
+
repo,
|
|
64
|
+
number,
|
|
65
|
+
title: '',
|
|
66
|
+
skippedAt: new Date(dateMs).toISOString(),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return results;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Read the skipped-issues file from disk and parse it.
|
|
73
|
+
* Returns `[]` when:
|
|
74
|
+
* - `path` is undefined or empty,
|
|
75
|
+
* - the file does not exist,
|
|
76
|
+
* - the file cannot be read (a warning is logged).
|
|
77
|
+
*/
|
|
78
|
+
export function loadSkippedIssues(path) {
|
|
79
|
+
if (!path)
|
|
80
|
+
return [];
|
|
81
|
+
if (!fs.existsSync(path))
|
|
82
|
+
return [];
|
|
83
|
+
let content;
|
|
84
|
+
try {
|
|
85
|
+
content = fs.readFileSync(path, 'utf-8');
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
warn('skip-file-parser', `Failed to read skipped-issues file at ${path}: ${errorMessage(err)}`);
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
return parseSkippedIssuesContent(content);
|
|
92
|
+
}
|