opencode-magi 0.0.0-dev-20260525205150 → 0.0.0-dev-20260525213616
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/README.md +10 -12
- package/dist/config/resolve.js +3 -1
- package/dist/config/validate.js +21 -11
- package/package.json +1 -1
- package/schema.json +1 -1
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ OpenCode Magi recreates the review cycle humans already run on GitHub: multiple
|
|
|
17
17
|
- Multi-agent reviews with an odd-number majority of 3 or more reviewers.
|
|
18
18
|
- Optional unanimous approval policy for merge automation when every reviewer must approve before a PR is merged.
|
|
19
19
|
- Finding-level voting before posting change requests, so only findings accepted by reviewer majority are submitted.
|
|
20
|
-
-
|
|
20
|
+
- Single-account identity mode by default, where one GitHub account posts consensus-backed review and triage results for multiple logical agents, plus multi-account mode for setups that need separate GitHub identities.
|
|
21
21
|
- Re-review support for edited PRs: fixed threads are resolved, satisfied reviewers approve, and remaining issues are posted as additional comments.
|
|
22
22
|
- Optional merge and close automation where an editor agent responds on behalf of the author, fixes changes it agrees with, pushes commits when needed, and repeats the reviewer/editor cycle until the PR can be approved, queued, merged, or closed.
|
|
23
23
|
- Per-agent OpenCode permissions for reviewer, CI classifier, and editor child sessions.
|
|
@@ -61,6 +61,7 @@ Add the following content to the configuration file.
|
|
|
61
61
|
```json
|
|
62
62
|
{
|
|
63
63
|
"$schema": "https://raw.githubusercontent.com/magi-ai/opencode-magi/main/schema.json",
|
|
64
|
+
"account": "your-account",
|
|
64
65
|
"agents": {
|
|
65
66
|
"refs": {
|
|
66
67
|
"account-1": {
|
|
@@ -75,7 +76,6 @@ Add the following content to the configuration file.
|
|
|
75
76
|
}
|
|
76
77
|
},
|
|
77
78
|
"review": {
|
|
78
|
-
"account": "your-account",
|
|
79
79
|
"reviewers": [
|
|
80
80
|
{ "ref": "account-1" },
|
|
81
81
|
{ "ref": "account-2" },
|
|
@@ -85,14 +85,14 @@ Add the following content to the configuration file.
|
|
|
85
85
|
}
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
By default, `
|
|
88
|
+
By default, `mode` is `"single"`. Magi uses one top-level `account` to post reviewer- and triage-originated GitHub mutations while still running multiple logical agents and preserving majority voting, finding validation, and close reconsideration. The account must be authenticated with `gh auth token --user <account>`.
|
|
89
89
|
|
|
90
|
-
For team setups that need separate
|
|
90
|
+
For advanced team setups that need GitHub to see separate review or triage identities, set top-level `mode: "multi"` and configure unique accounts for each reviewer or triage voter.
|
|
91
91
|
|
|
92
92
|
```json
|
|
93
93
|
{
|
|
94
|
+
"mode": "multi",
|
|
94
95
|
"review": {
|
|
95
|
-
"mode": "multi",
|
|
96
96
|
"reviewers": [
|
|
97
97
|
{ "id": "general", "model": "openai/gpt-5.5", "account": "account-1" },
|
|
98
98
|
{
|
|
@@ -121,6 +121,7 @@ Add the following content to the configuration file.
|
|
|
121
121
|
```json
|
|
122
122
|
{
|
|
123
123
|
"$schema": "https://raw.githubusercontent.com/magi-ai/opencode-magi/main/schema.json",
|
|
124
|
+
"account": "your-account",
|
|
124
125
|
"github": {
|
|
125
126
|
"owner": "your-owner",
|
|
126
127
|
"repo": "your-repo"
|
|
@@ -128,16 +129,13 @@ Add the following content to the configuration file.
|
|
|
128
129
|
"agents": {
|
|
129
130
|
"refs": {
|
|
130
131
|
"account-1": {
|
|
131
|
-
"model": "openai/gpt-5.5"
|
|
132
|
-
"account": "account-1"
|
|
132
|
+
"model": "openai/gpt-5.5"
|
|
133
133
|
},
|
|
134
134
|
"account-2": {
|
|
135
|
-
"model": "anthropic/claude-opus-4-7"
|
|
136
|
-
"account": "account-2"
|
|
135
|
+
"model": "anthropic/claude-opus-4-7"
|
|
137
136
|
},
|
|
138
137
|
"account-3": {
|
|
139
|
-
"model": "opencode/kimi-k2-6"
|
|
140
|
-
"account": "account-3"
|
|
138
|
+
"model": "opencode/kimi-k2-6"
|
|
141
139
|
},
|
|
142
140
|
"account-4": {
|
|
143
141
|
"model": "openai/gpt-5.5",
|
|
@@ -182,7 +180,7 @@ Entries with `ref` are expanded from `agents.refs`. Fields set alongside `ref` o
|
|
|
182
180
|
}
|
|
183
181
|
```
|
|
184
182
|
|
|
185
|
-
After `refs` are expanded, `
|
|
183
|
+
After `refs` are expanded, top-level `account` is the GitHub account used for reviewer- and triage-originated posts and mutations in `single` mode. In `multi` mode, `review.reviewers[].account` and `triage.voters[].account` are used instead and must be unique within their agent lists. `merge.editor.account` is still used by `/magi:merge` to push fixes, close PRs, and merge PRs.
|
|
186
184
|
|
|
187
185
|
#### Validate config
|
|
188
186
|
|
package/dist/config/resolve.js
CHANGED
|
@@ -114,6 +114,7 @@ export function resolveAgents(config) {
|
|
|
114
114
|
const editor = config.merge?.editor;
|
|
115
115
|
const creator = config.triage?.creator;
|
|
116
116
|
const singleReviewAccount = config.review && config.mode !== "multi" ? config.account : undefined;
|
|
117
|
+
const singleTriageAccount = config.triage && config.mode !== "multi" ? config.account : undefined;
|
|
117
118
|
return {
|
|
118
119
|
editor: editor
|
|
119
120
|
? {
|
|
@@ -132,6 +133,7 @@ export function resolveAgents(config) {
|
|
|
132
133
|
})),
|
|
133
134
|
triage: (config.triage?.voters ?? []).map((agent, index) => ({
|
|
134
135
|
...agent,
|
|
136
|
+
account: singleTriageAccount ?? agent.account ?? "",
|
|
135
137
|
key: triageAgentKey(agent, index),
|
|
136
138
|
index,
|
|
137
139
|
model: normalizedModel(agent.model),
|
|
@@ -140,7 +142,7 @@ export function resolveAgents(config) {
|
|
|
140
142
|
triageCreator: creator
|
|
141
143
|
? {
|
|
142
144
|
...creator,
|
|
143
|
-
account: creator.account ?? "",
|
|
145
|
+
account: singleTriageAccount ?? creator.account ?? "",
|
|
144
146
|
model: normalizedModel(creator.model),
|
|
145
147
|
permission: resolveTriageCreatorPermission(agents, creator),
|
|
146
148
|
}
|
package/dist/config/validate.js
CHANGED
|
@@ -421,7 +421,7 @@ function validateReviewerList(reviewers, path, errors, catalog, mode = "single")
|
|
|
421
421
|
}
|
|
422
422
|
});
|
|
423
423
|
}
|
|
424
|
-
function validateTriageAgentList(voters, path, errors, catalog) {
|
|
424
|
+
function validateTriageAgentList(voters, path, errors, catalog, mode = "single") {
|
|
425
425
|
if (voters == null)
|
|
426
426
|
return;
|
|
427
427
|
if (!Array.isArray(voters)) {
|
|
@@ -441,8 +441,10 @@ function validateTriageAgentList(voters, path, errors, catalog) {
|
|
|
441
441
|
if (!agent.model)
|
|
442
442
|
errors.push(`${path}[${index}].model is required`);
|
|
443
443
|
validateAndNormalizeModel(agent, `${path}[${index}].model`, errors, catalog);
|
|
444
|
-
if (!agent.account)
|
|
444
|
+
if (mode === "multi" && !agent.account)
|
|
445
445
|
errors.push(`${path}[${index}].account is required`);
|
|
446
|
+
if (mode === "single" && agent.account)
|
|
447
|
+
errors.push(`${path}[${index}].account is not supported in single mode`);
|
|
446
448
|
validateString(agent.account, `${path}[${index}].account`, errors);
|
|
447
449
|
validateString(agent.persona, `${path}[${index}].persona`, errors);
|
|
448
450
|
validatePermissionConfig(agent.permissions, `${path}[${index}].permissions`, errors);
|
|
@@ -480,14 +482,14 @@ function validateReviewIdentity(config, errors) {
|
|
|
480
482
|
errors.push("account is required when mode is single");
|
|
481
483
|
}
|
|
482
484
|
}
|
|
483
|
-
function validateResolvedTriageAgents(agents, path, errors) {
|
|
485
|
+
function validateResolvedTriageAgents(agents, path, errors, mode = "single") {
|
|
484
486
|
const keys = new Set();
|
|
485
487
|
const accounts = new Set();
|
|
486
488
|
for (const agent of agents) {
|
|
487
489
|
if (keys.has(agent.key))
|
|
488
490
|
errors.push(`${path} has duplicate agent key: ${agent.key}`);
|
|
489
491
|
keys.add(agent.key);
|
|
490
|
-
if (accounts.has(agent.account))
|
|
492
|
+
if (mode === "multi" && accounts.has(agent.account))
|
|
491
493
|
errors.push(`${path} has duplicate agent account: ${agent.account}`);
|
|
492
494
|
accounts.add(agent.account);
|
|
493
495
|
}
|
|
@@ -531,7 +533,7 @@ function validateEditor(editor, path, errors, catalog) {
|
|
|
531
533
|
}
|
|
532
534
|
}
|
|
533
535
|
}
|
|
534
|
-
function validateTriageCreator(creator, path, errors, catalog) {
|
|
536
|
+
function validateTriageCreator(creator, path, errors, catalog, mode = "single") {
|
|
535
537
|
if (!creator)
|
|
536
538
|
return;
|
|
537
539
|
if (!isPlainObject(creator)) {
|
|
@@ -541,6 +543,8 @@ function validateTriageCreator(creator, path, errors, catalog) {
|
|
|
541
543
|
validateKnownKeys(creator, path, TRIAGE_CREATOR_KEYS, errors);
|
|
542
544
|
if (!creator.model)
|
|
543
545
|
errors.push(`${path}.model is required`);
|
|
546
|
+
if (mode === "single" && creator.account)
|
|
547
|
+
errors.push(`${path}.account is not supported in single mode`);
|
|
544
548
|
validateString(creator.account, `${path}.account`, errors);
|
|
545
549
|
validateAndNormalizeModel(creator, `${path}.model`, errors, catalog);
|
|
546
550
|
validateString(creator.persona, `${path}.persona`, errors);
|
|
@@ -829,6 +833,7 @@ function validatePromptObject(prompts, path, keys, errors) {
|
|
|
829
833
|
}
|
|
830
834
|
function validateTriage(config, errors, options) {
|
|
831
835
|
const triage = config.triage;
|
|
836
|
+
const mode = reviewMode(config);
|
|
832
837
|
if (!triage)
|
|
833
838
|
return;
|
|
834
839
|
if (!isPlainObject(triage)) {
|
|
@@ -843,7 +848,7 @@ function validateTriage(config, errors, options) {
|
|
|
843
848
|
const safety = triage.safety;
|
|
844
849
|
if (!triage.voters)
|
|
845
850
|
errors.push("triage.voters is required");
|
|
846
|
-
validateTriageAgentList(triage.voters, "triage.voters", errors, options.modelCatalog);
|
|
851
|
+
validateTriageAgentList(triage.voters, "triage.voters", errors, options.modelCatalog, mode);
|
|
847
852
|
if (Array.isArray(triage.voters)) {
|
|
848
853
|
const resolvedTriageAgents = triage.voters.map((agent, index) => ({
|
|
849
854
|
account: agent && typeof agent === "object" && typeof agent.account === "string"
|
|
@@ -851,17 +856,21 @@ function validateTriage(config, errors, options) {
|
|
|
851
856
|
: "",
|
|
852
857
|
key: agent && typeof agent === "object" ? triageAgentKey(agent, index) : "",
|
|
853
858
|
}));
|
|
854
|
-
validateResolvedTriageAgents(resolvedTriageAgents, "triage.resolvedAgents", errors);
|
|
855
|
-
if (
|
|
859
|
+
validateResolvedTriageAgents(resolvedTriageAgents, "triage.resolvedAgents", errors, mode);
|
|
860
|
+
if (mode === "multi" &&
|
|
861
|
+
reporter != null &&
|
|
856
862
|
!resolvedTriageAgents.some((agent) => agent.key === reporter)) {
|
|
857
863
|
errors.push(`triage.reporter must match a triage voter key: ${reporter}`);
|
|
858
864
|
}
|
|
859
865
|
}
|
|
866
|
+
if (mode === "single" && reporter != null) {
|
|
867
|
+
errors.push("triage.reporter is not supported in single mode");
|
|
868
|
+
}
|
|
860
869
|
validateString(triage.reporter, "triage.reporter", errors);
|
|
861
|
-
validateTriageCreator(creator, "triage.creator", errors, options.modelCatalog);
|
|
870
|
+
validateTriageCreator(creator, "triage.creator", errors, options.modelCatalog, mode);
|
|
862
871
|
if (automation?.create && !creator)
|
|
863
872
|
errors.push("triage.creator is required when triage.automation.create is true");
|
|
864
|
-
if (automation?.create && creator && !creator.account)
|
|
873
|
+
if (mode === "multi" && automation?.create && creator && !creator.account)
|
|
865
874
|
errors.push("triage.creator.account is required when triage.automation.create is true");
|
|
866
875
|
if (automation != null && !isPlainObject(automation)) {
|
|
867
876
|
errors.push("triage.automation must be an object");
|
|
@@ -1041,6 +1050,8 @@ export async function validateConfig(config, options = {}) {
|
|
|
1041
1050
|
validateString(config.$schema, "$schema", errors);
|
|
1042
1051
|
validateString(config.account, "account", errors);
|
|
1043
1052
|
validateString(config.language, "language", errors);
|
|
1053
|
+
if (config.review || config.triage)
|
|
1054
|
+
validateReviewIdentity(config, errors);
|
|
1044
1055
|
if (config.agents != null && !isPlainObject(config.agents)) {
|
|
1045
1056
|
errors.push("agents must be an object");
|
|
1046
1057
|
}
|
|
@@ -1059,7 +1070,6 @@ export async function validateConfig(config, options = {}) {
|
|
|
1059
1070
|
else {
|
|
1060
1071
|
validateKnownKeys(config.review, "review", REVIEW_KEYS, errors);
|
|
1061
1072
|
}
|
|
1062
|
-
validateReviewIdentity(config, errors);
|
|
1063
1073
|
if (!config.review.reviewers)
|
|
1064
1074
|
errors.push("review.reviewers is required");
|
|
1065
1075
|
validateReviewerList(config.review.reviewers, "review.reviewers", errors, options.modelCatalog, mode);
|
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-20260525213616",
|
|
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>",
|
package/schema.json
CHANGED
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
"triageAgent": {
|
|
116
116
|
"type": "object",
|
|
117
117
|
"if": { "not": { "required": ["ref"] } },
|
|
118
|
-
"then": { "required": ["model"
|
|
118
|
+
"then": { "required": ["model"] },
|
|
119
119
|
"additionalProperties": false,
|
|
120
120
|
"properties": {
|
|
121
121
|
"ref": { "type": "string", "minLength": 1 },
|