opencode-magi 0.0.0-dev-20260630051947 → 0.0.0-dev-20260630052239
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/magi.js +4 -8
- package/dist/tools/merge/action.js +2 -2
- package/dist/tools/merge/editor.js +3 -3
- package/dist/tools/merge/merge.js +1 -1
- package/dist/tools/review/action.js +4 -4
- package/dist/tools/review/check.js +6 -6
- package/dist/tools/review/review.js +5 -1
- package/dist/tools/review/reviewer.js +9 -9
- package/dist/utils/function.js +1 -1
- package/package.json +1 -1
package/dist/magi.js
CHANGED
|
@@ -200,7 +200,7 @@ export class Magi {
|
|
|
200
200
|
const prev = await this.getState(dir);
|
|
201
201
|
const state = merge(prev, next);
|
|
202
202
|
const values = [this.createStateFile(state)];
|
|
203
|
-
if (next.text)
|
|
203
|
+
if (!state.sync && next.text)
|
|
204
204
|
values.push(this.notify(prev.sessionId, next.text));
|
|
205
205
|
await Promise.all(values);
|
|
206
206
|
return state;
|
|
@@ -267,10 +267,8 @@ export class Magi {
|
|
|
267
267
|
const path = this.getPath(join(dir, number.toString(), id));
|
|
268
268
|
try {
|
|
269
269
|
await mkdir(dirname(path), { recursive: true });
|
|
270
|
-
await this.exec(command("git", "worktree", "add", quote(path)), {
|
|
271
|
-
|
|
272
|
-
});
|
|
273
|
-
await this.exec(command("gh", "pr", "checkout", number), {
|
|
270
|
+
await this.exec(command("git", "worktree", "add", "--detach", quote(path)), { signal });
|
|
271
|
+
await this.exec(command("gh", "pr", "checkout", number, "--detach"), {
|
|
274
272
|
cwd: path,
|
|
275
273
|
signal,
|
|
276
274
|
});
|
|
@@ -278,9 +276,7 @@ export class Magi {
|
|
|
278
276
|
cwd: path,
|
|
279
277
|
signal,
|
|
280
278
|
});
|
|
281
|
-
|
|
282
|
-
throw new Error("Failed to determine worktree branch");
|
|
283
|
-
return { branch, path };
|
|
279
|
+
return { branch: branch || undefined, path };
|
|
284
280
|
}
|
|
285
281
|
catch (e) {
|
|
286
282
|
try {
|
|
@@ -9,10 +9,10 @@ export async function editCycles(callback) {
|
|
|
9
9
|
throw error;
|
|
10
10
|
return verdict;
|
|
11
11
|
}, {
|
|
12
|
-
error: (e, count) => {
|
|
12
|
+
error: async (e, count) => {
|
|
13
13
|
if (e !== error)
|
|
14
14
|
throw e;
|
|
15
|
-
this.
|
|
15
|
+
await this.notify(`Attempt ${count} failed to edit cycles for ${this.getLink()}. Retrying...`);
|
|
16
16
|
},
|
|
17
17
|
retries: this.config.merge.maxThreadResolutionCycles,
|
|
18
18
|
});
|
|
@@ -40,7 +40,7 @@ export async function edit() {
|
|
|
40
40
|
}
|
|
41
41
|
return parsed;
|
|
42
42
|
}, {
|
|
43
|
-
error: (_, count) => this.
|
|
43
|
+
error: (_, count) => this.notify(`Attempt ${count} failed to edit ${this.getLink()}. Retrying...`),
|
|
44
44
|
retries: this.config.output.repairAttempts,
|
|
45
45
|
});
|
|
46
46
|
if (!output)
|
|
@@ -49,7 +49,7 @@ export async function edit() {
|
|
|
49
49
|
editor: { outputs: [...(this.state.editor.outputs ?? []), output] },
|
|
50
50
|
text: `Finished editing ${this.getLink()}.`,
|
|
51
51
|
});
|
|
52
|
-
await this.
|
|
52
|
+
await this.notify(filterEmpty([
|
|
53
53
|
`Editor ${output.mode === "EDITED" ? "edited" : "replied to"} ${this.getLink()}.`,
|
|
54
54
|
output.commitSha
|
|
55
55
|
? `Commit: ${output.commitSha}${output.commitMessage ? ` ${output.commitMessage}` : ""}`
|
|
@@ -135,7 +135,7 @@ export async function resolveConflict() {
|
|
|
135
135
|
responses: [],
|
|
136
136
|
};
|
|
137
137
|
}, {
|
|
138
|
-
error: (_, count) => this.
|
|
138
|
+
error: (_, count) => this.notify(`Attempt ${count} failed to resolve conflicts for ${this.getLink()}. Retrying...`),
|
|
139
139
|
retries: this.config.output.repairAttempts,
|
|
140
140
|
});
|
|
141
141
|
if (!output)
|
|
@@ -27,8 +27,8 @@ export class Merge extends Review {
|
|
|
27
27
|
permissions: config.merge.editor.permissions,
|
|
28
28
|
};
|
|
29
29
|
const state = await magi.createState(join(config.review.output, number.toString()), {
|
|
30
|
+
...options,
|
|
30
31
|
command: "merge",
|
|
31
|
-
dryRun: options.dryRun,
|
|
32
32
|
editor,
|
|
33
33
|
operator,
|
|
34
34
|
pr: { number, url },
|
|
@@ -45,7 +45,7 @@ export async function postReviews() {
|
|
|
45
45
|
.map((finding) => ({ ...finding, id }));
|
|
46
46
|
});
|
|
47
47
|
if (!findings.length) {
|
|
48
|
-
this.
|
|
48
|
+
await this.notify(`Finished posting reviews for ${this.getLink()}.`);
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
51
|
params.comments = findings.map(({ body, id, line, path, startLine }) => ({
|
|
@@ -63,7 +63,7 @@ export async function postReviews() {
|
|
|
63
63
|
if (this.state.pr.verdict !== "APPROVED") {
|
|
64
64
|
if (!this.state.operator?.sessionId)
|
|
65
65
|
throw new MagiError("blocked", "Reporter session ID not found.");
|
|
66
|
-
await this.
|
|
66
|
+
await this.notify(`Generating comment for ${this.getLink()} by operator.`);
|
|
67
67
|
const contents = JSON.stringify(reviewers.flatMap(([, { outputs }]) => {
|
|
68
68
|
const output = outputs?.at(-1);
|
|
69
69
|
if (!output || output.verdict === "APPROVED")
|
|
@@ -88,12 +88,12 @@ export async function postReviews() {
|
|
|
88
88
|
throw new Error("Invalid output for operator.");
|
|
89
89
|
return parsed.comment;
|
|
90
90
|
}, {
|
|
91
|
-
error: (_, count) => this.
|
|
91
|
+
error: (_, count) => this.notify(`Attempt ${count} failed to post comment for ${this.getLink()} by operator. Retrying...`),
|
|
92
92
|
retries: this.config.output.repairAttempts,
|
|
93
93
|
});
|
|
94
94
|
if (!output)
|
|
95
95
|
throw new MagiError("blocked", "Invalid output for operator.");
|
|
96
|
-
await this.
|
|
96
|
+
await this.notify(`Generated comment for ${this.getLink()} by operator.`);
|
|
97
97
|
params.body = output;
|
|
98
98
|
}
|
|
99
99
|
params.body = [
|
|
@@ -115,7 +115,7 @@ export async function classifyChecks() {
|
|
|
115
115
|
await Promise.all(Object.entries(this.state.reviewers ?? {}).map(([id, { sessionId }]) => worker.run(async () => {
|
|
116
116
|
if (!sessionId)
|
|
117
117
|
throw new Error(`No session ID found for reviewer ${id}.`);
|
|
118
|
-
await this.
|
|
118
|
+
await this.notify(`Classifying CI checks for ${this.getLink()} with reviewer ${id}.`);
|
|
119
119
|
const output = await retry(async (count) => {
|
|
120
120
|
const raw = await this.magi.promptSession(sessionId, count === 1 ? taskMessage : repairMessage);
|
|
121
121
|
const parsed = prompt.parse(raw);
|
|
@@ -123,7 +123,7 @@ export async function classifyChecks() {
|
|
|
123
123
|
throw new Error(`Invalid output for reviewer ${id}.`);
|
|
124
124
|
return parsed;
|
|
125
125
|
}, {
|
|
126
|
-
error: (_, count) => this.
|
|
126
|
+
error: (_, count) => this.notify(`Attempt ${count} failed to classify CI checks for ${this.getLink()} with reviewer ${id}. Retrying...`),
|
|
127
127
|
retries: this.config.output.repairAttempts,
|
|
128
128
|
});
|
|
129
129
|
if (!output)
|
|
@@ -151,7 +151,7 @@ export async function classifyChecks() {
|
|
|
151
151
|
.filter(([, { scope }]) => !scope)
|
|
152
152
|
.map(([id, { comment }]) => `- ${id}: ${comment}`),
|
|
153
153
|
};
|
|
154
|
-
await this.
|
|
154
|
+
await this.notify(filterEmpty([
|
|
155
155
|
scope
|
|
156
156
|
? `Check ${name} for ${this.getLink()} was classified as in scope by majority vote.`
|
|
157
157
|
: `Check ${name} for ${this.getLink()} was classified as out of scope by majority vote. Rerunning it.`,
|
|
@@ -192,7 +192,7 @@ export async function rerunChecks() {
|
|
|
192
192
|
}
|
|
193
193
|
else {
|
|
194
194
|
await retry(async () => {
|
|
195
|
-
await this.
|
|
195
|
+
await this.notify(`Rerunning checks ${label} for ${this.getLink()}.`);
|
|
196
196
|
await Promise.all(checks.failed
|
|
197
197
|
.filter(({ scope }) => !scope)
|
|
198
198
|
.map(async ({ id }) => rerunCheck.call(this, id)));
|
|
@@ -212,7 +212,7 @@ export async function rerunChecks() {
|
|
|
212
212
|
label = failedChecks.map(({ name }) => name).join(", ");
|
|
213
213
|
throw new Error(`Checks ${label} for ${this.getLink()} still failed.`);
|
|
214
214
|
}, {
|
|
215
|
-
error: (_, count) => this.
|
|
215
|
+
error: (_, count) => this.notify(`Attempt ${count} failed to rerun checks ${label} for ${this.getLink()}. Retrying...`),
|
|
216
216
|
retries: this.config.review.checks.retryFailedJobs,
|
|
217
217
|
});
|
|
218
218
|
}
|
|
@@ -227,7 +227,7 @@ export async function rerunChecks() {
|
|
|
227
227
|
: undefined,
|
|
228
228
|
]).join("\n");
|
|
229
229
|
if (message)
|
|
230
|
-
await this.
|
|
230
|
+
await this.notify(message);
|
|
231
231
|
this.state = await this.magi.updateState(this.state.output, {
|
|
232
232
|
pr: { checks },
|
|
233
233
|
text: `Finished rerunning checks for ${this.getLink()}.`,
|
|
@@ -40,8 +40,8 @@ export class Review {
|
|
|
40
40
|
? config.review.reviewers.find(({ id }) => id === config.review.operator)
|
|
41
41
|
: config.review.reviewers[Math.abs(number) % config.review.reviewers.length];
|
|
42
42
|
const state = await magi.createState(join(config.review.output, number.toString()), {
|
|
43
|
+
...options,
|
|
43
44
|
command: "review",
|
|
44
|
-
dryRun: options.dryRun,
|
|
45
45
|
operator,
|
|
46
46
|
pr: { number, url },
|
|
47
47
|
repo: quote(`${config.github.owner}/${config.github.repo}`),
|
|
@@ -64,6 +64,10 @@ export class Review {
|
|
|
64
64
|
postReviews = postReviews;
|
|
65
65
|
automate = automate;
|
|
66
66
|
createReport = createReport;
|
|
67
|
+
async notify(text) {
|
|
68
|
+
if (!this.state.sync)
|
|
69
|
+
await this.magi.notify(this.state.sessionId, text);
|
|
70
|
+
}
|
|
67
71
|
async cleanup() {
|
|
68
72
|
if (this.state.worktree?.path)
|
|
69
73
|
await this.magi.deleteWorktree(this.state.worktree.path);
|
|
@@ -22,7 +22,7 @@ export async function review() {
|
|
|
22
22
|
throw new MagiError("blocked", "Reviewers not found.");
|
|
23
23
|
const { outputs, review, sessionId, status } = this.state.reviewers[id];
|
|
24
24
|
if (status === "skip") {
|
|
25
|
-
this.
|
|
25
|
+
await this.notify(`Skipping review for ${this.getLink()} with reviewer ${id}.`);
|
|
26
26
|
return [id, {}];
|
|
27
27
|
}
|
|
28
28
|
else {
|
|
@@ -32,7 +32,7 @@ export async function review() {
|
|
|
32
32
|
throw new MagiError("blocked", `Missing previous review commit for reviewer ${id}.`);
|
|
33
33
|
const rereview = status !== "initial";
|
|
34
34
|
const label = rereview ? "rereview" : "review";
|
|
35
|
-
await this.
|
|
35
|
+
await this.notify(`Running ${label} for ${this.getLink()} with reviewer ${id}.`);
|
|
36
36
|
const sha = rereview
|
|
37
37
|
? (review?.commit_id ?? this.state.pr.metadata.base.sha)
|
|
38
38
|
: this.state.pr.metadata.base.sha;
|
|
@@ -104,7 +104,7 @@ export async function review() {
|
|
|
104
104
|
validateInlineCommentTargets(status, parsed, inlineCommentTargets);
|
|
105
105
|
return parsed;
|
|
106
106
|
}, {
|
|
107
|
-
error: (_, count) => this.
|
|
107
|
+
error: (_, count) => this.notify(`Attempt ${count} failed to ${label} for ${this.getLink()} with reviewer ${id}. Retrying...`),
|
|
108
108
|
retries: this.config.output.repairAttempts,
|
|
109
109
|
});
|
|
110
110
|
if (!output)
|
|
@@ -184,7 +184,7 @@ export async function reconsiderClose() {
|
|
|
184
184
|
repo: this.config.github.repo,
|
|
185
185
|
});
|
|
186
186
|
const repairMessage = await prompt.repair();
|
|
187
|
-
await this.
|
|
187
|
+
await this.notify(`Reconsidering close verdict for ${this.getLink()} with reviewer ${id}.`);
|
|
188
188
|
const output = await retry(async (count) => {
|
|
189
189
|
const raw = await this.magi.promptSession(sessionId, count === 1 ? taskMessage : repairMessage);
|
|
190
190
|
const parsed = prompt.parse(raw);
|
|
@@ -193,7 +193,7 @@ export async function reconsiderClose() {
|
|
|
193
193
|
validateInlineCommentTargets("initial", parsed, inlineCommentTargets);
|
|
194
194
|
return parsed;
|
|
195
195
|
}, {
|
|
196
|
-
error: (_, count) => this.
|
|
196
|
+
error: (_, count) => this.notify(`Attempt ${count} failed to reconsider close verdict for ${this.getLink()} with reviewer ${id}. Retrying...`),
|
|
197
197
|
retries: this.config.output.repairAttempts,
|
|
198
198
|
});
|
|
199
199
|
if (!output)
|
|
@@ -240,7 +240,7 @@ async function collectAcceptedFindings(findings) {
|
|
|
240
240
|
const { sessionId } = this.state.reviewers[id];
|
|
241
241
|
if (!sessionId)
|
|
242
242
|
throw new MagiError("blocked", `No session ID found for reviewer ${id}.`);
|
|
243
|
-
await this.
|
|
243
|
+
await this.notify(`Validating review findings for ${this.getLink()} with reviewer ${id}.`);
|
|
244
244
|
const targetFindings = findings.filter((target) => target.reviewer !== id);
|
|
245
245
|
const expectedKeys = new Set(targetFindings.map(({ index, reviewer }) => `${reviewer}:${index}`));
|
|
246
246
|
const taskMessage = await prompt.create(this.config.review.prompts?.findingValidation, [
|
|
@@ -273,7 +273,7 @@ async function collectAcceptedFindings(findings) {
|
|
|
273
273
|
throw new Error(`Missing finding vote: ${key}.`);
|
|
274
274
|
return parsed;
|
|
275
275
|
}, {
|
|
276
|
-
error: (_, count) => this.
|
|
276
|
+
error: (_, count) => this.notify(`Attempt ${count} failed to validate review findings for ${this.getLink()} with reviewer ${id}. Retrying...`),
|
|
277
277
|
retries: this.config.output.repairAttempts,
|
|
278
278
|
});
|
|
279
279
|
if (!output)
|
|
@@ -298,7 +298,7 @@ async function collectAcceptedFindings(findings) {
|
|
|
298
298
|
.map(({ validator, vote }) => `- ${validator}: ${vote.comment}`);
|
|
299
299
|
if (agrees >= threshold)
|
|
300
300
|
accepted.add(key);
|
|
301
|
-
await this.
|
|
301
|
+
await this.notify(filterEmpty([
|
|
302
302
|
`Finding ${reviewer} #${index + 1} for ${this.getLink()} was ${agrees >= threshold ? "accepted" : "rejected"} by majority vote.`,
|
|
303
303
|
`Finding: ${finding.path}:${finding.line}\n${finding.body}`,
|
|
304
304
|
acceptedComments.length
|
|
@@ -338,7 +338,7 @@ async function notifyVerdictChanges(prev, next, reason) {
|
|
|
338
338
|
const nextVerdict = reviewer.outputs?.at(-1)?.verdict;
|
|
339
339
|
if (!prevVerdict || !nextVerdict || prevVerdict === nextVerdict)
|
|
340
340
|
return;
|
|
341
|
-
await this.
|
|
341
|
+
await this.notify(`Reviewer ${id} verdict changed from ${prevVerdict} to ${nextVerdict} for ${this.getLink()} ${reason}.`);
|
|
342
342
|
}));
|
|
343
343
|
}
|
|
344
344
|
function validateInlineCommentTargets(status, output, inlineCommentTargets) {
|
package/dist/utils/function.js
CHANGED
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-20260630052239",
|
|
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>",
|