opencode-magi 0.7.0 → 0.9.0

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.
@@ -256,6 +256,24 @@ The object must match this shape:
256
256
  ]
257
257
  }
258
258
  </output_contract>`.trim();
259
+ export const triageSignalOutputContract = `
260
+ <output_contract>
261
+ Return exactly one JSON object and nothing else. Do not wrap it in markdown.
262
+
263
+ The object must match this shape:
264
+ {
265
+ "signals": [
266
+ {
267
+ "id": "configured_signal_id",
268
+ "reason": "Short rationale."
269
+ }
270
+ ]
271
+ }
272
+
273
+ Rules:
274
+ - Return only configured signal IDs that apply.
275
+ - Return an empty signals array when none apply.
276
+ </output_contract>`.trim();
259
277
  const outputContractsBySchemaName = {
260
278
  "CI classification": ciClassificationOutputContract,
261
279
  "close reconsideration": closeReconsiderationOutputContract,
@@ -264,13 +282,14 @@ const outputContractsBySchemaName = {
264
282
  rereview: rereviewOutputContract,
265
283
  "rereview close reconsideration": rereviewCloseReconsiderationOutputContract,
266
284
  review: reviewOutputContract,
267
- "triage acceptance": triageVoteOutputContract('"YES" | "NO" | "ASK"'),
285
+ "triage acceptance": triageVoteOutputContract('"YES" | "NO" | "INVALID" | "ASK"'),
268
286
  "triage category": triageVoteOutputContract('"ASK" or one of the configured category IDs'),
269
287
  "triage create PR": triageCreatePrOutputContract,
270
288
  "triage comment classification": triageCommentClassificationOutputContract,
271
289
  "triage duplicate": triageDuplicateOutputContract,
272
290
  "triage existing PR": triageVoteOutputContract('"RELATED_PR_HANDLES_ISSUE" | "RELATED_PR_DOES_NOT_HANDLE_ISSUE"'),
273
291
  "triage reconsider": triageVoteOutputContract('"YES" | "NO" | "ASK"'),
292
+ "triage signal": triageSignalOutputContract,
274
293
  };
275
294
  export function repairPrompt(schemaName) {
276
295
  const outputContract = outputContractsBySchemaName[schemaName];
@@ -112,7 +112,25 @@ export function parseTriageCategoryOutput(text, categories) {
112
112
  return parseTriageVote(text, ["ASK", ...categories]);
113
113
  }
114
114
  export function parseTriageBinaryOutput(text) {
115
- return parseTriageVote(text, ["ASK", "NO", "YES"]);
115
+ return parseTriageVote(text, ["ASK", "INVALID", "NO", "YES"]);
116
+ }
117
+ export function parseTriageSignalOutput(text, signalIds) {
118
+ const data = extractJson(text);
119
+ if (!data || typeof data !== "object")
120
+ throw new Error("triage signal output must be an object");
121
+ const ids = new Set(signalIds);
122
+ return {
123
+ signals: requireArray(data.signals, "signals").map((item, index) => {
124
+ const value = item;
125
+ const id = requireString(value.id, `signals[${index}].id`);
126
+ if (!ids.has(id))
127
+ throw new Error(`signals[${index}].id is not configured`);
128
+ return {
129
+ id,
130
+ reason: requireString(value.reason, `signals[${index}].reason`),
131
+ };
132
+ }),
133
+ };
116
134
  }
117
135
  export function parseTriageDuplicateOutput(text) {
118
136
  const data = extractJson(text);
@@ -0,0 +1,10 @@
1
+ Resolve merge conflicts for pull request #{pr} in {owner}/{repo}.
2
+ The PR worktree is {worktreePath}.
3
+
4
+ The latest base branch is {baseBranch} at {baseSha}.
5
+ The PR head before conflict recovery was {headSha}.
6
+
7
+ Conflicted files:
8
+ {conflictedFiles}
9
+
10
+ Resolve every merge conflict in the worktree. Preserve the intended PR behavior while incorporating the latest base branch changes. Stage all resolved files and create a commit. Do not push.
@@ -13,6 +13,8 @@ Every newFinding must target a valid right-side line in the PR diff.
13
13
  If the problem itself does not have an exact changed line, choose the nearest changed line that represents the cause, responsibility, missing implementation, or affected behavior. This includes but is not limited to missing validation, missing wiring, missing requirements, missing tests, missing documentation, affected configuration, or relevant call sites.
14
14
  Do not omit line. Do not create file-level or body-only newFindings.
15
15
 
16
+ If `<merge_conflict_context>` is present, treat unresolved merge conflicts as review findings. Request changes when a conflict makes the PR unsafe or impossible to merge, and prefer the provided `suggestedLine` when it is present.
17
+
16
18
  {ciFailureContextBlock}
17
19
  Do not edit files or perform write operations.
18
20
 
@@ -14,4 +14,6 @@ Every finding must target a valid right-side line in the PR diff.
14
14
  If the problem itself does not have an exact changed line, choose the nearest changed line that represents the cause, responsibility, missing implementation, or affected behavior. This includes but is not limited to missing validation, missing wiring, missing requirements, missing tests, missing documentation, affected configuration, or relevant call sites.
15
15
  Do not omit line. Do not create file-level or body-only findings.
16
16
 
17
+ If `<merge_conflict_context>` is present, treat unresolved merge conflicts as review findings. Request changes when a conflict makes the PR unsafe or impossible to merge, and prefer the provided `suggestedLine` when it is present.
18
+
17
19
  {ciFailureContextBlock}
@@ -1,6 +1,6 @@
1
1
  Evaluate issue #{issue} in {owner}/{repo} for the selected category.
2
2
 
3
- Choose YES when the issue should be accepted for the project. Choose NO when it should be rejected, is not actionable, or is not appropriate for this project. Choose ASK when specific missing information is required before deciding.
3
+ Choose YES when the issue should be accepted for the project. Choose NO when it should not be worked on for normal wontfix reasons. Choose INVALID when the report is unreproducible, contradictory, based on an impossible premise, or otherwise not actionable as an issue. Choose ASK when specific missing information is required before deciding.
4
4
 
5
5
  <context>
6
6
  {context}
@@ -0,0 +1,10 @@
1
+ Evaluate optional triage signals for issue #{issue} in {owner}/{repo}.
2
+
3
+ Configured signals:
4
+ {signalOptions}
5
+
6
+ Return every configured signal that applies to the final triage result and issue context. Do not invent signal IDs.
7
+
8
+ <context>
9
+ {context}
10
+ </context>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-magi",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
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>",
@@ -28,26 +28,26 @@
28
28
  "schema.json"
29
29
  ],
30
30
  "dependencies": {
31
- "@opencode-ai/plugin": "^1.15.5",
31
+ "@opencode-ai/plugin": "^1.15.10",
32
32
  "ajv": "^8.20.0",
33
33
  "picomatch": "^4.0.4",
34
- "valibot": "^1.4.0"
34
+ "valibot": "^1.4.1"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@changesets/changelog-github": "^0.7.0",
38
38
  "@changesets/cli": "^2.30.0",
39
39
  "@commitlint/cli": "^21.0.1",
40
40
  "@commitlint/config-conventional": "^21.0.1",
41
- "@types/node": "^25.9.0",
41
+ "@types/node": "^25.9.1",
42
42
  "@types/picomatch": "^4.0.3",
43
- "@typescript/native-preview": "7.0.0-dev.20260518.1",
43
+ "@typescript/native-preview": "7.0.0-dev.20260525.1",
44
44
  "@vitest/coverage-v8": "^4.1.7",
45
45
  "@vitest/ui": "^4.1.7",
46
46
  "eslint-plugin-perfectionist": "^5.9.0",
47
47
  "eslint-plugin-unused-imports": "^4.4.1",
48
- "lefthook": "^2.1.6",
49
- "oxfmt": "^0.50.0",
50
- "oxlint": "^1.65.0",
48
+ "lefthook": "^2.1.8",
49
+ "oxfmt": "^0.51.0",
50
+ "oxlint": "^1.66.0",
51
51
  "oxlint-tsgolint": "^0.23.0",
52
52
  "rimraf": "^6.1.3",
53
53
  "vitest": "^4.1.7"
package/schema.json CHANGED
@@ -77,7 +77,7 @@
77
77
  "reviewer": {
78
78
  "type": "object",
79
79
  "if": { "not": { "required": ["ref"] } },
80
- "then": { "required": ["model", "account"] },
80
+ "then": { "required": ["model"] },
81
81
  "additionalProperties": false,
82
82
  "properties": {
83
83
  "ref": { "type": "string", "minLength": 1 },
@@ -179,6 +179,15 @@
179
179
  "close": { "type": "boolean" }
180
180
  }
181
181
  },
182
+ "mergeAutomation": {
183
+ "type": "object",
184
+ "additionalProperties": false,
185
+ "properties": {
186
+ "merge": { "type": "boolean", "default": true },
187
+ "close": { "type": "boolean", "default": false },
188
+ "conflict": { "type": "boolean", "default": false }
189
+ }
190
+ },
182
191
  "reviewChecks": {
183
192
  "type": "object",
184
193
  "additionalProperties": false,
@@ -270,6 +279,47 @@
270
279
  "description": { "type": "string" }
271
280
  }
272
281
  },
282
+ "triageSignal": {
283
+ "type": "object",
284
+ "additionalProperties": false,
285
+ "required": ["id", "description"],
286
+ "properties": {
287
+ "id": { "type": "string", "pattern": "^[A-Za-z0-9_-]+$" },
288
+ "description": { "type": "string", "minLength": 1 }
289
+ }
290
+ },
291
+ "triageLabelRuleCondition": {
292
+ "type": "object",
293
+ "additionalProperties": false,
294
+ "minProperties": 1,
295
+ "properties": {
296
+ "disposition": {
297
+ "enum": [
298
+ "accepted",
299
+ "rejected",
300
+ "invalid",
301
+ "duplicate",
302
+ "already_handled",
303
+ "needs_category",
304
+ "needs_acceptance",
305
+ "blocked",
306
+ "failed"
307
+ ]
308
+ },
309
+ "category": { "type": "string" },
310
+ "signals": { "type": "array", "items": { "type": "string" } }
311
+ }
312
+ },
313
+ "triageLabelRule": {
314
+ "type": "object",
315
+ "additionalProperties": false,
316
+ "required": ["when"],
317
+ "properties": {
318
+ "when": { "$ref": "#/$defs/triageLabelRuleCondition" },
319
+ "add": { "type": "array", "items": { "type": "string" } },
320
+ "remove": { "type": "array", "items": { "type": "string" } }
321
+ }
322
+ },
273
323
  "triageAutomation": {
274
324
  "type": "object",
275
325
  "additionalProperties": false,
@@ -278,10 +328,9 @@
278
328
  "create": { "type": "boolean", "default": false },
279
329
  "review": { "type": "boolean", "default": false },
280
330
  "merge": { "type": "boolean", "default": false },
281
- "clear": {
331
+ "label": {
282
332
  "type": "array",
283
- "items": { "type": "string" },
284
- "default": ["triage"]
333
+ "items": { "$ref": "#/$defs/triageLabelRule" }
285
334
  }
286
335
  }
287
336
  },
@@ -317,6 +366,8 @@
317
366
  "type": "object",
318
367
  "additionalProperties": false,
319
368
  "properties": {
369
+ "mode": { "enum": ["multi", "single"], "default": "single" },
370
+ "account": { "type": "string", "minLength": 1 },
320
371
  "reviewers": {
321
372
  "type": "array",
322
373
  "minItems": 3,
@@ -339,7 +390,7 @@
339
390
  "editor": { "$ref": "#/$defs/editor" },
340
391
  "checks": { "$ref": "#/$defs/mergeChecks" },
341
392
  "prompts": { "$ref": "#/$defs/mergePrompts" },
342
- "automation": { "$ref": "#/$defs/automation" },
393
+ "automation": { "$ref": "#/$defs/mergeAutomation" },
343
394
  "maxThreadResolutionCycles": {
344
395
  "type": "integer",
345
396
  "minimum": 0,
@@ -362,6 +413,10 @@
362
413
  "type": "array",
363
414
  "items": { "$ref": "#/$defs/triageCategory" }
364
415
  },
416
+ "signals": {
417
+ "type": "array",
418
+ "items": { "$ref": "#/$defs/triageSignal" }
419
+ },
365
420
  "automation": { "$ref": "#/$defs/triageAutomation" },
366
421
  "safety": { "$ref": "#/$defs/triageSafety" },
367
422
  "concurrency": { "$ref": "#/$defs/triageConcurrency" },