opencode-magi 0.2.0 → 0.4.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.
- package/README.md +19 -0
- package/dist/commands.js +4 -0
- package/dist/config/output.js +11 -2
- package/dist/config/resolve.js +81 -1
- package/dist/config/validate.js +341 -3
- package/dist/config/worktree.js +8 -2
- package/dist/github/commands.js +381 -19
- package/dist/index.js +252 -26
- package/dist/orchestrator/ci.js +1 -1
- package/dist/orchestrator/findings.js +4 -3
- package/dist/orchestrator/inline-comments.js +79 -0
- package/dist/orchestrator/majority.js +14 -0
- package/dist/orchestrator/merge.js +108 -34
- package/dist/orchestrator/report.js +25 -7
- package/dist/orchestrator/review-context.js +309 -0
- package/dist/orchestrator/review.js +122 -14
- package/dist/orchestrator/run-manager.js +408 -17
- package/dist/orchestrator/triage.js +1119 -0
- package/dist/permissions/editor.json +8 -1
- package/dist/prompts/compose.js +163 -1
- package/dist/prompts/contracts.js +131 -18
- package/dist/prompts/output.js +173 -22
- package/dist/prompts/templates/merge/edit.md +12 -5
- package/dist/prompts/templates/review/review.md +6 -0
- package/dist/prompts/templates/triage/acceptance.md +7 -0
- package/dist/prompts/templates/triage/action.md +5 -0
- package/dist/prompts/templates/triage/category.md +10 -0
- package/dist/prompts/templates/triage/comment-classification.md +7 -0
- package/dist/prompts/templates/triage/comment.md +5 -0
- package/dist/prompts/templates/triage/create.md +7 -0
- package/dist/prompts/templates/triage/duplicate.md +7 -0
- package/dist/prompts/templates/triage/existing-pr.md +7 -0
- package/dist/prompts/templates/triage/question.md +5 -0
- package/dist/prompts/templates/triage/reconsider.md +5 -0
- package/package.json +5 -2
- package/schema.json +162 -5
package/dist/prompts/output.js
CHANGED
|
@@ -71,6 +71,110 @@ function requireNumber(value, path) {
|
|
|
71
71
|
throw new Error(`${path} must be an integer`);
|
|
72
72
|
return value;
|
|
73
73
|
}
|
|
74
|
+
function optionalLine(value, path) {
|
|
75
|
+
return value == null ? undefined : requireNumber(value, path);
|
|
76
|
+
}
|
|
77
|
+
function optionalStartLine(input) {
|
|
78
|
+
if (input.value == null)
|
|
79
|
+
return undefined;
|
|
80
|
+
if (input.line == null)
|
|
81
|
+
throw new Error(`${input.path} requires line`);
|
|
82
|
+
return requireNumber(input.value, input.path);
|
|
83
|
+
}
|
|
84
|
+
function parseRequirementFindings(value) {
|
|
85
|
+
return (value == null ? [] : requireArray(value, "requirementFindings")).map((finding, index) => {
|
|
86
|
+
const item = finding;
|
|
87
|
+
return {
|
|
88
|
+
evidence: requireString(item.evidence, `requirementFindings[${index}].evidence`),
|
|
89
|
+
fix: requireString(item.fix, `requirementFindings[${index}].fix`),
|
|
90
|
+
issueNumber: requireNumber(item.issueNumber, `requirementFindings[${index}].issueNumber`),
|
|
91
|
+
requirement: requireString(item.requirement, `requirementFindings[${index}].requirement`),
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function requireOneOf(value, path, values) {
|
|
96
|
+
const text = requireString(value, path);
|
|
97
|
+
if (!values.includes(text)) {
|
|
98
|
+
throw new Error(`${path} must be ${values.join(", ")}`);
|
|
99
|
+
}
|
|
100
|
+
return text;
|
|
101
|
+
}
|
|
102
|
+
function parseTriageVote(text, votes) {
|
|
103
|
+
const data = extractJson(text);
|
|
104
|
+
if (!data || typeof data !== "object")
|
|
105
|
+
throw new Error("triage vote output must be an object");
|
|
106
|
+
return {
|
|
107
|
+
reason: requireString(data.reason, "reason"),
|
|
108
|
+
vote: requireOneOf(data.vote, "vote", votes),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
export function parseTriageExistingPrOutput(text) {
|
|
112
|
+
return parseTriageVote(text, [
|
|
113
|
+
"RELATED_PR_DOES_NOT_HANDLE_ISSUE",
|
|
114
|
+
"RELATED_PR_HANDLES_ISSUE",
|
|
115
|
+
]);
|
|
116
|
+
}
|
|
117
|
+
export function parseTriageCategoryOutput(text, categories) {
|
|
118
|
+
return parseTriageVote(text, ["ASK", ...categories]);
|
|
119
|
+
}
|
|
120
|
+
export function parseTriageBinaryOutput(text) {
|
|
121
|
+
return parseTriageVote(text, ["ASK", "NO", "YES"]);
|
|
122
|
+
}
|
|
123
|
+
export function parseTriageDuplicateOutput(text) {
|
|
124
|
+
const data = extractJson(text);
|
|
125
|
+
if (!data || typeof data !== "object")
|
|
126
|
+
throw new Error("triage duplicate output must be an object");
|
|
127
|
+
const vote = requireOneOf(data.vote, "vote", [
|
|
128
|
+
"DUPLICATE",
|
|
129
|
+
"NOT_DUPLICATE",
|
|
130
|
+
]);
|
|
131
|
+
const duplicateOf = data.duplicateOf == null
|
|
132
|
+
? undefined
|
|
133
|
+
: requireNumber(data.duplicateOf, "duplicateOf");
|
|
134
|
+
if (vote === "DUPLICATE" && duplicateOf == null)
|
|
135
|
+
throw new Error("DUPLICATE requires duplicateOf");
|
|
136
|
+
return {
|
|
137
|
+
duplicateOf,
|
|
138
|
+
reason: requireString(data.reason, "reason"),
|
|
139
|
+
vote,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
export function parseTriageCommentClassificationOutput(text) {
|
|
143
|
+
const data = extractJson(text);
|
|
144
|
+
if (!data || typeof data !== "object")
|
|
145
|
+
throw new Error("triage comment classification output must be an object");
|
|
146
|
+
return {
|
|
147
|
+
comments: requireArray(data.comments, "comments").map((item, index) => {
|
|
148
|
+
const value = item;
|
|
149
|
+
return {
|
|
150
|
+
classification: requireOneOf(value.classification, `comments[${index}].classification`, [
|
|
151
|
+
"ACKNOWLEDGEMENT",
|
|
152
|
+
"CLARIFICATION",
|
|
153
|
+
"NEW_EVIDENCE",
|
|
154
|
+
"OBJECTION",
|
|
155
|
+
"UNRELATED",
|
|
156
|
+
]),
|
|
157
|
+
commentId: requireNumber(value.commentId, `comments[${index}].commentId`),
|
|
158
|
+
reason: requireString(value.reason, `comments[${index}].reason`),
|
|
159
|
+
};
|
|
160
|
+
}),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
export function parseTriageActionOutput(text) {
|
|
164
|
+
const data = extractJson(text);
|
|
165
|
+
if (!data || typeof data !== "object")
|
|
166
|
+
throw new Error("triage action output must be an object");
|
|
167
|
+
return {
|
|
168
|
+
action: requireOneOf(data.action, "action", [
|
|
169
|
+
"ASK",
|
|
170
|
+
"CLEAR_ONLY",
|
|
171
|
+
"CLOSE",
|
|
172
|
+
"COMMENT",
|
|
173
|
+
"PR",
|
|
174
|
+
]),
|
|
175
|
+
reason: requireString(data.reason, "reason"),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
74
178
|
export function parseReviewOutput(text) {
|
|
75
179
|
const data = extractJson(text);
|
|
76
180
|
if (!data || typeof data !== "object")
|
|
@@ -79,25 +183,33 @@ export function parseReviewOutput(text) {
|
|
|
79
183
|
throw new Error("verdict must be MERGE, CHANGES_REQUESTED, or CLOSE");
|
|
80
184
|
const findings = requireArray(data.findings, "findings").map((finding, index) => {
|
|
81
185
|
const item = finding;
|
|
186
|
+
const line = optionalLine(item.line, `findings[${index}].line`);
|
|
82
187
|
return {
|
|
83
188
|
fix: requireString(item.fix, `findings[${index}].fix`),
|
|
84
189
|
issue: requireString(item.issue, `findings[${index}].issue`),
|
|
85
|
-
line
|
|
190
|
+
line,
|
|
86
191
|
path: requireString(item.path, `findings[${index}].path`),
|
|
87
192
|
perspective: item.perspective == null
|
|
88
193
|
? undefined
|
|
89
194
|
: requireString(item.perspective, `findings[${index}].perspective`),
|
|
90
|
-
startLine:
|
|
91
|
-
|
|
92
|
-
:
|
|
195
|
+
startLine: optionalStartLine({
|
|
196
|
+
line,
|
|
197
|
+
path: `findings[${index}].startLine`,
|
|
198
|
+
value: item.startLine,
|
|
199
|
+
}),
|
|
93
200
|
};
|
|
94
201
|
});
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
throw new Error("
|
|
99
|
-
if (data.verdict === "
|
|
100
|
-
|
|
202
|
+
const requirementFindings = parseRequirementFindings(data.requirementFindings);
|
|
203
|
+
if (data.verdict === "MERGE" &&
|
|
204
|
+
(findings.length || requirementFindings.length))
|
|
205
|
+
throw new Error("MERGE requires no findings or requirementFindings");
|
|
206
|
+
if (data.verdict === "CHANGES_REQUESTED" &&
|
|
207
|
+
!findings.length &&
|
|
208
|
+
!requirementFindings.length)
|
|
209
|
+
throw new Error("CHANGES_REQUESTED requires findings or requirementFindings");
|
|
210
|
+
if (data.verdict === "CLOSE" &&
|
|
211
|
+
(findings.length || requirementFindings.length))
|
|
212
|
+
throw new Error("CLOSE requires no findings or requirementFindings");
|
|
101
213
|
const reason = typeof data.reason === "string" && data.reason.trim()
|
|
102
214
|
? data.reason
|
|
103
215
|
: undefined;
|
|
@@ -106,6 +218,7 @@ export function parseReviewOutput(text) {
|
|
|
106
218
|
return {
|
|
107
219
|
findings,
|
|
108
220
|
reason,
|
|
221
|
+
requirementFindings,
|
|
109
222
|
verdict: data.verdict,
|
|
110
223
|
};
|
|
111
224
|
}
|
|
@@ -129,33 +242,41 @@ export function parseRereviewOutput(text) {
|
|
|
129
242
|
});
|
|
130
243
|
const newFindings = requireArray(data.newFindings, "newFindings").map((item, index) => {
|
|
131
244
|
const value = item;
|
|
245
|
+
const line = optionalLine(value.line, `newFindings[${index}].line`);
|
|
132
246
|
return {
|
|
133
247
|
body: requireString(value.body, `newFindings[${index}].body`),
|
|
134
|
-
line
|
|
248
|
+
line,
|
|
135
249
|
path: requireString(value.path, `newFindings[${index}].path`),
|
|
136
|
-
startLine:
|
|
137
|
-
|
|
138
|
-
:
|
|
250
|
+
startLine: optionalStartLine({
|
|
251
|
+
line,
|
|
252
|
+
path: `newFindings[${index}].startLine`,
|
|
253
|
+
value: value.startLine,
|
|
254
|
+
}),
|
|
139
255
|
};
|
|
140
256
|
});
|
|
141
|
-
|
|
142
|
-
|
|
257
|
+
const requirementFindings = parseRequirementFindings(data.requirementFindings);
|
|
258
|
+
if (data.verdict === "MERGE" &&
|
|
259
|
+
(followUps.length || newFindings.length || requirementFindings.length)) {
|
|
260
|
+
throw new Error("MERGE requires no followUps, newFindings, or requirementFindings");
|
|
143
261
|
}
|
|
144
|
-
if (data.verdict === "CLOSE" &&
|
|
145
|
-
|
|
262
|
+
if (data.verdict === "CLOSE" &&
|
|
263
|
+
(followUps.length || newFindings.length || requirementFindings.length)) {
|
|
264
|
+
throw new Error("CLOSE requires no followUps, newFindings, or requirementFindings");
|
|
146
265
|
}
|
|
147
266
|
if (data.verdict === "CLOSE" && !data.reason) {
|
|
148
267
|
throw new Error("CLOSE requires reason");
|
|
149
268
|
}
|
|
150
269
|
if (data.verdict === "CHANGES_REQUESTED" &&
|
|
151
270
|
!followUps.length &&
|
|
152
|
-
!newFindings.length
|
|
153
|
-
|
|
271
|
+
!newFindings.length &&
|
|
272
|
+
!requirementFindings.length) {
|
|
273
|
+
throw new Error("CHANGES_REQUESTED requires followUps, newFindings, or requirementFindings");
|
|
154
274
|
}
|
|
155
275
|
return {
|
|
156
276
|
followUps,
|
|
157
277
|
newFindings,
|
|
158
278
|
reason: data.reason == null ? undefined : requireString(data.reason, "reason"),
|
|
279
|
+
requirementFindings,
|
|
159
280
|
resolve,
|
|
160
281
|
verdict: data.verdict,
|
|
161
282
|
};
|
|
@@ -212,7 +333,21 @@ export function parseCiClassificationOutput(text) {
|
|
|
212
333
|
}),
|
|
213
334
|
};
|
|
214
335
|
}
|
|
215
|
-
|
|
336
|
+
function parsePullRequest(value, options) {
|
|
337
|
+
if (value == null) {
|
|
338
|
+
if (options.required)
|
|
339
|
+
throw new Error("pullRequest is required");
|
|
340
|
+
return undefined;
|
|
341
|
+
}
|
|
342
|
+
if (typeof value !== "object")
|
|
343
|
+
throw new Error("pullRequest must be an object");
|
|
344
|
+
const pullRequest = value;
|
|
345
|
+
return {
|
|
346
|
+
body: requireString(pullRequest.body, "pullRequest.body"),
|
|
347
|
+
title: requireString(pullRequest.title, "pullRequest.title"),
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
function parseEditOutputWithOptions(text, options) {
|
|
216
351
|
const data = extractJson(text);
|
|
217
352
|
if (!data || typeof data !== "object")
|
|
218
353
|
throw new Error("edit output must be an object");
|
|
@@ -230,16 +365,20 @@ export function parseEditOutput(text) {
|
|
|
230
365
|
commentId: requireNumber(value.commentId, `responses[${index}].commentId`),
|
|
231
366
|
};
|
|
232
367
|
});
|
|
233
|
-
if (!responses.length)
|
|
368
|
+
if (options.requireResponses && data.mode === "REPLIED" && !responses.length)
|
|
234
369
|
throw new Error("responses must not be empty");
|
|
235
370
|
if (data.mode === "EDITED") {
|
|
236
371
|
if (!filesTouched.length)
|
|
237
372
|
throw new Error("EDITED requires filesTouched");
|
|
373
|
+
const pullRequest = parsePullRequest(data.pullRequest, {
|
|
374
|
+
required: options.requirePullRequest,
|
|
375
|
+
});
|
|
238
376
|
return {
|
|
239
377
|
commitMessage: requireString(data.commitMessage, "commitMessage"),
|
|
240
378
|
commitSha: requireString(data.commitSha, "commitSha"),
|
|
241
379
|
filesTouched,
|
|
242
380
|
mode: data.mode,
|
|
381
|
+
...(pullRequest ? { pullRequest } : {}),
|
|
243
382
|
responses,
|
|
244
383
|
};
|
|
245
384
|
}
|
|
@@ -258,3 +397,15 @@ export function parseEditOutput(text) {
|
|
|
258
397
|
responses,
|
|
259
398
|
};
|
|
260
399
|
}
|
|
400
|
+
export function parseEditOutput(text) {
|
|
401
|
+
return parseEditOutputWithOptions(text, {
|
|
402
|
+
requirePullRequest: false,
|
|
403
|
+
requireResponses: true,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
export function parseTriageCreatePrOutput(text) {
|
|
407
|
+
return parseEditOutputWithOptions(text, {
|
|
408
|
+
requirePullRequest: true,
|
|
409
|
+
requireResponses: false,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
Fix pull request #{pr} for {owner}/{repo}.
|
|
2
2
|
The PR worktree is {worktreePath}.
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
Act as the PR author and address every blocking review finding listed below.
|
|
5
|
+
Review findings are the complete set of requested changes. Inline findings target a PR diff line; file-level findings may not have a GitHub thread; requirement findings describe missing closing-issue requirements.
|
|
6
|
+
{reviewFindings}
|
|
7
|
+
|
|
8
|
+
Unresolved GitHub review threads are conversations that may need replies or resolution.
|
|
4
9
|
{unresolvedThreads}
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
If
|
|
8
|
-
If
|
|
10
|
+
|
|
11
|
+
For each review finding and thread, decide whether you agree with the reviewer.
|
|
12
|
+
If you understand and agree with the requested change, edit the code, stage changes, commit, and reply with action FIXED for each related thread.
|
|
13
|
+
If a requested change in a thread is incorrect or unnecessary and you have a clear reason, do not edit for that thread; reply with action DISAGREE and explain why.
|
|
14
|
+
If you cannot determine whether a threaded request is correct or what change is expected, do not blindly edit; reply with action ASK and ask a concrete question.
|
|
15
|
+
File-level and requirement findings may not have a thread to reply to, but they are still blocking and must be addressed.
|
|
9
16
|
Do not make changes just because a reviewer requested them. Do not push.
|
|
@@ -4,4 +4,10 @@ Review only the diff from {baseSha} to {headSha}.
|
|
|
4
4
|
Use: git -C {jsonEncodedWorktreePath} diff {baseSha}...{headSha}
|
|
5
5
|
Do not edit files or perform write operations.
|
|
6
6
|
|
|
7
|
+
This PR may include closing issue references.
|
|
8
|
+
For each closing issue, review whether the PR fully satisfies the issue body, acceptance criteria, required behavior, required tests, required documentation, and bounded issue comments.
|
|
9
|
+
Request changes if a closing issue requirement is missing, only documented, only schema-exposed, or not wired into runtime behavior.
|
|
10
|
+
Do not approve solely because the PR improves the codebase if it claims to close an issue that remains incomplete.
|
|
11
|
+
For referenced non-closing issues, use them as context only unless the PR body explicitly claims to complete them.
|
|
12
|
+
|
|
7
13
|
{ciFailureContextBlock}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Evaluate issue #{issue} in {owner}/{repo} for the selected category.
|
|
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.
|
|
4
|
+
|
|
5
|
+
<context>
|
|
6
|
+
{context}
|
|
7
|
+
</context>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Classify issue #{issue} in {owner}/{repo}.
|
|
2
|
+
|
|
3
|
+
Choose ASK when more information is required to classify what should be done. Otherwise choose exactly one configured category ID.
|
|
4
|
+
|
|
5
|
+
Configured categories:
|
|
6
|
+
{categoryOptions}
|
|
7
|
+
|
|
8
|
+
<context>
|
|
9
|
+
{context}
|
|
10
|
+
</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,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>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-magi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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>",
|
|
@@ -41,6 +41,8 @@
|
|
|
41
41
|
"@types/node": "^25.9.0",
|
|
42
42
|
"@types/picomatch": "^4.0.3",
|
|
43
43
|
"@typescript/native-preview": "7.0.0-dev.20260518.1",
|
|
44
|
+
"@vitest/coverage-v8": "^4.1.7",
|
|
45
|
+
"@vitest/ui": "^4.1.7",
|
|
44
46
|
"eslint-plugin-perfectionist": "^5.9.0",
|
|
45
47
|
"eslint-plugin-unused-imports": "^4.4.1",
|
|
46
48
|
"lefthook": "^2.1.6",
|
|
@@ -48,7 +50,7 @@
|
|
|
48
50
|
"oxlint": "^1.65.0",
|
|
49
51
|
"oxlint-tsgolint": "^0.23.0",
|
|
50
52
|
"rimraf": "^6.1.3",
|
|
51
|
-
"vitest": "^4.1.
|
|
53
|
+
"vitest": "^4.1.7"
|
|
52
54
|
},
|
|
53
55
|
"scripts": {
|
|
54
56
|
"prebuild": "node scripts/copy-data.ts",
|
|
@@ -60,6 +62,7 @@
|
|
|
60
62
|
"lint:fix": "oxlint . --max-warnings=0 --fix",
|
|
61
63
|
"quality": "pnpm format:check && pnpm lint:check && pnpm typecheck && pnpm test",
|
|
62
64
|
"test": "vitest run",
|
|
65
|
+
"test:dev": "vitest --watch --ui",
|
|
63
66
|
"typecheck": "tsgo --noEmit",
|
|
64
67
|
"release": "changeset publish",
|
|
65
68
|
"release:dev": "changeset version --snapshot dev && changeset publish --tag dev"
|
package/schema.json
CHANGED
|
@@ -9,7 +9,11 @@
|
|
|
9
9
|
"type": "object",
|
|
10
10
|
"additionalProperties": false,
|
|
11
11
|
"properties": {
|
|
12
|
-
"permissions": { "$ref": "#/$defs/permissions" }
|
|
12
|
+
"permissions": { "$ref": "#/$defs/permissions" },
|
|
13
|
+
"refs": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"additionalProperties": { "$ref": "#/$defs/agentRef" }
|
|
16
|
+
}
|
|
13
17
|
}
|
|
14
18
|
},
|
|
15
19
|
"clear": {
|
|
@@ -47,14 +51,37 @@
|
|
|
47
51
|
"repairAttempts": { "type": "integer", "minimum": 0, "default": 3 }
|
|
48
52
|
}
|
|
49
53
|
},
|
|
50
|
-
"review": { "$ref": "#/$defs/review" }
|
|
54
|
+
"review": { "$ref": "#/$defs/review" },
|
|
55
|
+
"triage": { "$ref": "#/$defs/triage" }
|
|
51
56
|
},
|
|
52
57
|
"$defs": {
|
|
58
|
+
"agentRef": {
|
|
59
|
+
"type": "object",
|
|
60
|
+
"additionalProperties": false,
|
|
61
|
+
"properties": {
|
|
62
|
+
"id": { "type": "string", "pattern": "^[A-Za-z0-9_-]+$" },
|
|
63
|
+
"model": { "type": "string", "minLength": 1 },
|
|
64
|
+
"options": { "type": "object", "additionalProperties": true },
|
|
65
|
+
"account": { "type": "string", "minLength": 1 },
|
|
66
|
+
"author": {
|
|
67
|
+
"type": "object",
|
|
68
|
+
"additionalProperties": false,
|
|
69
|
+
"properties": {
|
|
70
|
+
"name": { "type": "string", "minLength": 1 },
|
|
71
|
+
"email": { "type": "string", "minLength": 1 }
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"permissions": { "$ref": "#/$defs/permissions" },
|
|
75
|
+
"persona": { "type": "string" }
|
|
76
|
+
}
|
|
77
|
+
},
|
|
53
78
|
"reviewer": {
|
|
54
79
|
"type": "object",
|
|
55
|
-
"
|
|
80
|
+
"if": { "not": { "required": ["ref"] } },
|
|
81
|
+
"then": { "required": ["model", "account"] },
|
|
56
82
|
"additionalProperties": false,
|
|
57
83
|
"properties": {
|
|
84
|
+
"ref": { "type": "string", "minLength": 1 },
|
|
58
85
|
"id": { "type": "string", "pattern": "^[A-Za-z0-9_-]+$" },
|
|
59
86
|
"model": { "type": "string", "minLength": 1 },
|
|
60
87
|
"options": { "type": "object", "additionalProperties": true },
|
|
@@ -65,12 +92,51 @@
|
|
|
65
92
|
},
|
|
66
93
|
"editor": {
|
|
67
94
|
"type": "object",
|
|
68
|
-
"
|
|
95
|
+
"if": { "not": { "required": ["ref"] } },
|
|
96
|
+
"then": { "required": ["model", "account", "author"] },
|
|
97
|
+
"additionalProperties": false,
|
|
98
|
+
"properties": {
|
|
99
|
+
"ref": { "type": "string", "minLength": 1 },
|
|
100
|
+
"model": { "type": "string", "minLength": 1 },
|
|
101
|
+
"options": { "type": "object", "additionalProperties": true },
|
|
102
|
+
"account": { "type": "string", "minLength": 1 },
|
|
103
|
+
"author": {
|
|
104
|
+
"type": "object",
|
|
105
|
+
"required": ["name", "email"],
|
|
106
|
+
"additionalProperties": false,
|
|
107
|
+
"properties": {
|
|
108
|
+
"name": { "type": "string", "minLength": 1 },
|
|
109
|
+
"email": { "type": "string", "minLength": 1 }
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
"permissions": { "$ref": "#/$defs/permissions" },
|
|
113
|
+
"persona": { "type": "string" }
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
"triageAgent": {
|
|
117
|
+
"type": "object",
|
|
118
|
+
"if": { "not": { "required": ["ref"] } },
|
|
119
|
+
"then": { "required": ["model"] },
|
|
69
120
|
"additionalProperties": false,
|
|
70
121
|
"properties": {
|
|
122
|
+
"ref": { "type": "string", "minLength": 1 },
|
|
123
|
+
"id": { "type": "string", "pattern": "^[A-Za-z0-9_-]+$" },
|
|
71
124
|
"model": { "type": "string", "minLength": 1 },
|
|
72
125
|
"options": { "type": "object", "additionalProperties": true },
|
|
126
|
+
"permissions": { "$ref": "#/$defs/permissions" },
|
|
127
|
+
"persona": { "type": "string" }
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
"triageCreator": {
|
|
131
|
+
"type": "object",
|
|
132
|
+
"if": { "not": { "required": ["ref"] } },
|
|
133
|
+
"then": { "required": ["model", "author"] },
|
|
134
|
+
"additionalProperties": false,
|
|
135
|
+
"properties": {
|
|
136
|
+
"ref": { "type": "string", "minLength": 1 },
|
|
73
137
|
"account": { "type": "string", "minLength": 1 },
|
|
138
|
+
"model": { "type": "string", "minLength": 1 },
|
|
139
|
+
"options": { "type": "object", "additionalProperties": true },
|
|
74
140
|
"author": {
|
|
75
141
|
"type": "object",
|
|
76
142
|
"required": ["name", "email"],
|
|
@@ -88,7 +154,7 @@
|
|
|
88
154
|
"type": "object",
|
|
89
155
|
"additionalProperties": false,
|
|
90
156
|
"properties": {
|
|
91
|
-
"merge": { "type": "boolean" },
|
|
157
|
+
"merge": { "type": "boolean", "default": true },
|
|
92
158
|
"close": { "type": "boolean" }
|
|
93
159
|
}
|
|
94
160
|
},
|
|
@@ -116,6 +182,13 @@
|
|
|
116
182
|
"reviewers": { "type": "integer", "minimum": 1, "default": 3 }
|
|
117
183
|
}
|
|
118
184
|
},
|
|
185
|
+
"triageConcurrency": {
|
|
186
|
+
"type": "object",
|
|
187
|
+
"additionalProperties": false,
|
|
188
|
+
"properties": {
|
|
189
|
+
"runs": { "type": "integer", "minimum": 1, "default": 3 }
|
|
190
|
+
}
|
|
191
|
+
},
|
|
119
192
|
"safety": {
|
|
120
193
|
"type": "object",
|
|
121
194
|
"additionalProperties": false,
|
|
@@ -147,6 +220,67 @@
|
|
|
147
220
|
"ciClassification": { "type": "string" }
|
|
148
221
|
}
|
|
149
222
|
},
|
|
223
|
+
"triagePrompts": {
|
|
224
|
+
"type": "object",
|
|
225
|
+
"additionalProperties": false,
|
|
226
|
+
"properties": {
|
|
227
|
+
"existingPr": { "type": "string" },
|
|
228
|
+
"duplicate": { "type": "string" },
|
|
229
|
+
"category": { "type": "string" },
|
|
230
|
+
"acceptance": { "type": "string" },
|
|
231
|
+
"action": { "type": "string" },
|
|
232
|
+
"question": { "type": "string" },
|
|
233
|
+
"comment": { "type": "string" },
|
|
234
|
+
"commentClassification": { "type": "string" },
|
|
235
|
+
"reconsider": { "type": "string" },
|
|
236
|
+
"create": { "type": "string" },
|
|
237
|
+
"createGuidelines": { "type": "string" }
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
"triageCategory": {
|
|
241
|
+
"type": "object",
|
|
242
|
+
"additionalProperties": false,
|
|
243
|
+
"required": ["id"],
|
|
244
|
+
"properties": {
|
|
245
|
+
"id": {
|
|
246
|
+
"type": "string",
|
|
247
|
+
"pattern": "^[A-Za-z0-9_-]+$",
|
|
248
|
+
"not": { "enum": ["ASK", "none"] }
|
|
249
|
+
},
|
|
250
|
+
"labels": { "type": "array", "items": { "type": "string" } },
|
|
251
|
+
"types": { "type": "array", "items": { "type": "string" } },
|
|
252
|
+
"description": { "type": "string" }
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
"triageAutomation": {
|
|
256
|
+
"type": "object",
|
|
257
|
+
"additionalProperties": false,
|
|
258
|
+
"properties": {
|
|
259
|
+
"close": { "type": "boolean", "default": false },
|
|
260
|
+
"create": { "type": "boolean", "default": false },
|
|
261
|
+
"review": { "type": "boolean", "default": false },
|
|
262
|
+
"merge": { "type": "boolean", "default": false },
|
|
263
|
+
"clear": {
|
|
264
|
+
"type": "array",
|
|
265
|
+
"items": { "type": "string" },
|
|
266
|
+
"default": ["triage"]
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
"triageSafety": {
|
|
271
|
+
"type": "object",
|
|
272
|
+
"additionalProperties": false,
|
|
273
|
+
"properties": {
|
|
274
|
+
"requiredLabels": { "type": "array", "items": { "type": "string" } },
|
|
275
|
+
"blockedLabels": { "type": "array", "items": { "type": "string" } },
|
|
276
|
+
"allowAuthors": { "type": "array", "items": { "type": "string" } },
|
|
277
|
+
"allowMentionActors": {
|
|
278
|
+
"type": "array",
|
|
279
|
+
"items": { "type": "string" }
|
|
280
|
+
},
|
|
281
|
+
"allowMentionRoles": { "type": "array", "items": { "type": "string" } }
|
|
282
|
+
}
|
|
283
|
+
},
|
|
150
284
|
"reviewMerge": {
|
|
151
285
|
"type": "object",
|
|
152
286
|
"additionalProperties": false,
|
|
@@ -196,6 +330,29 @@
|
|
|
196
330
|
}
|
|
197
331
|
}
|
|
198
332
|
},
|
|
333
|
+
"triage": {
|
|
334
|
+
"type": "object",
|
|
335
|
+
"additionalProperties": false,
|
|
336
|
+
"properties": {
|
|
337
|
+
"account": { "type": "string", "minLength": 1 },
|
|
338
|
+
"agents": {
|
|
339
|
+
"type": "array",
|
|
340
|
+
"minItems": 3,
|
|
341
|
+
"items": { "$ref": "#/$defs/triageAgent" }
|
|
342
|
+
},
|
|
343
|
+
"creator": { "$ref": "#/$defs/triageCreator" },
|
|
344
|
+
"categories": {
|
|
345
|
+
"type": "array",
|
|
346
|
+
"items": { "$ref": "#/$defs/triageCategory" }
|
|
347
|
+
},
|
|
348
|
+
"automation": { "$ref": "#/$defs/triageAutomation" },
|
|
349
|
+
"safety": { "$ref": "#/$defs/triageSafety" },
|
|
350
|
+
"concurrency": { "$ref": "#/$defs/triageConcurrency" },
|
|
351
|
+
"prompts": { "$ref": "#/$defs/triagePrompts" },
|
|
352
|
+
"output": { "type": "string" },
|
|
353
|
+
"worktree": { "type": "string" }
|
|
354
|
+
}
|
|
355
|
+
},
|
|
199
356
|
"permissionAction": { "enum": ["allow", "ask", "deny"] },
|
|
200
357
|
"permissionRule": {
|
|
201
358
|
"oneOf": [
|