opencode-magi 0.0.0-dev-20260520163717 → 0.0.0-dev-20260520163847
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.
|
@@ -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-20260520163847",
|
|
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>",
|