@oss-autopilot/core 3.13.4 → 3.14.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.
- package/README.md +3 -3
- package/dist/cli-registry.js +59 -84
- package/dist/cli.bundle.cjs +112 -109
- package/dist/cli.js +5 -4
- package/dist/commands/comments.js +44 -10
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +50 -2
- package/dist/commands/curated-list.d.ts +17 -0
- package/dist/commands/curated-list.js +25 -0
- package/dist/commands/daily.d.ts +7 -1
- package/dist/commands/daily.js +136 -57
- package/dist/commands/dashboard-cache.d.ts +69 -0
- package/dist/commands/dashboard-cache.js +219 -0
- package/dist/commands/dashboard-data.d.ts +18 -10
- package/dist/commands/dashboard-data.js +58 -8
- package/dist/commands/dashboard-gist-sync.d.ts +93 -0
- package/dist/commands/dashboard-gist-sync.js +237 -0
- package/dist/commands/dashboard-server.d.ts +6 -10
- package/dist/commands/dashboard-server.js +181 -347
- package/dist/commands/features.js +6 -0
- package/dist/commands/guidelines.d.ts +6 -0
- package/dist/commands/guidelines.js +7 -0
- package/dist/commands/index.d.ts +2 -5
- package/dist/commands/index.js +2 -4
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +7 -1
- package/dist/commands/list-mark-done.js +6 -21
- package/dist/commands/list-move-tier.js +3 -5
- package/dist/commands/locate-issue-list.d.ts +25 -0
- package/dist/commands/locate-issue-list.js +67 -0
- package/dist/commands/merge-loop.d.ts +63 -0
- package/dist/commands/merge-loop.js +157 -0
- package/dist/commands/repo-vet.js +40 -1
- package/dist/commands/scout-bridge.d.ts +35 -2
- package/dist/commands/scout-bridge.js +65 -13
- package/dist/commands/search.d.ts +4 -6
- package/dist/commands/search.js +58 -11
- package/dist/commands/setup.d.ts +2 -0
- package/dist/commands/setup.js +56 -2
- package/dist/commands/skip-file-parser.d.ts +23 -0
- package/dist/commands/skip-file-parser.js +23 -10
- package/dist/commands/startup.d.ts +1 -6
- package/dist/commands/startup.js +25 -59
- package/dist/commands/track.d.ts +2 -2
- package/dist/commands/track.js +2 -2
- package/dist/commands/vet-list.d.ts +6 -6
- package/dist/commands/vet-list.js +194 -65
- package/dist/core/config-registry.js +36 -0
- package/dist/core/daily-logic.d.ts +25 -2
- package/dist/core/daily-logic.js +58 -3
- package/dist/core/gist-health.d.ts +81 -0
- package/dist/core/gist-health.js +39 -0
- package/dist/core/gist-state-store.d.ts +3 -1
- package/dist/core/gist-state-store.js +7 -2
- package/dist/core/github-stats.d.ts +2 -2
- package/dist/core/github-stats.js +20 -4
- package/dist/core/index.d.ts +5 -4
- package/dist/core/index.js +5 -4
- package/dist/core/issue-conversation.js +8 -2
- package/dist/core/issue-grading.d.ts +9 -0
- package/dist/core/issue-grading.js +9 -0
- package/dist/core/issue-verification.d.ts +39 -0
- package/dist/core/issue-verification.js +48 -0
- package/dist/core/pagination.d.ts +27 -0
- package/dist/core/pagination.js +23 -5
- package/dist/core/pr-comments-fetcher.d.ts +7 -0
- package/dist/core/pr-comments-fetcher.js +19 -8
- package/dist/core/pr-monitor.d.ts +2 -0
- package/dist/core/pr-monitor.js +26 -9
- package/dist/core/repo-score-manager.d.ts +2 -2
- package/dist/core/repo-score-manager.js +3 -3
- package/dist/core/repo-vet.d.ts +2 -2
- package/dist/core/repo-vet.js +1 -1
- package/dist/core/review-analysis.d.ts +19 -0
- package/dist/core/review-analysis.js +28 -0
- package/dist/core/state-schema.d.ts +43 -6
- package/dist/core/state-schema.js +81 -4
- package/dist/core/state.d.ts +36 -5
- package/dist/core/state.js +177 -28
- package/dist/core/strategy.js +6 -5
- package/dist/core/types.d.ts +8 -0
- package/dist/core/untrusted-content.d.ts +45 -0
- package/dist/core/untrusted-content.js +54 -0
- package/dist/formatters/json.d.ts +120 -12
- package/dist/formatters/json.js +55 -2
- package/package.json +2 -2
- package/dist/commands/shelve.d.ts +0 -45
- package/dist/commands/shelve.js +0 -54
package/dist/cli.js
CHANGED
|
@@ -80,13 +80,14 @@ program.hook('preAction', async (thisCommand, actionCommand) => {
|
|
|
80
80
|
// when Gist mode is the configured persistence (#1000). Hard errors
|
|
81
81
|
// still throw (#1202); the resolving degraded modes are surfaced in the
|
|
82
82
|
// JSON envelope so --json consumers see them too (#1433).
|
|
83
|
-
const { ensureGistPersistence } = await import('./core/index.js');
|
|
83
|
+
const { ensureGistPersistence, renderGistWarning } = await import('./core/index.js');
|
|
84
84
|
const status = await ensureGistPersistence(token);
|
|
85
85
|
if (status === 'degraded' || status === 'state-unreadable') {
|
|
86
86
|
const { setEnvelopeGistWarning } = await import('./formatters/json.js');
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
// Shared renderer (#1444). 'degraded' is ensureGistPersistence's
|
|
88
|
+
// deliberate conflation of the transient init fallback and a #1443
|
|
89
|
+
// degraded bootstrap — rendered as the init-fallback cause, as before.
|
|
90
|
+
setEnvelopeGistWarning(renderGistWarning(status === 'degraded' ? 'init-fallback' : 'state-unreadable'));
|
|
90
91
|
}
|
|
91
92
|
}
|
|
92
93
|
else {
|
|
@@ -7,7 +7,7 @@ import { wrapUntrustedContent } from '../core/untrusted-content.js';
|
|
|
7
7
|
import { ValidationError, isRateLimitOrAuthError } from '../core/errors.js';
|
|
8
8
|
import { warn } from '../core/logger.js';
|
|
9
9
|
const MODULE = 'comments';
|
|
10
|
-
import {
|
|
10
|
+
import { paginateAllDetailed } from '../core/pagination.js';
|
|
11
11
|
import { buildStalenessWarning } from '../formatters/json.js';
|
|
12
12
|
import { validateUrl, validateMessage, validateGitHubUrl, PR_URL_PATTERN, ISSUE_OR_PR_URL_PATTERN, ISSUE_URL_PATTERN, } from './validation.js';
|
|
13
13
|
/**
|
|
@@ -35,22 +35,22 @@ export async function runComments(options) {
|
|
|
35
35
|
// Get PR details
|
|
36
36
|
const { data: pr } = await octokit.pulls.get({ owner, repo, pull_number });
|
|
37
37
|
// Fetch review comments, issue comments, and reviews in parallel
|
|
38
|
-
const [
|
|
39
|
-
|
|
38
|
+
const [reviewCommentsResult, issueCommentsResult, reviewsResult] = await Promise.all([
|
|
39
|
+
paginateAllDetailed((page) => octokit.pulls.listReviewComments({
|
|
40
40
|
owner,
|
|
41
41
|
repo,
|
|
42
42
|
pull_number,
|
|
43
43
|
per_page: 100,
|
|
44
44
|
page,
|
|
45
45
|
})),
|
|
46
|
-
|
|
46
|
+
paginateAllDetailed((page) => octokit.issues.listComments({
|
|
47
47
|
owner,
|
|
48
48
|
repo,
|
|
49
49
|
issue_number: pull_number,
|
|
50
50
|
per_page: 100,
|
|
51
51
|
page,
|
|
52
52
|
})),
|
|
53
|
-
|
|
53
|
+
paginateAllDetailed((page) => octokit.pulls.listReviews({
|
|
54
54
|
owner,
|
|
55
55
|
repo,
|
|
56
56
|
pull_number,
|
|
@@ -58,12 +58,28 @@ export async function runComments(options) {
|
|
|
58
58
|
page,
|
|
59
59
|
})),
|
|
60
60
|
]);
|
|
61
|
+
const { items: reviewComments } = reviewCommentsResult;
|
|
62
|
+
const { items: issueComments } = issueCommentsResult;
|
|
63
|
+
const { items: reviews } = reviewsResult;
|
|
64
|
+
// Pagination truncation is a data-quality signal, not a failure: these
|
|
65
|
+
// endpoints return oldest-first, so hitting the page cap drops the NEWEST
|
|
66
|
+
// comments — exactly the ones a "what needs response" consumer cares
|
|
67
|
+
// about. Thread it into the structured warnings channel (#1456).
|
|
68
|
+
const truncatedStreams = [
|
|
69
|
+
...(reviewsResult.truncated ? ['reviews'] : []),
|
|
70
|
+
...(reviewCommentsResult.truncated ? ['inline review comments'] : []),
|
|
71
|
+
...(issueCommentsResult.truncated ? ['discussion comments'] : []),
|
|
72
|
+
];
|
|
61
73
|
// Filter out own comments, optionally show bots
|
|
62
74
|
const username = stateManager.getState().config.githubUsername;
|
|
63
75
|
const filterComment = (c) => {
|
|
64
76
|
if (!c.user)
|
|
65
77
|
return false;
|
|
66
|
-
|
|
78
|
+
// Lowercase both sides: GitHub logins are case-insensitive, and a
|
|
79
|
+
// non-canonical-case configured username must not leak the user's own
|
|
80
|
+
// comments into "needs response" (#1456). Mirrors review-analysis.ts
|
|
81
|
+
// and issue-conversation.ts.
|
|
82
|
+
if (c.user.login?.toLowerCase() === username?.toLowerCase())
|
|
67
83
|
return false;
|
|
68
84
|
if (c.user.type === 'Bot' && !options.showBots)
|
|
69
85
|
return false;
|
|
@@ -85,6 +101,18 @@ export async function runComments(options) {
|
|
|
85
101
|
const fenceLabel = `${owner}/${repo}#${pull_number}`;
|
|
86
102
|
const fence = (body, source, author, association) => wrapUntrustedContent(body, fenceLabel, { author, association, source });
|
|
87
103
|
const staleness = stateManager.getStateStaleness();
|
|
104
|
+
const warnings = [];
|
|
105
|
+
if (truncatedStreams.length > 0) {
|
|
106
|
+
warnings.push({
|
|
107
|
+
phase: 'fetch',
|
|
108
|
+
operation: 'fetch PR comments (truncated)',
|
|
109
|
+
message: `Pagination cap reached fetching ${truncatedStreams.join(', ')} for ${owner}/${repo}#${pull_number}; ` +
|
|
110
|
+
`the newest entries in those streams may be missing.`,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
if (staleness) {
|
|
114
|
+
warnings.push(buildStalenessWarning(staleness));
|
|
115
|
+
}
|
|
88
116
|
return {
|
|
89
117
|
pr: {
|
|
90
118
|
title: pr.title,
|
|
@@ -116,7 +144,7 @@ export async function runComments(options) {
|
|
|
116
144
|
inlineCommentCount: relevantReviewComments.length,
|
|
117
145
|
discussionCommentCount: relevantIssueComments.length,
|
|
118
146
|
},
|
|
119
|
-
...(
|
|
147
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
120
148
|
};
|
|
121
149
|
}
|
|
122
150
|
/**
|
|
@@ -212,6 +240,7 @@ export async function runClaim(options) {
|
|
|
212
240
|
});
|
|
213
241
|
// Add to tracked issues — non-fatal if state save fails (comment already posted)
|
|
214
242
|
let gistSyncWarning = null;
|
|
243
|
+
let stateSaveWarning;
|
|
215
244
|
try {
|
|
216
245
|
const stateManager = getStateManager();
|
|
217
246
|
stateManager.addIssue({
|
|
@@ -232,13 +261,18 @@ export async function runClaim(options) {
|
|
|
232
261
|
gistSyncWarning = await maybeCheckpoint(stateManager, MODULE);
|
|
233
262
|
}
|
|
234
263
|
catch (error) {
|
|
235
|
-
//
|
|
236
|
-
//
|
|
237
|
-
|
|
264
|
+
// The claim comment is live on GitHub but local state never tracked it —
|
|
265
|
+
// an otherwise-clean envelope would hide an invisible untracked claim, so
|
|
266
|
+
// thread the failure into the output (#1448). Structured warning instead
|
|
267
|
+
// of bare console.error so the breadcrumb shows up in the plugin's log
|
|
268
|
+
// pipeline (#1056 M24).
|
|
269
|
+
stateSaveWarning = `Comment posted on ${options.issueUrl} but failed to save to local state: ${error instanceof Error ? error.message : error}`;
|
|
270
|
+
warn(MODULE, stateSaveWarning);
|
|
238
271
|
}
|
|
239
272
|
return {
|
|
240
273
|
commentUrl: comment.html_url,
|
|
241
274
|
issueUrl: options.issueUrl,
|
|
242
275
|
...(gistSyncWarning ? { gistSyncWarning } : {}),
|
|
276
|
+
...(stateSaveWarning ? { stateSaveWarning } : {}),
|
|
243
277
|
};
|
|
244
278
|
}
|
|
@@ -13,6 +13,8 @@ export interface ConfigSetOutput {
|
|
|
13
13
|
success: true;
|
|
14
14
|
key: string;
|
|
15
15
|
value: string;
|
|
16
|
+
/** Set when the post-mutation Gist checkpoint failed; the local mutation succeeded (#1440). */
|
|
17
|
+
gistSyncWarning?: string;
|
|
16
18
|
}
|
|
17
19
|
export interface ConfigListKeysOutput {
|
|
18
20
|
keys: readonly ConfigKeyDef[];
|
package/dist/commands/config.js
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
* Config command
|
|
3
3
|
* Shows or updates configuration
|
|
4
4
|
*/
|
|
5
|
-
import { CONFIG_KEY_REGISTRY, formatUnknownKeyError, getStateManager } from '../core/index.js';
|
|
5
|
+
import { CONFIG_KEY_REGISTRY, formatUnknownKeyError, getStateManager, maybeCheckpoint, } from '../core/index.js';
|
|
6
6
|
import { ValidationError } from '../core/errors.js';
|
|
7
7
|
import { ISSUE_SCOPES, DIFF_TOOLS } from '../core/types.js';
|
|
8
8
|
import { validateGitHubUsername } from './validation.js';
|
|
9
|
+
const MODULE = 'config';
|
|
9
10
|
function validateScope(value) {
|
|
10
11
|
if (!ISSUE_SCOPES.includes(value)) {
|
|
11
12
|
throw new Error(`Invalid scope "${value}". Valid scopes: ${ISSUE_SCOPES.join(', ')}`);
|
|
@@ -115,6 +116,49 @@ export async function runConfig(options) {
|
|
|
115
116
|
}
|
|
116
117
|
break;
|
|
117
118
|
}
|
|
119
|
+
case 'add-avoid-repo': {
|
|
120
|
+
// Same owner/repo shape as exclude-repo, but no cleanupExcludedData
|
|
121
|
+
// call: avoidRepos is a soft ranking penalty, not a hard exclusion,
|
|
122
|
+
// so tracked data for the repo stays intact (#1464).
|
|
123
|
+
const parts = value.split('/');
|
|
124
|
+
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
125
|
+
throw new Error(`Invalid repo format "${value}". Use "owner/repo" format.`);
|
|
126
|
+
}
|
|
127
|
+
const currentAvoid = currentConfig.avoidRepos ?? [];
|
|
128
|
+
const valueLower = value.toLowerCase();
|
|
129
|
+
if (!currentAvoid.some((r) => r.toLowerCase() === valueLower)) {
|
|
130
|
+
stateManager.updateConfig({ avoidRepos: [...currentAvoid, value] });
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
case 'remove-avoid-repo': {
|
|
135
|
+
const currentAvoid = currentConfig.avoidRepos ?? [];
|
|
136
|
+
const valueLower = value.toLowerCase();
|
|
137
|
+
if (!currentAvoid.some((r) => r.toLowerCase() === valueLower)) {
|
|
138
|
+
throw new Error(`Repo "${value}" is not on the avoid list. Current avoid list: ${currentAvoid.join(', ') || '(empty)'}`);
|
|
139
|
+
}
|
|
140
|
+
stateManager.updateConfig({ avoidRepos: currentAvoid.filter((r) => r.toLowerCase() !== valueLower) });
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
case 'add-boost-issue-type': {
|
|
144
|
+
// Scout matches boostIssueTypes against issue labels case-insensitively,
|
|
145
|
+
// so the duplicate check is case-insensitive too (#1464).
|
|
146
|
+
const currentTypes = currentConfig.boostIssueTypes ?? [];
|
|
147
|
+
const valueLower = value.toLowerCase();
|
|
148
|
+
if (!currentTypes.some((t) => t.toLowerCase() === valueLower)) {
|
|
149
|
+
stateManager.updateConfig({ boostIssueTypes: [...currentTypes, value] });
|
|
150
|
+
}
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
case 'remove-boost-issue-type': {
|
|
154
|
+
const currentTypes = currentConfig.boostIssueTypes ?? [];
|
|
155
|
+
const valueLower = value.toLowerCase();
|
|
156
|
+
if (!currentTypes.some((t) => t.toLowerCase() === valueLower)) {
|
|
157
|
+
throw new Error(`Issue type "${value}" is not on the boost list. Current boost list: ${currentTypes.join(', ') || '(empty)'}`);
|
|
158
|
+
}
|
|
159
|
+
stateManager.updateConfig({ boostIssueTypes: currentTypes.filter((t) => t.toLowerCase() !== valueLower) });
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
118
162
|
case 'issueListPath': {
|
|
119
163
|
stateManager.updateConfig({ issueListPath: value || undefined });
|
|
120
164
|
break;
|
|
@@ -136,5 +180,9 @@ export async function runConfig(options) {
|
|
|
136
180
|
throw new ValidationError(formatUnknownKeyError(options.key, 'config'));
|
|
137
181
|
}
|
|
138
182
|
}
|
|
139
|
-
|
|
183
|
+
// Push the config mutation to the Gist in gist mode (no-op locally).
|
|
184
|
+
// Without this the change only hits the local cache and the next
|
|
185
|
+
// bootstrap reverts it from the Gist (#1440).
|
|
186
|
+
const gistSyncWarning = await maybeCheckpoint(stateManager, MODULE);
|
|
187
|
+
return { success: true, key: options.key, value, ...(gistSyncWarning ? { gistSyncWarning } : {}) };
|
|
140
188
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared line-matching helpers for the curated issue-list commands
|
|
3
|
+
* (`list-move-tier`, `list-mark-done`). Both commands locate entries in the
|
|
4
|
+
* same markdown file, so they must agree on what counts as an entry line
|
|
5
|
+
* and on URL matching semantics — they had drifted on both (#1442).
|
|
6
|
+
*/
|
|
7
|
+
/** Top-level list entry — `- `, `* `, `+ `, or `1.` at the start (no leading whitespace). */
|
|
8
|
+
export declare const ENTRY_LINE_RE: RegExp;
|
|
9
|
+
/** The entry's leading list marker, for stripping before inspecting the body. */
|
|
10
|
+
export declare const ENTRY_MARKER_RE: RegExp;
|
|
11
|
+
/**
|
|
12
|
+
* Match the URL only when followed by a non-digit character (or end of
|
|
13
|
+
* line). A bare `includes(issueUrl)` would match `issues/1` against a line
|
|
14
|
+
* containing `issues/10`, so acting on issue 1 would also hit issues
|
|
15
|
+
* 10/100/... (#1442).
|
|
16
|
+
*/
|
|
17
|
+
export declare function lineMentionsUrl(line: string, issueUrl: string): boolean;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared line-matching helpers for the curated issue-list commands
|
|
3
|
+
* (`list-move-tier`, `list-mark-done`). Both commands locate entries in the
|
|
4
|
+
* same markdown file, so they must agree on what counts as an entry line
|
|
5
|
+
* and on URL matching semantics — they had drifted on both (#1442).
|
|
6
|
+
*/
|
|
7
|
+
/** Top-level list entry — `- `, `* `, `+ `, or `1.` at the start (no leading whitespace). */
|
|
8
|
+
export const ENTRY_LINE_RE = /^[*+-]\s|^\d+\.\s/;
|
|
9
|
+
/** The entry's leading list marker, for stripping before inspecting the body. */
|
|
10
|
+
export const ENTRY_MARKER_RE = /^(?:[*+-]\s+|\d+\.\s+)/;
|
|
11
|
+
/**
|
|
12
|
+
* Match the URL only when followed by a non-digit character (or end of
|
|
13
|
+
* line). A bare `includes(issueUrl)` would match `issues/1` against a line
|
|
14
|
+
* containing `issues/10`, so acting on issue 1 would also hit issues
|
|
15
|
+
* 10/100/... (#1442).
|
|
16
|
+
*/
|
|
17
|
+
export function lineMentionsUrl(line, issueUrl) {
|
|
18
|
+
const idx = line.indexOf(issueUrl);
|
|
19
|
+
if (idx === -1)
|
|
20
|
+
return false;
|
|
21
|
+
const next = line.charCodeAt(idx + issueUrl.length);
|
|
22
|
+
// NaN (end of string) → boundary OK. Otherwise reject any digit
|
|
23
|
+
// immediately after the URL so 'issues/1' doesn't match 'issues/10'.
|
|
24
|
+
return Number.isNaN(next) || next < 48 /* '0' */ || next > 57; /* '9' */
|
|
25
|
+
}
|
package/dist/commands/daily.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { type AttentionSummary, type DailyDigest, type CommentedIssue, type PRCheckFailure, type RepoGroup } from '../core/index.js';
|
|
10
10
|
import { type StrategyResult } from '../core/strategy.js';
|
|
11
|
-
import { type DailyOutput, type DailyWarning, type CapacityAssessment, type ActionableIssue, type ActionMenu } from '../formatters/json.js';
|
|
11
|
+
import { type DailyOutput, type DailyWarning, type CapacityAssessment, type ActionableIssue, type ActionMenu, type MergedPRListUpdate } from '../formatters/json.js';
|
|
12
12
|
export { applyStatusOverrides, computeRepoSignals, groupPRsByRepo, assessCapacity, collectActionableIssues, computeActionMenu, toShelvedPRRef, formatBriefSummary, formatSummary, printDigest, CRITICAL_STATUSES, } from '../core/index.js';
|
|
13
13
|
import { buildStarFilter } from '../core/daily-logic.js';
|
|
14
14
|
export { buildStarFilter };
|
|
@@ -38,6 +38,12 @@ export interface DailyCheckResult {
|
|
|
38
38
|
* where the gate stays silent.
|
|
39
39
|
*/
|
|
40
40
|
strategySummary?: StrategyResult | null;
|
|
41
|
+
/**
|
|
42
|
+
* Curated-list entries auto-marked done because their PR merged (#1463).
|
|
43
|
+
* Set only when at least one entry was struck this run; merge-free runs
|
|
44
|
+
* omit it so serialized output (and contract goldens) stay unchanged.
|
|
45
|
+
*/
|
|
46
|
+
listUpdates?: MergedPRListUpdate[];
|
|
41
47
|
}
|
|
42
48
|
/**
|
|
43
49
|
* Convert a full DailyCheckResult to the compact DailyOutput for JSON serialization (#287).
|