opencode-magi 0.0.0-dev-20260520163717 → 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 },
|
|
@@ -4,7 +4,7 @@ import { issueRunOutputDir } from "../config/output";
|
|
|
4
4
|
import { worktreeBaseDir } from "../config/worktree";
|
|
5
5
|
import { assignIssue, closeIssue, closePullRequest, configureGitIdentity, createPullRequest, fetchIssue, fetchIssueComments, fetchRelatedPullRequests, postIssueComment, pushHead, removeIssueLabels, removeWorktree, searchDuplicateIssues, shellQuote, updateIssueComment, } from "../github/commands";
|
|
6
6
|
import { composeTriageBugPrompt, composeTriageActionPrompt, composeTriageCommentClassificationPrompt, composeTriageCommentPrompt, composeTriageCreatePrPrompt, composeTriageDuplicatePrompt, composeTriageExistingPrPrompt, composeTriageFeaturePrompt, composeTriageKindPrompt, composeTriageQuestionPrompt, composeTriageReconsiderPrompt, } from "../prompts/compose";
|
|
7
|
-
import {
|
|
7
|
+
import { parseTriageActionOutput, parseTriageBinaryOutput, parseTriageCommentClassificationOutput, parseTriageCreatePrOutput, parseTriageDuplicateOutput, parseTriageExistingPrOutput, parseTriageFinalOutput, parseTriageKindOutput, } from "../prompts/output";
|
|
8
8
|
import { aggregateStringMajority, majorityThreshold } from "./majority";
|
|
9
9
|
import { runModelText, runModelWithRepair } from "./model";
|
|
10
10
|
const MARKER_PREFIX = "opencode-magi:triage";
|
|
@@ -65,6 +65,10 @@ function labelsContain(labels, targets) {
|
|
|
65
65
|
const set = new Set(labels.map((label) => label.toLowerCase()));
|
|
66
66
|
return targets.some((target) => set.has(target.toLowerCase()));
|
|
67
67
|
}
|
|
68
|
+
function existingClearLabels(issue, labels) {
|
|
69
|
+
const existing = new Set(issue.labels.map((label) => label.toLowerCase()));
|
|
70
|
+
return labels.filter((label) => existing.has(label.toLowerCase()));
|
|
71
|
+
}
|
|
68
72
|
export function resolveIssueKind(issue, repository) {
|
|
69
73
|
const triage = repository.triage;
|
|
70
74
|
if (!triage)
|
|
@@ -251,6 +255,11 @@ function markerPr(marker) {
|
|
|
251
255
|
const pr = Number(marker.pr);
|
|
252
256
|
return Number.isInteger(pr) && pr > 0 ? pr : undefined;
|
|
253
257
|
}
|
|
258
|
+
function pullRequestNumberFromUrl(url) {
|
|
259
|
+
const match = url.match(/\/pull\/(\d+)(?:\D|$)/);
|
|
260
|
+
const number = match ? Number(match[1]) : undefined;
|
|
261
|
+
return number && Number.isInteger(number) ? number : undefined;
|
|
262
|
+
}
|
|
254
263
|
export function mentionAllowed(comment, repository) {
|
|
255
264
|
const safety = repository.triage?.safety;
|
|
256
265
|
if (!safety)
|
|
@@ -329,6 +338,30 @@ function actionPlan(input) {
|
|
|
329
338
|
postComment: true,
|
|
330
339
|
};
|
|
331
340
|
}
|
|
341
|
+
function previousAutomationPlan(input) {
|
|
342
|
+
const base = actionPlan({ result: input.result, triage: input.triage });
|
|
343
|
+
const clearLabels = base.clearLabels &&
|
|
344
|
+
existingClearLabels(input.issue, input.triage.automation.clear).length > 0;
|
|
345
|
+
const closeIssue = input.marker.action === "CLOSE" &&
|
|
346
|
+
base.closeIssue &&
|
|
347
|
+
input.issue.state === "OPEN";
|
|
348
|
+
const createPr = input.marker.action === "PR" &&
|
|
349
|
+
base.createPr &&
|
|
350
|
+
!markerPr(input.marker) &&
|
|
351
|
+
!input.relationship.relatedPullRequests.length;
|
|
352
|
+
if (!clearLabels && !closeIssue && !createPr)
|
|
353
|
+
return undefined;
|
|
354
|
+
const action = closeIssue ? "CLOSE" : createPr ? "PR" : "CLEAR_ONLY";
|
|
355
|
+
return {
|
|
356
|
+
...base,
|
|
357
|
+
action,
|
|
358
|
+
allowedActions: [action],
|
|
359
|
+
clearLabels,
|
|
360
|
+
closeIssue,
|
|
361
|
+
createPr,
|
|
362
|
+
postComment: false,
|
|
363
|
+
};
|
|
364
|
+
}
|
|
332
365
|
async function runActionPrompt(input) {
|
|
333
366
|
const agent = input.input.repository.agents.triage?.[0];
|
|
334
367
|
if (!agent)
|
|
@@ -454,7 +487,7 @@ async function persistProcessedMarker(input) {
|
|
|
454
487
|
action: input.marker.action ?? input.marker.result ?? "ASK",
|
|
455
488
|
checkpoint: markerCheckpoint(input.marker),
|
|
456
489
|
issue: input.issue.number,
|
|
457
|
-
pr: markerPr(input.marker),
|
|
490
|
+
pr: input.pr ?? markerPr(input.marker),
|
|
458
491
|
processed: input.processed,
|
|
459
492
|
result: input.marker.result ?? "ASK",
|
|
460
493
|
});
|
|
@@ -471,7 +504,7 @@ async function finishWithResult(input) {
|
|
|
471
504
|
const triage = input.input.repository.triage;
|
|
472
505
|
if (!triage)
|
|
473
506
|
throw new Error("triage configuration is required");
|
|
474
|
-
const plan = actionPlan({ result: input.result, triage });
|
|
507
|
+
const plan = input.plan ?? actionPlan({ result: input.result, triage });
|
|
475
508
|
await runActionPrompt({
|
|
476
509
|
context: input.context,
|
|
477
510
|
input: input.input,
|
|
@@ -502,6 +535,12 @@ async function finishWithResult(input) {
|
|
|
502
535
|
repository: input.input.repository,
|
|
503
536
|
});
|
|
504
537
|
}
|
|
538
|
+
if (plan.clearLabels) {
|
|
539
|
+
const clearLabels = existingClearLabels(input.issue, triage.automation.clear);
|
|
540
|
+
if (clearLabels.length) {
|
|
541
|
+
await removeIssueLabels(input.input.exec, input.input.repository, input.issue.number, clearLabels, triage.account ?? "");
|
|
542
|
+
}
|
|
543
|
+
}
|
|
505
544
|
if (plan.closeIssue) {
|
|
506
545
|
const closedPrs = [];
|
|
507
546
|
for (const pr of input.relationship.relatedPullRequests.filter((pr) => pr.state === "OPEN")) {
|
|
@@ -522,8 +561,18 @@ async function finishWithResult(input) {
|
|
|
522
561
|
if (prUrl)
|
|
523
562
|
await writeJson(join(input.outputDir, "pr.json"), { url: prUrl });
|
|
524
563
|
}
|
|
525
|
-
if (
|
|
526
|
-
await
|
|
564
|
+
if (input.previousMarker && prUrl) {
|
|
565
|
+
await persistProcessedMarker({
|
|
566
|
+
account: triage.account ?? "",
|
|
567
|
+
comments: input.relationship.comments,
|
|
568
|
+
exec: input.input.exec,
|
|
569
|
+
issue: input.issue,
|
|
570
|
+
marker: input.previousMarker,
|
|
571
|
+
outputDir: input.outputDir,
|
|
572
|
+
pr: pullRequestNumberFromUrl(prUrl),
|
|
573
|
+
processed: input.processed ?? input.previousMarker.processed,
|
|
574
|
+
repository: input.input.repository,
|
|
575
|
+
});
|
|
527
576
|
}
|
|
528
577
|
}
|
|
529
578
|
const report = [
|
|
@@ -589,7 +638,7 @@ async function createImplementationPr(input) {
|
|
|
589
638
|
client: input.input.client,
|
|
590
639
|
model: creator.model,
|
|
591
640
|
options: creator.options,
|
|
592
|
-
parse:
|
|
641
|
+
parse: parseTriageCreatePrOutput,
|
|
593
642
|
permission: creator.permission,
|
|
594
643
|
prompt,
|
|
595
644
|
repairAttempts: 3,
|
|
@@ -650,6 +699,26 @@ export async function runTriage(input) {
|
|
|
650
699
|
if (relationship.previousMarker) {
|
|
651
700
|
if (!relationship.mentionReplies.length) {
|
|
652
701
|
const result = finalResultFromMarker(relationship.previousMarker);
|
|
702
|
+
const plan = previousAutomationPlan({
|
|
703
|
+
issue,
|
|
704
|
+
marker: relationship.previousMarker,
|
|
705
|
+
relationship,
|
|
706
|
+
result,
|
|
707
|
+
triage,
|
|
708
|
+
});
|
|
709
|
+
if (plan) {
|
|
710
|
+
return finishWithResult({
|
|
711
|
+
context,
|
|
712
|
+
input,
|
|
713
|
+
issue,
|
|
714
|
+
outputDir,
|
|
715
|
+
plan,
|
|
716
|
+
previousMarker: relationship.previousMarker,
|
|
717
|
+
processed,
|
|
718
|
+
relationship,
|
|
719
|
+
result,
|
|
720
|
+
});
|
|
721
|
+
}
|
|
653
722
|
const report = `Magi triage skipped #${issue.number} because no eligible mention replies were found for reconsideration.`;
|
|
654
723
|
await writeFile(join(outputDir, "report.md"), `${report}\n`);
|
|
655
724
|
return { issue: issue.number, outputDir, report, result };
|
|
@@ -746,6 +815,10 @@ export async function runTriage(input) {
|
|
|
746
815
|
outputDir,
|
|
747
816
|
repository: input.repository,
|
|
748
817
|
});
|
|
818
|
+
const clearLabels = existingClearLabels(issue, triage.automation.clear);
|
|
819
|
+
if (clearLabels.length) {
|
|
820
|
+
await removeIssueLabels(input.exec, input.repository, issue.number, clearLabels, triage.account);
|
|
821
|
+
}
|
|
749
822
|
const closedPrs = [];
|
|
750
823
|
for (const pr of relationship.relatedPullRequests.filter((pr) => pr.state === "OPEN")) {
|
|
751
824
|
await closePullRequest(input.exec, input.repository, pr.number, triage.account);
|
|
@@ -754,7 +827,6 @@ export async function runTriage(input) {
|
|
|
754
827
|
if (closedPrs.length)
|
|
755
828
|
await writeJson(join(outputDir, "closed-prs.json"), closedPrs);
|
|
756
829
|
await closeIssue(input.exec, input.repository, issue.number, triage.account);
|
|
757
|
-
await removeIssueLabels(input.exec, input.repository, issue.number, triage.automation.clear, triage.account);
|
|
758
830
|
}
|
|
759
831
|
const report = `Magi triage closed #${issue.number} because a related PR was merged.`;
|
|
760
832
|
await writeFile(join(outputDir, "report.md"), `${report}\n`);
|
package/dist/prompts/output.js
CHANGED
|
@@ -305,7 +305,7 @@ export function parseCiClassificationOutput(text) {
|
|
|
305
305
|
}),
|
|
306
306
|
};
|
|
307
307
|
}
|
|
308
|
-
|
|
308
|
+
function parseEditOutputWithOptions(text, options) {
|
|
309
309
|
const data = extractJson(text);
|
|
310
310
|
if (!data || typeof data !== "object")
|
|
311
311
|
throw new Error("edit output must be an object");
|
|
@@ -323,7 +323,7 @@ export function parseEditOutput(text) {
|
|
|
323
323
|
commentId: requireNumber(value.commentId, `responses[${index}].commentId`),
|
|
324
324
|
};
|
|
325
325
|
});
|
|
326
|
-
if (!responses.length)
|
|
326
|
+
if (options.requireResponses && !responses.length)
|
|
327
327
|
throw new Error("responses must not be empty");
|
|
328
328
|
if (data.mode === "EDITED") {
|
|
329
329
|
if (!filesTouched.length)
|
|
@@ -351,3 +351,9 @@ export function parseEditOutput(text) {
|
|
|
351
351
|
responses,
|
|
352
352
|
};
|
|
353
353
|
}
|
|
354
|
+
export function parseEditOutput(text) {
|
|
355
|
+
return parseEditOutputWithOptions(text, { requireResponses: true });
|
|
356
|
+
}
|
|
357
|
+
export function parseTriageCreatePrOutput(text) {
|
|
358
|
+
return parseEditOutputWithOptions(text, { requireResponses: false });
|
|
359
|
+
}
|
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>",
|