opencode-magi 0.0.0-dev-20260522104950 → 0.0.0-dev-20260522105602
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/github/commands.js
CHANGED
|
@@ -330,10 +330,13 @@ export async function removeIssueLabels(exec, repository, issue, labels, account
|
|
|
330
330
|
return removed;
|
|
331
331
|
}
|
|
332
332
|
export async function fetchPullRequestReviews(exec, repository, pr) {
|
|
333
|
-
const query = `query($owner: String!, $repo: String!, $pr: Int!) { repository(owner: $owner, name: $repo) { pullRequest(number: $pr) { reviews(first: 100) { nodes { author { login } submittedAt state body commit { oid } } } } } }`;
|
|
333
|
+
const query = `query($owner: String!, $repo: String!, $pr: Int!) { repository(owner: $owner, name: $repo) { pullRequest(number: $pr) { reviews(first: 100) { nodes { author { login } submittedAt state body commit { oid } comments(first: 100) { nodes { body path line startLine } } } } } } }`;
|
|
334
334
|
const raw = await exec(`gh api${ghHostOption(repository)} graphql -f query=${shellQuote(query)} -F owner=${shellQuote(repository.github.owner)} -F repo=${shellQuote(repository.github.repo)} -F pr=${pr}`);
|
|
335
335
|
const data = JSON.parse(raw);
|
|
336
|
-
return data.data.repository.pullRequest.reviews.nodes
|
|
336
|
+
return data.data.repository.pullRequest.reviews.nodes.map((review) => ({
|
|
337
|
+
...review,
|
|
338
|
+
comments: review.comments?.nodes ?? [],
|
|
339
|
+
}));
|
|
337
340
|
}
|
|
338
341
|
export async function fetchPullRequestCommits(exec, repository, pr) {
|
|
339
342
|
const query = `query($owner: String!, $repo: String!, $pr: Int!) { repository(owner: $owner, name: $repo) { pullRequest(number: $pr) { commits(first: 100) { nodes { commit { oid committedDate parents { totalCount } } } } } } }`;
|
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import { majorityThreshold } from "./majority";
|
|
2
|
+
function validationFindings(output) {
|
|
3
|
+
if ("findings" in output)
|
|
4
|
+
return output.findings;
|
|
5
|
+
return output.newFindings.map((finding) => ({
|
|
6
|
+
fix: "Please address this before merging.",
|
|
7
|
+
issue: finding.body,
|
|
8
|
+
line: finding.line,
|
|
9
|
+
path: finding.path,
|
|
10
|
+
startLine: finding.startLine,
|
|
11
|
+
}));
|
|
12
|
+
}
|
|
2
13
|
export function reviewFindingTargets(outputs) {
|
|
3
14
|
return Object.entries(outputs).flatMap(([reviewer, output]) => {
|
|
4
15
|
if (output.verdict !== "CHANGES_REQUESTED")
|
|
5
16
|
return [];
|
|
6
|
-
return output.
|
|
17
|
+
return validationFindings(output).map((finding, findingIndex) => ({
|
|
7
18
|
finding,
|
|
8
19
|
findingIndex,
|
|
9
20
|
reviewer,
|
|
@@ -41,7 +52,9 @@ export function applyFindingValidation(input) {
|
|
|
41
52
|
next[reviewer] = output;
|
|
42
53
|
continue;
|
|
43
54
|
}
|
|
44
|
-
const
|
|
55
|
+
const keptIndexes = new Set();
|
|
56
|
+
const findings = validationFindings(output);
|
|
57
|
+
findings.forEach((finding, findingIndex) => {
|
|
45
58
|
let agrees = 1;
|
|
46
59
|
for (const validator of input.reviewerKeys) {
|
|
47
60
|
if (validator === reviewer)
|
|
@@ -53,14 +66,23 @@ export function applyFindingValidation(input) {
|
|
|
53
66
|
const target = { finding, findingIndex, reviewer };
|
|
54
67
|
if (agrees >= threshold) {
|
|
55
68
|
kept.push(target);
|
|
56
|
-
|
|
69
|
+
keptIndexes.add(findingIndex);
|
|
70
|
+
return;
|
|
57
71
|
}
|
|
58
72
|
discarded.push(target);
|
|
59
|
-
return false;
|
|
60
73
|
});
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
74
|
+
if ("findings" in output) {
|
|
75
|
+
const keptFindings = output.findings.filter((_finding, index) => keptIndexes.has(index));
|
|
76
|
+
next[reviewer] = keptFindings.length
|
|
77
|
+
? { ...output, findings: keptFindings }
|
|
78
|
+
: { findings: [], verdict: "MERGE" };
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const newFindings = output.newFindings.filter((_finding, index) => keptIndexes.has(index));
|
|
82
|
+
next[reviewer] =
|
|
83
|
+
newFindings.length || output.followUps.length
|
|
84
|
+
? { ...output, newFindings }
|
|
85
|
+
: { ...output, newFindings, verdict: "MERGE" };
|
|
64
86
|
}
|
|
65
87
|
return { outputs: next, summary: { discarded, kept } };
|
|
66
88
|
}
|
|
@@ -135,6 +135,9 @@ function parseRereviewOutputWithInlineTargets(text, targets) {
|
|
|
135
135
|
validateInlineCommentTargets(output.newFindings, targets, "newFindings");
|
|
136
136
|
return output;
|
|
137
137
|
}
|
|
138
|
+
export async function inlineCommentTargetsForDiff(input) {
|
|
139
|
+
return parseRightSideDiffTargets(await input.exec(`git diff --no-ext-diff --unified=3 ${shellQuote(input.fromSha)}...${shellQuote(input.toSha)}`, { cwd: input.worktreePath }));
|
|
140
|
+
}
|
|
138
141
|
function parsePostedFindingLocation(location) {
|
|
139
142
|
const range = /^(.*):(\d+)-(\d+)$/.exec(location);
|
|
140
143
|
if (range) {
|
|
@@ -182,10 +185,42 @@ function reviewFindingsFromBody(body) {
|
|
|
182
185
|
}
|
|
183
186
|
return { findings };
|
|
184
187
|
}
|
|
188
|
+
function parsePostedFindingComment(body) {
|
|
189
|
+
const match = /^\*\*Issue:\*\*\s*([\s\S]*?)\s*\r?\n\r?\n\*\*Fix:\*\*\s*([\s\S]+?)\s*$/.exec(body);
|
|
190
|
+
if (!match)
|
|
191
|
+
return undefined;
|
|
192
|
+
return {
|
|
193
|
+
fix: match[2]?.trim() || "Please address this before merging.",
|
|
194
|
+
issue: match[1]?.trim() || "Review finding.",
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function reviewFindingsFromComments(comments) {
|
|
198
|
+
return {
|
|
199
|
+
findings: (comments ?? []).flatMap((comment) => {
|
|
200
|
+
if (comment.line == null)
|
|
201
|
+
return [];
|
|
202
|
+
const parsed = parsePostedFindingComment(comment.body);
|
|
203
|
+
if (!parsed)
|
|
204
|
+
return [];
|
|
205
|
+
return [
|
|
206
|
+
{
|
|
207
|
+
...parsed,
|
|
208
|
+
line: comment.line,
|
|
209
|
+
path: comment.path,
|
|
210
|
+
startLine: comment.startLine ?? undefined,
|
|
211
|
+
},
|
|
212
|
+
];
|
|
213
|
+
}),
|
|
214
|
+
};
|
|
215
|
+
}
|
|
185
216
|
export function reviewOutputFromState(review) {
|
|
186
217
|
const verdict = reviewStateToVerdict(review.state);
|
|
187
|
-
if (verdict === "CHANGES_REQUESTED")
|
|
218
|
+
if (verdict === "CHANGES_REQUESTED") {
|
|
219
|
+
const fromComments = reviewFindingsFromComments(review.comments);
|
|
220
|
+
if (fromComments.findings.length)
|
|
221
|
+
return { ...fromComments, verdict };
|
|
188
222
|
return { ...reviewFindingsFromBody(review.body), verdict };
|
|
223
|
+
}
|
|
189
224
|
return verdict === "CLOSE"
|
|
190
225
|
? {
|
|
191
226
|
findings: [],
|
|
@@ -230,8 +265,8 @@ function isReviewOutput(output) {
|
|
|
230
265
|
return "findings" in output;
|
|
231
266
|
}
|
|
232
267
|
async function runFindingValidation(input) {
|
|
233
|
-
const
|
|
234
|
-
const targets = reviewFindingTargets(
|
|
268
|
+
const outputs = Object.fromEntries(input.entries.map((entry) => [entry.key, entry.value]));
|
|
269
|
+
const targets = reviewFindingTargets(outputs);
|
|
235
270
|
if (!targets.length) {
|
|
236
271
|
return {
|
|
237
272
|
outputs: Object.fromEntries(input.entries.map((entry) => [entry.key, entry.value])),
|
|
@@ -316,7 +351,7 @@ async function runFindingValidation(input) {
|
|
|
316
351
|
return [reviewer.key, result.value];
|
|
317
352
|
}, { signal: input.reviewInput.signal }));
|
|
318
353
|
const filtered = applyFindingValidation({
|
|
319
|
-
outputs
|
|
354
|
+
outputs,
|
|
320
355
|
reviewerKeys: input.reviewInput.repository.agents.reviewers.map((reviewer) => reviewer.key),
|
|
321
356
|
validations,
|
|
322
357
|
});
|
|
@@ -324,7 +359,7 @@ async function runFindingValidation(input) {
|
|
|
324
359
|
await input.reviewInput.onProgress?.({
|
|
325
360
|
discarded: filtered.summary.discarded.length,
|
|
326
361
|
kept: filtered.summary.kept.length,
|
|
327
|
-
reviewersChangedToMerge: Object.entries(
|
|
362
|
+
reviewersChangedToMerge: Object.entries(outputs)
|
|
328
363
|
.filter(([reviewer, output]) => {
|
|
329
364
|
return (output.verdict === "CHANGES_REQUESTED" &&
|
|
330
365
|
filtered.outputs[reviewer]?.verdict === "MERGE");
|
|
@@ -407,7 +442,7 @@ async function runCloseReconsideration(input) {
|
|
|
407
442
|
parentSessionId: input.reviewInput.parentSessionId,
|
|
408
443
|
parse: (text) => {
|
|
409
444
|
const output = parseCloseReconsiderationOutput(text);
|
|
410
|
-
validateInlineCommentTargets(output.findings,
|
|
445
|
+
validateInlineCommentTargets(output.findings, entry.inlineCommentTargets);
|
|
411
446
|
return output;
|
|
412
447
|
},
|
|
413
448
|
permission: reviewer.permission,
|
|
@@ -436,6 +471,7 @@ async function runCloseReconsideration(input) {
|
|
|
436
471
|
});
|
|
437
472
|
input.sessionIds[reviewer.key] = result.sessionId;
|
|
438
473
|
return {
|
|
474
|
+
inlineCommentTargets: entry.inlineCommentTargets,
|
|
439
475
|
key: entry.key,
|
|
440
476
|
raw: result.raw,
|
|
441
477
|
sessionId: result.sessionId,
|
|
@@ -602,7 +638,12 @@ export async function runReview(input) {
|
|
|
602
638
|
return [];
|
|
603
639
|
return [{ assignment, reviewer }];
|
|
604
640
|
});
|
|
605
|
-
const
|
|
641
|
+
const initialInlineCommentTargets = await inlineCommentTargetsForDiff({
|
|
642
|
+
exec,
|
|
643
|
+
fromSha: meta.baseRefOid,
|
|
644
|
+
toSha: meta.headRefOid,
|
|
645
|
+
worktreePath,
|
|
646
|
+
});
|
|
606
647
|
for (const reviewer of input.repository.agents.reviewers) {
|
|
607
648
|
const assignment = mode.assignments.get(reviewer.account);
|
|
608
649
|
if (assignment?.type !== "skip")
|
|
@@ -622,6 +663,12 @@ export async function runReview(input) {
|
|
|
622
663
|
const previous = assignment.review;
|
|
623
664
|
if (!previous.commit?.oid)
|
|
624
665
|
throw new Error(`Missing previous review commit for ${reviewer.account}`);
|
|
666
|
+
const inlineCommentTargets = await inlineCommentTargetsForDiff({
|
|
667
|
+
exec,
|
|
668
|
+
fromSha: previous.commit.oid,
|
|
669
|
+
toSha: meta.headRefOid,
|
|
670
|
+
worktreePath,
|
|
671
|
+
});
|
|
625
672
|
const unresolved = unresolvedThreadsByAccount.get(reviewer.account) ??
|
|
626
673
|
(await fetchUnresolvedThreads(exec, input.repository, input.pr, reviewer.account));
|
|
627
674
|
const prompt = await composeRereviewPrompt({
|
|
@@ -688,6 +735,7 @@ export async function runReview(input) {
|
|
|
688
735
|
verdict: result.value.verdict,
|
|
689
736
|
});
|
|
690
737
|
return {
|
|
738
|
+
inlineCommentTargets,
|
|
691
739
|
key: reviewer.key,
|
|
692
740
|
raw: result.raw,
|
|
693
741
|
sessionId: result.sessionId,
|
|
@@ -736,7 +784,7 @@ export async function runReview(input) {
|
|
|
736
784
|
},
|
|
737
785
|
options: reviewer.options,
|
|
738
786
|
parentSessionId: input.parentSessionId,
|
|
739
|
-
parse: (text) => parseReviewOutputWithInlineTargets(text,
|
|
787
|
+
parse: (text) => parseReviewOutputWithInlineTargets(text, initialInlineCommentTargets),
|
|
740
788
|
permission: reviewer.permission,
|
|
741
789
|
prompt,
|
|
742
790
|
repairAttempts: input.config.output?.repairAttempts ?? 3,
|
|
@@ -755,6 +803,7 @@ export async function runReview(input) {
|
|
|
755
803
|
verdict: result.value.verdict,
|
|
756
804
|
});
|
|
757
805
|
return {
|
|
806
|
+
inlineCommentTargets: initialInlineCommentTargets,
|
|
758
807
|
key: reviewer.key,
|
|
759
808
|
raw: result.raw,
|
|
760
809
|
sessionId: result.sessionId,
|
|
@@ -788,6 +837,7 @@ export async function runReview(input) {
|
|
|
788
837
|
return [
|
|
789
838
|
{
|
|
790
839
|
key: reviewer.key,
|
|
840
|
+
inlineCommentTargets: initialInlineCommentTargets,
|
|
791
841
|
raw: assignment.review.body ?? "",
|
|
792
842
|
sessionId: "",
|
|
793
843
|
value: reviewOutputFromState(assignment.review),
|
|
@@ -796,7 +846,6 @@ export async function runReview(input) {
|
|
|
796
846
|
});
|
|
797
847
|
entries = await runCloseReconsideration({
|
|
798
848
|
entries: [...entries, ...skippedCloseEntries],
|
|
799
|
-
inlineCommentTargets,
|
|
800
849
|
meta,
|
|
801
850
|
outputDir,
|
|
802
851
|
reviewContext,
|
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-20260522105602",
|
|
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>",
|