opencode-magi 0.0.0-dev-20260520163847 → 0.0.0-dev-20260520165420
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.
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
function parseDiffPath(value) {
|
|
2
|
+
if (value === "/dev/null")
|
|
3
|
+
return undefined;
|
|
4
|
+
let path = value;
|
|
5
|
+
if (path.startsWith('"') && path.endsWith('"')) {
|
|
6
|
+
try {
|
|
7
|
+
path = JSON.parse(path);
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
path = path.slice(1, -1);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return path.startsWith("b/") ? path.slice(2) : path;
|
|
14
|
+
}
|
|
15
|
+
function addTargetLine(targets, path, line) {
|
|
16
|
+
const lines = targets.get(path) ?? new Set();
|
|
17
|
+
lines.add(line);
|
|
18
|
+
targets.set(path, lines);
|
|
19
|
+
}
|
|
20
|
+
export function parseRightSideDiffTargets(diff) {
|
|
21
|
+
const targets = new Map();
|
|
22
|
+
let currentPath;
|
|
23
|
+
let rightLine;
|
|
24
|
+
for (const line of diff.split("\n")) {
|
|
25
|
+
if (line.startsWith("+++ ")) {
|
|
26
|
+
currentPath = parseDiffPath(line.slice(4));
|
|
27
|
+
rightLine = undefined;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (line.startsWith("@@ ")) {
|
|
31
|
+
const match = line.match(/\+(\d+)(?:,\d+)?/);
|
|
32
|
+
rightLine = match ? Number(match[1]) : undefined;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (!currentPath || rightLine == null)
|
|
36
|
+
continue;
|
|
37
|
+
if (line.startsWith("+") || line.startsWith(" ")) {
|
|
38
|
+
addTargetLine(targets, currentPath, rightLine);
|
|
39
|
+
rightLine += 1;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (line.startsWith("-"))
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
return targets;
|
|
46
|
+
}
|
|
47
|
+
function assertPositiveInteger(value, name) {
|
|
48
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
49
|
+
throw new Error(`${name} must be a positive integer`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function validateInlineCommentTargets(findings, targets, label = "findings") {
|
|
53
|
+
for (const [index, finding] of findings.entries()) {
|
|
54
|
+
const name = `${label}[${index}]`;
|
|
55
|
+
assertPositiveInteger(finding.line, `${name}.line`);
|
|
56
|
+
if (finding.startLine != null) {
|
|
57
|
+
assertPositiveInteger(finding.startLine, `${name}.startLine`);
|
|
58
|
+
if (finding.startLine > finding.line) {
|
|
59
|
+
throw new Error(`${name}.startLine must be before or equal to line`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const lines = targets.get(finding.path);
|
|
63
|
+
if (!lines) {
|
|
64
|
+
throw new Error(`${name} targets ${finding.path}:${finding.line}, but path is not in the PR diff`);
|
|
65
|
+
}
|
|
66
|
+
const startLine = finding.startLine ?? finding.line;
|
|
67
|
+
for (let line = startLine; line <= finding.line; line += 1) {
|
|
68
|
+
if (!lines.has(line)) {
|
|
69
|
+
throw new Error(`${name} targets ${finding.path}:${line}, but line is not in a right-side PR diff hunk`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { prRunOutputDir } from "../config/output";
|
|
4
|
-
import { closePullRequest, configureGitIdentity, fetchMergeQueueRequirement, fetchPullRequest, fetchUnresolvedThreads, mergePullRequest, postApproval, postChangesRequested, postCloseComment, postReply, pushHead, removeWorktree, resolveThread, waitForMergeQueue, } from "../github/commands";
|
|
4
|
+
import { closePullRequest, configureGitIdentity, fetchMergeQueueRequirement, fetchPullRequest, fetchUnresolvedThreads, mergePullRequest, postApproval, postChangesRequested, postCloseComment, postReply, pushHead, removeWorktree, resolveThread, shellQuote, waitForMergeQueue, } from "../github/commands";
|
|
5
5
|
import { composeEditPrompt, composeRereviewCloseReconsiderationPrompt, composeRereviewPrompt, } from "../prompts/compose";
|
|
6
6
|
import { parseEditOutput, parseRereviewCloseReconsiderationOutput, parseRereviewOutput, } from "../prompts/output";
|
|
7
7
|
import { throwIfAborted, withAbortSignal } from "./abort";
|
|
8
8
|
import { waitForChecksWithClassification } from "./ci";
|
|
9
|
+
import { parseRightSideDiffTargets, validateInlineCommentTargets, } from "./inline-comments";
|
|
9
10
|
import { closeMinorityReviewers, mergeVerdictForPolicy } from "./majority";
|
|
10
11
|
import { runModelWithRepair } from "./model";
|
|
11
12
|
import { mapPool } from "./pool";
|
|
@@ -155,10 +156,16 @@ async function postRereviewOutput(input, reviewerKey, output) {
|
|
|
155
156
|
}
|
|
156
157
|
return replies[0] ?? "";
|
|
157
158
|
}
|
|
159
|
+
function parseRereviewOutputWithInlineTargets(text, targets) {
|
|
160
|
+
const output = parseRereviewOutput(text);
|
|
161
|
+
validateInlineCommentTargets(output.newFindings, targets, "newFindings");
|
|
162
|
+
return output;
|
|
163
|
+
}
|
|
158
164
|
async function runRereview(input, worktreePath, previousHeadSha, cycle, sessionIds, ciFailureContext, options = {}) {
|
|
159
165
|
throwIfAborted(input.signal);
|
|
160
166
|
const meta = await fetchPullRequest(input.exec, input.repository, input.pr);
|
|
161
167
|
const headSha = options.dryRunHeadSha ?? meta.headRefOid;
|
|
168
|
+
const inlineCommentTargets = parseRightSideDiffTargets(await input.exec(`git diff --no-ext-diff --unified=3 ${shellQuote(meta.baseRefOid)} ${shellQuote(headSha)}`, { cwd: worktreePath }));
|
|
162
169
|
const artifactDir = outputDir(input);
|
|
163
170
|
let entries = await mapPool(input.repository.agents.reviewers, input.repository.concurrency.reviewers, async (reviewer) => {
|
|
164
171
|
throwIfAborted(input.signal);
|
|
@@ -213,7 +220,7 @@ async function runRereview(input, worktreePath, previousHeadSha, cycle, sessionI
|
|
|
213
220
|
}
|
|
214
221
|
},
|
|
215
222
|
options: reviewer.options,
|
|
216
|
-
parse:
|
|
223
|
+
parse: (text) => parseRereviewOutputWithInlineTargets(text, inlineCommentTargets),
|
|
217
224
|
permission: reviewer.permission,
|
|
218
225
|
prompt,
|
|
219
226
|
repairAttempts: input.config.output?.repairAttempts ?? 3,
|
|
@@ -296,7 +303,11 @@ async function runRereview(input, worktreePath, previousHeadSha, cycle, sessionI
|
|
|
296
303
|
}
|
|
297
304
|
},
|
|
298
305
|
options: reviewer.options,
|
|
299
|
-
parse:
|
|
306
|
+
parse: (text) => {
|
|
307
|
+
const output = parseRereviewCloseReconsiderationOutput(text);
|
|
308
|
+
validateInlineCommentTargets(output.newFindings, inlineCommentTargets, "newFindings");
|
|
309
|
+
return output;
|
|
310
|
+
},
|
|
300
311
|
permission: reviewer.permission,
|
|
301
312
|
prompt,
|
|
302
313
|
repairAttempts: input.config.output?.repairAttempts ?? 3,
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { createWorktree, fetchPullRequest, fetchPullRequestCommits, fetchPullRequestReviews, fetchUnresolvedThreads, closePullRequest, mergePullRequest, postApproval, postChangesRequested, postCloseComment, postReply, removeWorktree, resolveThread, } from "../github/commands";
|
|
3
|
+
import { createWorktree, fetchPullRequest, fetchPullRequestCommits, fetchPullRequestReviews, fetchUnresolvedThreads, closePullRequest, mergePullRequest, postApproval, postChangesRequested, postCloseComment, postReply, removeWorktree, resolveThread, shellQuote, } from "../github/commands";
|
|
4
4
|
import { composeFindingValidationPrompt, composeCloseReconsiderationPrompt, composeRereviewPrompt, composeReviewPrompt, } from "../prompts/compose";
|
|
5
5
|
import { prRunOutputDir } from "../config/output";
|
|
6
6
|
import { worktreeBaseDir } from "../config/worktree";
|
|
7
7
|
import { parseCloseReconsiderationOutput, parseFindingValidationOutput, parseRereviewOutput, parseReviewOutput, } from "../prompts/output";
|
|
8
8
|
import { throwIfAborted, withAbortSignal } from "./abort";
|
|
9
9
|
import { waitForChecksWithClassification } from "./ci";
|
|
10
|
+
import { parseRightSideDiffTargets, validateInlineCommentTargets, } from "./inline-comments";
|
|
10
11
|
import { applyFindingValidation, reviewFindingTargets, validateFindingVotes, } from "./findings";
|
|
11
12
|
import { closeMinorityReviewers, mergeVerdictForPolicy, } from "./majority";
|
|
12
13
|
import { runModelWithRepair } from "./model";
|
|
@@ -123,6 +124,16 @@ function previousReviewText(review) {
|
|
|
123
124
|
submittedAt: review.submittedAt,
|
|
124
125
|
}, null, 2);
|
|
125
126
|
}
|
|
127
|
+
function parseReviewOutputWithInlineTargets(text, targets) {
|
|
128
|
+
const output = parseReviewOutput(text);
|
|
129
|
+
validateInlineCommentTargets(output.findings, targets);
|
|
130
|
+
return output;
|
|
131
|
+
}
|
|
132
|
+
function parseRereviewOutputWithInlineTargets(text, targets) {
|
|
133
|
+
const output = parseRereviewOutput(text);
|
|
134
|
+
validateInlineCommentTargets(output.newFindings, targets, "newFindings");
|
|
135
|
+
return output;
|
|
136
|
+
}
|
|
126
137
|
function reviewOutputFromState(review) {
|
|
127
138
|
const verdict = reviewStateToVerdict(review.state);
|
|
128
139
|
return verdict === "CLOSE"
|
|
@@ -336,7 +347,11 @@ async function runCloseReconsideration(input) {
|
|
|
336
347
|
}
|
|
337
348
|
},
|
|
338
349
|
options: reviewer.options,
|
|
339
|
-
parse:
|
|
350
|
+
parse: (text) => {
|
|
351
|
+
const output = parseCloseReconsiderationOutput(text);
|
|
352
|
+
validateInlineCommentTargets(output.findings, input.inlineCommentTargets);
|
|
353
|
+
return output;
|
|
354
|
+
},
|
|
340
355
|
permission: reviewer.permission,
|
|
341
356
|
prompt,
|
|
342
357
|
repairAttempts: input.reviewInput.config.output?.repairAttempts ?? 3,
|
|
@@ -519,6 +534,7 @@ export async function runReview(input) {
|
|
|
519
534
|
return [];
|
|
520
535
|
return [{ assignment, reviewer }];
|
|
521
536
|
});
|
|
537
|
+
const inlineCommentTargets = parseRightSideDiffTargets(await exec(`git diff --no-ext-diff --unified=3 ${shellQuote(meta.baseRefOid)} ${shellQuote(meta.headRefOid)}`, { cwd: worktreePath }));
|
|
522
538
|
for (const reviewer of input.repository.agents.reviewers) {
|
|
523
539
|
const assignment = mode.assignments.get(reviewer.account);
|
|
524
540
|
if (assignment?.type !== "skip")
|
|
@@ -583,7 +599,7 @@ export async function runReview(input) {
|
|
|
583
599
|
}
|
|
584
600
|
},
|
|
585
601
|
options: reviewer.options,
|
|
586
|
-
parse:
|
|
602
|
+
parse: (text) => parseRereviewOutputWithInlineTargets(text, inlineCommentTargets),
|
|
587
603
|
permission: reviewer.permission,
|
|
588
604
|
prompt,
|
|
589
605
|
repairAttempts: input.config.output?.repairAttempts ?? 3,
|
|
@@ -648,7 +664,7 @@ export async function runReview(input) {
|
|
|
648
664
|
}
|
|
649
665
|
},
|
|
650
666
|
options: reviewer.options,
|
|
651
|
-
parse:
|
|
667
|
+
parse: (text) => parseReviewOutputWithInlineTargets(text, inlineCommentTargets),
|
|
652
668
|
permission: reviewer.permission,
|
|
653
669
|
prompt,
|
|
654
670
|
repairAttempts: input.config.output?.repairAttempts ?? 3,
|
|
@@ -708,6 +724,7 @@ export async function runReview(input) {
|
|
|
708
724
|
});
|
|
709
725
|
entries = await runCloseReconsideration({
|
|
710
726
|
entries: [...entries, ...skippedCloseEntries],
|
|
727
|
+
inlineCommentTargets,
|
|
711
728
|
meta,
|
|
712
729
|
outputDir,
|
|
713
730
|
reviewInput: { ...input, exec },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-magi",
|
|
3
|
-
"version": "0.0.0-dev-
|
|
3
|
+
"version": "0.0.0-dev-20260520165420",
|
|
4
4
|
"description": "Multi-agent PR review and merge orchestration plugin for OpenCode.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Hirotomo Yamada <hirotomo.yamada@avap.co.jp>",
|