@reneturcios/greenlight 0.10.1 → 0.11.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/README.md +3 -0
- package/dist/autopilot-fixes.d.ts +50 -0
- package/dist/autopilot-fixes.js +216 -0
- package/dist/autopilot-fixes.js.map +1 -0
- package/dist/autopilot-git.d.ts +83 -0
- package/dist/autopilot-git.js +164 -0
- package/dist/autopilot-git.js.map +1 -0
- package/dist/autopilot-triage.d.ts +93 -0
- package/dist/autopilot-triage.js +222 -0
- package/dist/autopilot-triage.js.map +1 -0
- package/dist/autopilot.d.ts +207 -0
- package/dist/autopilot.js +903 -0
- package/dist/autopilot.js.map +1 -0
- package/dist/cli.js +44 -0
- package/dist/cli.js.map +1 -1
- package/dist/help.js +18 -0
- package/dist/help.js.map +1 -1
- package/dist/index.js +18 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/server.json +6 -3
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { authorKindOf } from "./review-triage.js";
|
|
2
|
+
/**
|
|
3
|
+
* Pull candidate file paths and short code quotes out of a bot finding body.
|
|
4
|
+
* Review comments usually name the file explicitly (backticked paths or
|
|
5
|
+
* file names) or ship a diff hunk; both are usable for verification.
|
|
6
|
+
*/
|
|
7
|
+
export function extractCandidates(body, anchoredPath) {
|
|
8
|
+
const paths = new Set();
|
|
9
|
+
if (anchoredPath)
|
|
10
|
+
paths.add(anchoredPath);
|
|
11
|
+
const backtick = body.match(/`([^`\n]{1,120})`/g) ?? [];
|
|
12
|
+
for (const b of backtick) {
|
|
13
|
+
const inner = b.slice(1, -1).trim();
|
|
14
|
+
if (/^[a-zA-Z0-9_./-]+\.[a-zA-Z0-9]{1,8}$/.test(inner) || inner.startsWith("src/") || inner.startsWith("packages/")) {
|
|
15
|
+
paths.add(inner.replace(/^\.\//, ""));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const quoted = [];
|
|
19
|
+
for (const m of body.matchAll(/^```(?:\w+)?\n([\s\S]{1,800}?)\n```/gm)) {
|
|
20
|
+
const snippet = m[1].trim();
|
|
21
|
+
if (snippet.length >= 12)
|
|
22
|
+
quoted.push(snippet);
|
|
23
|
+
}
|
|
24
|
+
const inline = body.match(/`([^`]{12,160})`/g) ?? [];
|
|
25
|
+
for (const b of inline)
|
|
26
|
+
quoted.push(b.slice(1, -1));
|
|
27
|
+
return { paths: [...paths].slice(0, 4), quotes: quoted.slice(0, 4) };
|
|
28
|
+
}
|
|
29
|
+
const normalize = (s) => s.replace(/\s+/g, " ").trim();
|
|
30
|
+
/**
|
|
31
|
+
* Deterministically verify one bot finding against the code at head.
|
|
32
|
+
* REAL when the flagged pattern exists as described; FALSE_POSITIVE when
|
|
33
|
+
* it demonstrably does not (counter-evidence quoted); NEEDS_HUMAN when the
|
|
34
|
+
* files cannot be resolved or the claim is not checkable this way.
|
|
35
|
+
*/
|
|
36
|
+
export async function verifyBotFinding(item, deps = {}) {
|
|
37
|
+
const { paths, quotes } = extractCandidates(item.body, item.path);
|
|
38
|
+
const fetchFile = deps.fetchFileAtHead;
|
|
39
|
+
if (paths.length === 0 && quotes.length === 0) {
|
|
40
|
+
return {
|
|
41
|
+
verdict: "NEEDS_HUMAN",
|
|
42
|
+
evidence: "finding names no file and quotes no code to verify against",
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (!fetchFile) {
|
|
46
|
+
return {
|
|
47
|
+
verdict: "NEEDS_HUMAN",
|
|
48
|
+
evidence: "no file fetcher available, cannot verify against head",
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const texts = [];
|
|
52
|
+
for (const p of paths) {
|
|
53
|
+
try {
|
|
54
|
+
const content = await fetchFile(p);
|
|
55
|
+
if (content !== null)
|
|
56
|
+
texts.push(content);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// unreadable file: keep going with what we have
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (texts.length === 0) {
|
|
63
|
+
return {
|
|
64
|
+
verdict: "NEEDS_HUMAN",
|
|
65
|
+
evidence: `could not read any of the referenced files (${paths.join(", ")}) at head`,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const haystack = normalize(texts.join("\n"));
|
|
69
|
+
let real = false;
|
|
70
|
+
let falsePositive = false;
|
|
71
|
+
let counter = "";
|
|
72
|
+
for (const q of quotes) {
|
|
73
|
+
const needle = normalize(q);
|
|
74
|
+
if (needle.length < 12)
|
|
75
|
+
continue;
|
|
76
|
+
if (haystack.includes(needle)) {
|
|
77
|
+
real = true;
|
|
78
|
+
}
|
|
79
|
+
else if (!counter) {
|
|
80
|
+
// A quoted code block that does not exist at head is a candidate
|
|
81
|
+
// false positive, unless it is clearly a suggestion (diff-like).
|
|
82
|
+
if (!/^[+-]/.test(q.trim()) && !/\bsuggest\b|\binstead\b/i.test(item.body)) {
|
|
83
|
+
falsePositive = true;
|
|
84
|
+
counter = q.length > 160 ? q.slice(0, 160) + "..." : q;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (real) {
|
|
89
|
+
return { verdict: "REAL", evidence: `quoted code is present at head (${paths.length} file(s) checked)` };
|
|
90
|
+
}
|
|
91
|
+
if (falsePositive) {
|
|
92
|
+
return {
|
|
93
|
+
verdict: "FALSE_POSITIVE",
|
|
94
|
+
evidence: `the finding quotes "${counter}", which does not exist at head in any referenced file`,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
verdict: "NEEDS_HUMAN",
|
|
99
|
+
evidence: "the finding's claim could not be reduced to a code lookup",
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/** Kind-only classification for review items, before any verification. */
|
|
103
|
+
export function classifyReviewItem(input) {
|
|
104
|
+
const body = input.body;
|
|
105
|
+
const state = (input.reviewState ?? "").toUpperCase();
|
|
106
|
+
if (state === "APPROVED")
|
|
107
|
+
return "approval";
|
|
108
|
+
if (state === "CHANGES_REQUESTED")
|
|
109
|
+
return "review_change_request";
|
|
110
|
+
if (input.authorKind === "bot")
|
|
111
|
+
return "bot_finding";
|
|
112
|
+
// Human authors: questions and requests are maintainer input, not noise.
|
|
113
|
+
if (/\?\s*$/.test(body.trim()) || /\?(\s|$)/.test(body.slice(0, 200)))
|
|
114
|
+
return "maintainer_question";
|
|
115
|
+
if (/\b(please|could you|would you|can you)\b/i.test(body))
|
|
116
|
+
return "maintainer_question";
|
|
117
|
+
return "other";
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Build Autopilot feedback items from review-triage findings, keeping the
|
|
121
|
+
* triage class and the ready reply draft. Bot findings keep kind
|
|
122
|
+
* "bot_finding" so the verifier can adjudicate them.
|
|
123
|
+
*/
|
|
124
|
+
export function feedbackFromReviewFindings(findings, prAuthor) {
|
|
125
|
+
const out = [];
|
|
126
|
+
for (const f of findings) {
|
|
127
|
+
const isAuthor = f.author === prAuthor;
|
|
128
|
+
if (isAuthor)
|
|
129
|
+
continue; // the author's own comments are replies, not incoming
|
|
130
|
+
const kind = f.class === "likely-noise"
|
|
131
|
+
? "other"
|
|
132
|
+
: classifyReviewItem({ body: f.summary, author: f.author, authorKind: f.authorKind });
|
|
133
|
+
out.push({
|
|
134
|
+
id: `finding:${f.author}:${oneLineKey(f.summary)}`,
|
|
135
|
+
kind: f.class === "likely-noise" ? "other" : kind,
|
|
136
|
+
title: f.summary.slice(0, 120),
|
|
137
|
+
body: f.summary,
|
|
138
|
+
author: f.author,
|
|
139
|
+
authorKind: f.authorKind,
|
|
140
|
+
path: f.path,
|
|
141
|
+
line: f.line,
|
|
142
|
+
// Bot findings are verified against head by the Autopilot run after this.
|
|
143
|
+
verdict: undefined,
|
|
144
|
+
evidence: f.reason,
|
|
145
|
+
draftReply: f.draft || undefined,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return out;
|
|
149
|
+
}
|
|
150
|
+
function oneLineKey(s) {
|
|
151
|
+
return s.replace(/\s+/g, " ").trim().slice(0, 60);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Build feedback items from CI triage. Only failures the classifier leans
|
|
155
|
+
* "yours" or cannot place (indeterminate) become fix candidates; pre-existing
|
|
156
|
+
* and external blockers are reported but never touched.
|
|
157
|
+
*/
|
|
158
|
+
export function feedbackFromCiChecks(checks) {
|
|
159
|
+
return checks
|
|
160
|
+
.filter((c) => c.conclusion !== "success")
|
|
161
|
+
.map((c) => ({
|
|
162
|
+
id: `check:${c.name}`,
|
|
163
|
+
kind: "ci_failure",
|
|
164
|
+
title: `CI: ${c.name} (${c.conclusion})`,
|
|
165
|
+
body: [c.name, c.conclusion, c.failureLines?.join(" | ") ?? ""].filter(Boolean).join(" - "),
|
|
166
|
+
author: "ci",
|
|
167
|
+
authorKind: "bot",
|
|
168
|
+
url: c.detailsUrl,
|
|
169
|
+
evidence: c.evidence,
|
|
170
|
+
verdict: c.class === "likely-yours"
|
|
171
|
+
? "REAL"
|
|
172
|
+
: c.class === "indeterminate"
|
|
173
|
+
? "NEEDS_HUMAN"
|
|
174
|
+
: "FALSE_POSITIVE",
|
|
175
|
+
}));
|
|
176
|
+
}
|
|
177
|
+
export function summarizeFeedback(items) {
|
|
178
|
+
const byKind = {
|
|
179
|
+
ci_failure: 0,
|
|
180
|
+
review_change_request: 0,
|
|
181
|
+
bot_finding: 0,
|
|
182
|
+
merge_conflict: 0,
|
|
183
|
+
dco_gap: 0,
|
|
184
|
+
maintainer_question: 0,
|
|
185
|
+
approval: 0,
|
|
186
|
+
other: 0,
|
|
187
|
+
};
|
|
188
|
+
let botFindingsReal = 0;
|
|
189
|
+
let botFindingsFalsePositive = 0;
|
|
190
|
+
let botFindingsNeedsHuman = 0;
|
|
191
|
+
let ciIntroduced = 0;
|
|
192
|
+
let ciPreExistingOrExternal = 0;
|
|
193
|
+
for (const i of items) {
|
|
194
|
+
byKind[i.kind] += 1;
|
|
195
|
+
if (i.kind === "bot_finding") {
|
|
196
|
+
if (i.verdict === "REAL")
|
|
197
|
+
botFindingsReal += 1;
|
|
198
|
+
else if (i.verdict === "FALSE_POSITIVE")
|
|
199
|
+
botFindingsFalsePositive += 1;
|
|
200
|
+
else if (i.verdict === "NEEDS_HUMAN")
|
|
201
|
+
botFindingsNeedsHuman += 1;
|
|
202
|
+
}
|
|
203
|
+
if (i.kind === "ci_failure") {
|
|
204
|
+
if (i.verdict === "REAL")
|
|
205
|
+
ciIntroduced += 1;
|
|
206
|
+
else
|
|
207
|
+
ciPreExistingOrExternal += 1;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
total: items.length,
|
|
212
|
+
byKind,
|
|
213
|
+
botFindingsReal,
|
|
214
|
+
botFindingsFalsePositive,
|
|
215
|
+
botFindingsNeedsHuman,
|
|
216
|
+
ciIntroduced,
|
|
217
|
+
ciPreExistingOrExternal,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
// Re-export for callers that only need the author kind helper.
|
|
221
|
+
export { authorKindOf };
|
|
222
|
+
//# sourceMappingURL=autopilot-triage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autopilot-triage.js","sourceRoot":"","sources":["../src/autopilot-triage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { type CensusPr } from "./sync.js";
|
|
2
|
+
import { type ClassifiedCheck } from "./ci-triage.js";
|
|
3
|
+
import { authorKindOf } from "./review-triage.js";
|
|
4
|
+
import { type GitOps } from "./autopilot-git.js";
|
|
5
|
+
import { type FeedbackItem, type FeedbackKind } from "./autopilot-triage.js";
|
|
6
|
+
import { type GitRunner } from "./autopilot-fixes.js";
|
|
7
|
+
/** Rene's identity for fix commits. Standing signatory authority, non-legal. */
|
|
8
|
+
export declare const AUTOPILOT_COMMITTER: {
|
|
9
|
+
name: string;
|
|
10
|
+
email: string;
|
|
11
|
+
};
|
|
12
|
+
export interface AutopilotPull {
|
|
13
|
+
owner: string;
|
|
14
|
+
repo: string;
|
|
15
|
+
number: number;
|
|
16
|
+
url: string;
|
|
17
|
+
title: string;
|
|
18
|
+
author: string;
|
|
19
|
+
headSha: string;
|
|
20
|
+
baseRef: string;
|
|
21
|
+
baseSha: string;
|
|
22
|
+
mergeable: boolean | null;
|
|
23
|
+
mergeableState: string | null;
|
|
24
|
+
forkOwner: string;
|
|
25
|
+
forkRepo: string;
|
|
26
|
+
forkBranch: string;
|
|
27
|
+
forkCloneUrl: string;
|
|
28
|
+
}
|
|
29
|
+
export interface PrWatermark {
|
|
30
|
+
lastHeadSha: string;
|
|
31
|
+
lastReviewId: number;
|
|
32
|
+
lastReviewCommentId: number;
|
|
33
|
+
lastIssueCommentId: number;
|
|
34
|
+
checkConclusions: Record<string, string>;
|
|
35
|
+
}
|
|
36
|
+
export interface AutopilotState {
|
|
37
|
+
version: number;
|
|
38
|
+
prs: Record<string, PrWatermark>;
|
|
39
|
+
}
|
|
40
|
+
export declare function defaultAutopilotStatePath(): string;
|
|
41
|
+
export declare function defaultRepliesPath(): string;
|
|
42
|
+
/**
|
|
43
|
+
* Autopilot consumes the v0.10.1 capability ledger (src/capabilities.ts)
|
|
44
|
+
* rather than duplicating it: loadCapabilities reads the canonical
|
|
45
|
+
* ~/.greenlight/capabilities.json shape, prCreateBlockReason answers the
|
|
46
|
+
* skip question, and fix outcomes are appended with autopilot-scoped
|
|
47
|
+
* actions so they never shadow the ledger's pr_create/claim outcomes.
|
|
48
|
+
*/
|
|
49
|
+
export declare function recordAutopilotFixOutcome(capsPath: string, repo: string, kind: "dco" | "merge-conflict" | "format", outcome: "ok" | "failed-other", evidence: string, at?: string): void;
|
|
50
|
+
export declare function loadAutopilotState(path: string): AutopilotState;
|
|
51
|
+
export declare function saveAutopilotState(path: string, state: AutopilotState): void;
|
|
52
|
+
export interface AutopilotFix {
|
|
53
|
+
kind: "dco" | "merge-conflict" | "format";
|
|
54
|
+
itemId: string;
|
|
55
|
+
oldHead: string;
|
|
56
|
+
newHead: string;
|
|
57
|
+
detail: string;
|
|
58
|
+
}
|
|
59
|
+
export interface AutopilotPlan {
|
|
60
|
+
itemId: string;
|
|
61
|
+
kind: FeedbackKind;
|
|
62
|
+
title: string;
|
|
63
|
+
plan: string;
|
|
64
|
+
}
|
|
65
|
+
export interface ReplyDraft {
|
|
66
|
+
pr: string;
|
|
67
|
+
itemId: string;
|
|
68
|
+
kind: FeedbackKind;
|
|
69
|
+
draft: string;
|
|
70
|
+
queuedAt: string;
|
|
71
|
+
queuedReason: string;
|
|
72
|
+
}
|
|
73
|
+
export interface PrReport {
|
|
74
|
+
pr: AutopilotPull | null;
|
|
75
|
+
url: string;
|
|
76
|
+
skipped?: string;
|
|
77
|
+
items: FeedbackItem[];
|
|
78
|
+
fixes: AutopilotFix[];
|
|
79
|
+
plans: AutopilotPlan[];
|
|
80
|
+
replyDrafts: number;
|
|
81
|
+
error?: string;
|
|
82
|
+
}
|
|
83
|
+
export interface AutopilotSummary {
|
|
84
|
+
prsScanned: number;
|
|
85
|
+
prsSkippedBlocked: number;
|
|
86
|
+
prsErrored: number;
|
|
87
|
+
itemsFound: number;
|
|
88
|
+
fixesPushed: number;
|
|
89
|
+
plans: number;
|
|
90
|
+
repliesQueued: number;
|
|
91
|
+
itemsByKind: Record<FeedbackKind, number>;
|
|
92
|
+
botVerdicts: {
|
|
93
|
+
real: number;
|
|
94
|
+
falsePositive: number;
|
|
95
|
+
needsHuman: number;
|
|
96
|
+
};
|
|
97
|
+
ciIntroduced: number;
|
|
98
|
+
ciPreExistingOrExternal: number;
|
|
99
|
+
}
|
|
100
|
+
export interface AutopilotRun {
|
|
101
|
+
perPr: PrReport[];
|
|
102
|
+
summary: AutopilotSummary;
|
|
103
|
+
json: Record<string, unknown>;
|
|
104
|
+
markdown: string;
|
|
105
|
+
}
|
|
106
|
+
export interface AutopilotDeps {
|
|
107
|
+
discoverPrs?: (author: string) => Promise<CensusPr[]>;
|
|
108
|
+
fetchPr?: (owner: string, repo: string, number: number) => Promise<AutopilotPull>;
|
|
109
|
+
triageCi?: (prUrl: string) => Promise<{
|
|
110
|
+
checks: ClassifiedCheck[];
|
|
111
|
+
}>;
|
|
112
|
+
fetchReviewSources?: (owner: string, repo: string, number: number, since: {
|
|
113
|
+
reviewId: number;
|
|
114
|
+
reviewCommentId: number;
|
|
115
|
+
issueCommentId: number;
|
|
116
|
+
}) => Promise<{
|
|
117
|
+
reviews: Array<{
|
|
118
|
+
id: number;
|
|
119
|
+
state?: string;
|
|
120
|
+
body?: string;
|
|
121
|
+
user?: {
|
|
122
|
+
login?: string;
|
|
123
|
+
};
|
|
124
|
+
}>;
|
|
125
|
+
reviewComments: Array<{
|
|
126
|
+
id: number;
|
|
127
|
+
body?: string;
|
|
128
|
+
user?: {
|
|
129
|
+
login?: string;
|
|
130
|
+
};
|
|
131
|
+
path?: string;
|
|
132
|
+
line?: number;
|
|
133
|
+
diff_hunk?: string;
|
|
134
|
+
pull_request_review_id?: number;
|
|
135
|
+
}>;
|
|
136
|
+
issueComments: Array<{
|
|
137
|
+
id: number;
|
|
138
|
+
body?: string;
|
|
139
|
+
user?: {
|
|
140
|
+
login?: string;
|
|
141
|
+
};
|
|
142
|
+
}>;
|
|
143
|
+
maxIds: {
|
|
144
|
+
reviewId: number;
|
|
145
|
+
reviewCommentId: number;
|
|
146
|
+
issueCommentId: number;
|
|
147
|
+
};
|
|
148
|
+
}>;
|
|
149
|
+
fetchFileAtHead?: (owner: string, repo: string, headSha: string, path: string) => Promise<string | null>;
|
|
150
|
+
/** PR commit SHAs, oldest first. Defaults to the pulls/commits endpoint. */
|
|
151
|
+
fetchPrCommits?: (owner: string, repo: string, number: number) => Promise<string[]>;
|
|
152
|
+
gitOps?: GitOps;
|
|
153
|
+
gitRunner?: GitRunner;
|
|
154
|
+
runInDir?: (cwd: string, label: string) => Promise<void>;
|
|
155
|
+
cloneFork?: (cloneUrl: string, branch: string, dest: string) => Promise<void>;
|
|
156
|
+
expectedForkOwner?: string;
|
|
157
|
+
}
|
|
158
|
+
export interface AutopilotRunOptions {
|
|
159
|
+
author?: string;
|
|
160
|
+
prs?: string[];
|
|
161
|
+
maxPrs?: number;
|
|
162
|
+
apply?: boolean;
|
|
163
|
+
maxFixes?: number;
|
|
164
|
+
statePath?: string;
|
|
165
|
+
repliesPath?: string;
|
|
166
|
+
capabilitiesPath?: string;
|
|
167
|
+
concurrency?: number;
|
|
168
|
+
deps?: AutopilotDeps;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Draft reply for a fixed item: what a human would post on the PR.
|
|
172
|
+
* Autopilot never posts; these queue in autopilot-replies.json.
|
|
173
|
+
*/
|
|
174
|
+
export declare function draftForFix(item: FeedbackItem, fix: AutopilotFix): string;
|
|
175
|
+
/** Draft reply for a rebutted bot finding. */
|
|
176
|
+
export declare function draftForRebuttal(item: FeedbackItem): string;
|
|
177
|
+
/**
|
|
178
|
+
* Mechanical merge of the current base into the fork branch. Local git does
|
|
179
|
+
* the merge computation in a temp clone; the merged tree is committed
|
|
180
|
+
* through the Git Data API and the branch ref fast-forwarded. Returns
|
|
181
|
+
* ok:false with a NEEDS_HUMAN reason whenever a conflict is not mechanical.
|
|
182
|
+
*/
|
|
183
|
+
export declare function attemptMergeFix(pr: AutopilotPull, deps: AutopilotDeps, expectedForkOwner: string): Promise<{
|
|
184
|
+
ok: true;
|
|
185
|
+
oldHead: string;
|
|
186
|
+
newHead: string;
|
|
187
|
+
detail: string;
|
|
188
|
+
} | {
|
|
189
|
+
ok: false;
|
|
190
|
+
reason: string;
|
|
191
|
+
}>;
|
|
192
|
+
/** Persist reply drafts to the replies file, appending to any existing queue. */
|
|
193
|
+
export declare function saveReplyDrafts(path: string, drafts: ReplyDraft[]): number;
|
|
194
|
+
export declare function autopilotMarkdown(run: {
|
|
195
|
+
perPr: PrReport[];
|
|
196
|
+
summary: AutopilotSummary;
|
|
197
|
+
}): string;
|
|
198
|
+
export declare function autopilotJson(run: {
|
|
199
|
+
perPr: PrReport[];
|
|
200
|
+
summary: AutopilotSummary;
|
|
201
|
+
}): Record<string, unknown>;
|
|
202
|
+
/**
|
|
203
|
+
* Run Autopilot. Report-only by default; --apply performs fixes.
|
|
204
|
+
* Returns the run with markdown and json renderings.
|
|
205
|
+
*/
|
|
206
|
+
export declare function runAutopilot(opts: AutopilotRunOptions): Promise<AutopilotRun>;
|
|
207
|
+
export { authorKindOf as autopilotAuthorKindOf };
|