opencode-magi 0.0.0-dev-20260519144738 → 0.0.0-dev-20260520030110

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.
@@ -171,6 +171,48 @@ Rules:
171
171
  - SCOPE_OUT means the failure is likely flaky, external, or infrastructure-related and may be rerun.
172
172
  - If uncertain, choose SCOPE_IN.
173
173
  </output_contract>`.trim();
174
+ export function triageVoteOutputContract(votes) {
175
+ return `
176
+ <output_contract>
177
+ Return exactly one JSON object and nothing else. Do not wrap it in markdown.
178
+
179
+ The object must match this shape:
180
+ {
181
+ "vote": ${votes},
182
+ "reason": "Short rationale."
183
+ }
184
+ </output_contract>`.trim();
185
+ }
186
+ export const triageDuplicateOutputContract = `
187
+ <output_contract>
188
+ Return exactly one JSON object and nothing else. Do not wrap it in markdown.
189
+
190
+ The object must match this shape:
191
+ {
192
+ "vote": "DUPLICATE" | "NOT_DUPLICATE",
193
+ "duplicateOf": 123,
194
+ "reason": "Short rationale."
195
+ }
196
+
197
+ Rules:
198
+ - duplicateOf is required only when vote is DUPLICATE.
199
+ - duplicateOf must be one of the provided duplicate candidate issue numbers.
200
+ </output_contract>`.trim();
201
+ export const triageCommentClassificationOutputContract = `
202
+ <output_contract>
203
+ Return exactly one JSON object and nothing else. Do not wrap it in markdown.
204
+
205
+ The object must match this shape:
206
+ {
207
+ "comments": [
208
+ {
209
+ "commentId": 123,
210
+ "classification": "OBJECTION" | "NEW_EVIDENCE" | "CLARIFICATION" | "ACKNOWLEDGEMENT" | "UNRELATED",
211
+ "reason": "Short rationale."
212
+ }
213
+ ]
214
+ }
215
+ </output_contract>`.trim();
174
216
  const outputContractsBySchemaName = {
175
217
  "CI classification": ciClassificationOutputContract,
176
218
  "close reconsideration": closeReconsiderationOutputContract,
@@ -179,6 +221,12 @@ const outputContractsBySchemaName = {
179
221
  rereview: rereviewOutputContract,
180
222
  "rereview close reconsideration": rereviewCloseReconsiderationOutputContract,
181
223
  review: reviewOutputContract,
224
+ "triage bug": triageVoteOutputContract('"YES" | "NO" | "ASK"'),
225
+ "triage comment classification": triageCommentClassificationOutputContract,
226
+ "triage duplicate": triageDuplicateOutputContract,
227
+ "triage existing PR": triageVoteOutputContract('"RELATED_PR_HANDLES_ISSUE" | "RELATED_PR_DOES_NOT_HANDLE_ISSUE"'),
228
+ "triage feature": triageVoteOutputContract('"YES" | "NO" | "ASK"'),
229
+ "triage kind": triageVoteOutputContract('"BUG" | "FEATURE" | "ASK"'),
182
230
  };
183
231
  export function repairPrompt(schemaName) {
184
232
  const outputContract = outputContractsBySchemaName[schemaName];
@@ -71,6 +71,74 @@ function requireNumber(value, path) {
71
71
  throw new Error(`${path} must be an integer`);
72
72
  return value;
73
73
  }
74
+ function requireOneOf(value, path, values) {
75
+ const text = requireString(value, path);
76
+ if (!values.includes(text)) {
77
+ throw new Error(`${path} must be ${values.join(", ")}`);
78
+ }
79
+ return text;
80
+ }
81
+ function parseTriageVote(text, votes) {
82
+ const data = extractJson(text);
83
+ if (!data || typeof data !== "object")
84
+ throw new Error("triage vote output must be an object");
85
+ return {
86
+ reason: requireString(data.reason, "reason"),
87
+ vote: requireOneOf(data.vote, "vote", votes),
88
+ };
89
+ }
90
+ export function parseTriageExistingPrOutput(text) {
91
+ return parseTriageVote(text, [
92
+ "RELATED_PR_DOES_NOT_HANDLE_ISSUE",
93
+ "RELATED_PR_HANDLES_ISSUE",
94
+ ]);
95
+ }
96
+ export function parseTriageKindOutput(text) {
97
+ return parseTriageVote(text, ["ASK", "BUG", "FEATURE"]);
98
+ }
99
+ export function parseTriageBinaryOutput(text) {
100
+ return parseTriageVote(text, ["ASK", "NO", "YES"]);
101
+ }
102
+ export function parseTriageDuplicateOutput(text) {
103
+ const data = extractJson(text);
104
+ if (!data || typeof data !== "object")
105
+ throw new Error("triage duplicate output must be an object");
106
+ const vote = requireOneOf(data.vote, "vote", [
107
+ "DUPLICATE",
108
+ "NOT_DUPLICATE",
109
+ ]);
110
+ const duplicateOf = data.duplicateOf == null
111
+ ? undefined
112
+ : requireNumber(data.duplicateOf, "duplicateOf");
113
+ if (vote === "DUPLICATE" && duplicateOf == null)
114
+ throw new Error("DUPLICATE requires duplicateOf");
115
+ return {
116
+ duplicateOf,
117
+ reason: requireString(data.reason, "reason"),
118
+ vote,
119
+ };
120
+ }
121
+ export function parseTriageCommentClassificationOutput(text) {
122
+ const data = extractJson(text);
123
+ if (!data || typeof data !== "object")
124
+ throw new Error("triage comment classification output must be an object");
125
+ return {
126
+ comments: requireArray(data.comments, "comments").map((item, index) => {
127
+ const value = item;
128
+ return {
129
+ classification: requireOneOf(value.classification, `comments[${index}].classification`, [
130
+ "ACKNOWLEDGEMENT",
131
+ "CLARIFICATION",
132
+ "NEW_EVIDENCE",
133
+ "OBJECTION",
134
+ "UNRELATED",
135
+ ]),
136
+ commentId: requireNumber(value.commentId, `comments[${index}].commentId`),
137
+ reason: requireString(value.reason, `comments[${index}].reason`),
138
+ };
139
+ }),
140
+ };
141
+ }
74
142
  export function parseReviewOutput(text) {
75
143
  const data = extractJson(text);
76
144
  if (!data || typeof data !== "object")
@@ -0,0 +1,5 @@
1
+ Decide the next action for issue #{issue} in {owner}/{repo} from the provided triage result.
2
+
3
+ <context>
4
+ {context}
5
+ </context>
@@ -0,0 +1,7 @@
1
+ Evaluate bug issue #{issue} in {owner}/{repo}.
2
+
3
+ Choose YES when the bug is reproduced or otherwise valid based on strong evidence. Choose NO when it is not reproduced, invalid, a misunderstanding, or not actionable as a bug. Choose ASK when specific missing information is required.
4
+
5
+ <context>
6
+ {context}
7
+ </context>
@@ -0,0 +1,7 @@
1
+ Classify allowed mention replies for issue #{issue} in {owner}/{repo}.
2
+
3
+ Use OBJECTION for disagreement or reconsideration requests. Use NEW_EVIDENCE for new logs, reproduction steps, screenshots, links, or use cases. Use CLARIFICATION for answers to questions or ambiguity reduction. Use ACKNOWLEDGEMENT for acceptance or thanks. Use UNRELATED for unrelated content.
4
+
5
+ <context>
6
+ {context}
7
+ </context>
@@ -0,0 +1,5 @@
1
+ Compose a final triage comment for issue #{issue} in {owner}/{repo}.
2
+
3
+ <context>
4
+ {context}
5
+ </context>
@@ -0,0 +1,7 @@
1
+ Create an implementation pull request for issue #{issue} in {owner}/{repo}.
2
+
3
+ Use the checked-out worktree and commit your changes. Return the required structured output.
4
+
5
+ <context>
6
+ {context}
7
+ </context>
@@ -0,0 +1,7 @@
1
+ Decide whether issue #{issue} in {owner}/{repo} duplicates one of the provided duplicate candidates.
2
+
3
+ Use only the provided context. Return DUPLICATE only when the target issue is clearly the same underlying report or request.
4
+
5
+ <context>
6
+ {context}
7
+ </context>
@@ -0,0 +1,7 @@
1
+ Decide whether an existing related pull request already handles issue #{issue} in {owner}/{repo}.
2
+
3
+ Use only the provided context. Return HANDLE only when the PR clearly addresses the issue.
4
+
5
+ <context>
6
+ {context}
7
+ </context>
@@ -0,0 +1,7 @@
1
+ Evaluate feature issue #{issue} in {owner}/{repo}.
2
+
3
+ Choose YES when the feature should be implemented. Choose NO when it should not be implemented for this project. Choose ASK when specific missing information is required.
4
+
5
+ <context>
6
+ {context}
7
+ </context>
@@ -0,0 +1,7 @@
1
+ Classify issue #{issue} in {owner}/{repo}.
2
+
3
+ Choose BUG when it reports broken existing behavior. Choose FEATURE when it requests new behavior or enhancement. Choose ASK when more information is required to classify it.
4
+
5
+ <context>
6
+ {context}
7
+ </context>
@@ -0,0 +1,5 @@
1
+ Compose concrete questions for issue #{issue} in {owner}/{repo}.
2
+
3
+ <context>
4
+ {context}
5
+ </context>
@@ -0,0 +1,5 @@
1
+ Reconsider issue #{issue} in {owner}/{repo} using the provided previous triage result and triggering comments.
2
+
3
+ <context>
4
+ {context}
5
+ </context>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-magi",
3
- "version": "0.0.0-dev-20260519144738",
3
+ "version": "0.0.0-dev-20260520030110",
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
@@ -47,7 +47,8 @@
47
47
  "repairAttempts": { "type": "integer", "minimum": 0, "default": 3 }
48
48
  }
49
49
  },
50
- "review": { "$ref": "#/$defs/review" }
50
+ "review": { "$ref": "#/$defs/review" },
51
+ "triage": { "$ref": "#/$defs/triage" }
51
52
  },
52
53
  "$defs": {
53
54
  "reviewer": {
@@ -84,6 +85,39 @@
84
85
  "persona": { "type": "string" }
85
86
  }
86
87
  },
88
+ "triageAgent": {
89
+ "type": "object",
90
+ "required": ["model"],
91
+ "additionalProperties": false,
92
+ "properties": {
93
+ "id": { "type": "string", "pattern": "^[A-Za-z0-9_-]+$" },
94
+ "model": { "type": "string", "minLength": 1 },
95
+ "options": { "type": "object", "additionalProperties": true },
96
+ "permissions": { "$ref": "#/$defs/permissions" },
97
+ "persona": { "type": "string" }
98
+ }
99
+ },
100
+ "triageCreator": {
101
+ "type": "object",
102
+ "required": ["model", "author"],
103
+ "additionalProperties": false,
104
+ "properties": {
105
+ "account": { "type": "string", "minLength": 1 },
106
+ "model": { "type": "string", "minLength": 1 },
107
+ "options": { "type": "object", "additionalProperties": true },
108
+ "author": {
109
+ "type": "object",
110
+ "required": ["name", "email"],
111
+ "additionalProperties": false,
112
+ "properties": {
113
+ "name": { "type": "string", "minLength": 1 },
114
+ "email": { "type": "string", "minLength": 1 }
115
+ }
116
+ },
117
+ "permissions": { "$ref": "#/$defs/permissions" },
118
+ "persona": { "type": "string" }
119
+ }
120
+ },
87
121
  "automation": {
88
122
  "type": "object",
89
123
  "additionalProperties": false,
@@ -116,6 +150,13 @@
116
150
  "reviewers": { "type": "integer", "minimum": 1, "default": 3 }
117
151
  }
118
152
  },
153
+ "triageConcurrency": {
154
+ "type": "object",
155
+ "additionalProperties": false,
156
+ "properties": {
157
+ "runs": { "type": "integer", "minimum": 1, "default": 3 }
158
+ }
159
+ },
119
160
  "safety": {
120
161
  "type": "object",
121
162
  "additionalProperties": false,
@@ -147,6 +188,66 @@
147
188
  "ciClassification": { "type": "string" }
148
189
  }
149
190
  },
191
+ "triagePrompts": {
192
+ "type": "object",
193
+ "additionalProperties": false,
194
+ "properties": {
195
+ "existingPr": { "type": "string" },
196
+ "duplicate": { "type": "string" },
197
+ "kind": { "type": "string" },
198
+ "bug": { "type": "string" },
199
+ "feature": { "type": "string" },
200
+ "action": { "type": "string" },
201
+ "question": { "type": "string" },
202
+ "comment": { "type": "string" },
203
+ "commentClassification": { "type": "string" },
204
+ "reconsider": { "type": "string" },
205
+ "createPr": { "type": "string" }
206
+ }
207
+ },
208
+ "triageKindRule": {
209
+ "type": "object",
210
+ "additionalProperties": false,
211
+ "properties": {
212
+ "label": { "type": "array", "items": { "type": "string" } },
213
+ "type": { "type": "array", "items": { "type": "string" } }
214
+ }
215
+ },
216
+ "triageKind": {
217
+ "type": "object",
218
+ "additionalProperties": false,
219
+ "properties": {
220
+ "bug": { "$ref": "#/$defs/triageKindRule" },
221
+ "feature": { "$ref": "#/$defs/triageKindRule" }
222
+ }
223
+ },
224
+ "triageAutomation": {
225
+ "type": "object",
226
+ "additionalProperties": false,
227
+ "properties": {
228
+ "close": { "type": "boolean", "default": false },
229
+ "pr": { "type": "boolean", "default": false },
230
+ "clear": {
231
+ "type": "array",
232
+ "items": { "type": "string" },
233
+ "default": ["triage"]
234
+ }
235
+ }
236
+ },
237
+ "triageSafety": {
238
+ "type": "object",
239
+ "additionalProperties": false,
240
+ "properties": {
241
+ "requiredLabels": { "type": "array", "items": { "type": "string" } },
242
+ "blockedLabels": { "type": "array", "items": { "type": "string" } },
243
+ "allowAuthors": { "type": "array", "items": { "type": "string" } },
244
+ "allowMentionActors": {
245
+ "type": "array",
246
+ "items": { "type": "string" }
247
+ },
248
+ "allowMentionRoles": { "type": "array", "items": { "type": "string" } }
249
+ }
250
+ },
150
251
  "reviewMerge": {
151
252
  "type": "object",
152
253
  "additionalProperties": false,
@@ -196,6 +297,26 @@
196
297
  }
197
298
  }
198
299
  },
300
+ "triage": {
301
+ "type": "object",
302
+ "additionalProperties": false,
303
+ "properties": {
304
+ "account": { "type": "string", "minLength": 1 },
305
+ "agents": {
306
+ "type": "array",
307
+ "minItems": 3,
308
+ "items": { "$ref": "#/$defs/triageAgent" }
309
+ },
310
+ "creator": { "$ref": "#/$defs/triageCreator" },
311
+ "kind": { "$ref": "#/$defs/triageKind" },
312
+ "automation": { "$ref": "#/$defs/triageAutomation" },
313
+ "safety": { "$ref": "#/$defs/triageSafety" },
314
+ "concurrency": { "$ref": "#/$defs/triageConcurrency" },
315
+ "prompts": { "$ref": "#/$defs/triagePrompts" },
316
+ "output": { "type": "string" },
317
+ "worktree": { "type": "string" }
318
+ }
319
+ },
199
320
  "permissionAction": { "enum": ["allow", "ask", "deny"] },
200
321
  "permissionRule": {
201
322
  "oneOf": [