opencode-magi 0.0.0-dev-20260519013159 → 0.0.0-dev-20260519070815
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 +9 -7
- package/dist/config/output.js +1 -1
- package/dist/config/resolve.js +45 -26
- package/dist/config/validate.js +199 -204
- package/dist/config/worktree.js +1 -1
- package/dist/index.js +1 -1
- package/dist/orchestrator/merge.js +1 -0
- package/dist/orchestrator/review.js +28 -1
- package/dist/prompts/compose.js +1 -5
- package/package.json +1 -1
- package/schema.json +110 -97
package/README.md
CHANGED
|
@@ -61,8 +61,8 @@ 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
|
-
"
|
|
65
|
-
"
|
|
64
|
+
"review": {
|
|
65
|
+
"agents": [
|
|
66
66
|
{
|
|
67
67
|
"account": "your-account-1",
|
|
68
68
|
"model": "openai/gpt-5.5"
|
|
@@ -80,7 +80,7 @@ Add the following content to the configuration file.
|
|
|
80
80
|
}
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
`agents
|
|
83
|
+
`review.agents[].account` is the GitHub account used to post reviews and approvals. Must be authenticated with `gh auth token --user <account>` and must be unique.
|
|
84
84
|
|
|
85
85
|
#### Set project config
|
|
86
86
|
|
|
@@ -101,8 +101,8 @@ Add the following content to the configuration file.
|
|
|
101
101
|
"owner": "your-owner",
|
|
102
102
|
"repo": "your-repo"
|
|
103
103
|
},
|
|
104
|
-
"
|
|
105
|
-
"
|
|
104
|
+
"review": {
|
|
105
|
+
"agents": [
|
|
106
106
|
{
|
|
107
107
|
"account": "your-account-1",
|
|
108
108
|
"model": "openai/gpt-5.5"
|
|
@@ -115,7 +115,9 @@ Add the following content to the configuration file.
|
|
|
115
115
|
"account": "your-account-3",
|
|
116
116
|
"model": "opencode/kimi-k2-6"
|
|
117
117
|
}
|
|
118
|
-
]
|
|
118
|
+
]
|
|
119
|
+
},
|
|
120
|
+
"merge": {
|
|
119
121
|
"editor": {
|
|
120
122
|
"account": "your-editor-account",
|
|
121
123
|
"model": "openai/gpt-5.5",
|
|
@@ -128,7 +130,7 @@ Add the following content to the configuration file.
|
|
|
128
130
|
}
|
|
129
131
|
```
|
|
130
132
|
|
|
131
|
-
`agents
|
|
133
|
+
`review.agents[].account` is the GitHub account used to post reviews and approvals. Must be authenticated with `gh auth token --user <account>` and must be unique. `merge.editor.account` is used by `/magi:merge` to push fixes, close PRs, and merge PRs.
|
|
132
134
|
|
|
133
135
|
#### Validate config
|
|
134
136
|
|
package/dist/config/output.js
CHANGED
|
@@ -6,7 +6,7 @@ function resolvePath(directory, path) {
|
|
|
6
6
|
return isAbsolute(path) ? path : join(directory, path);
|
|
7
7
|
}
|
|
8
8
|
export function outputBaseDir(directory, config, kind) {
|
|
9
|
-
return resolvePath(directory, config.output
|
|
9
|
+
return resolvePath(directory, config.review?.output ?? DEFAULT_OUTPUT_DIRS[kind]);
|
|
10
10
|
}
|
|
11
11
|
export function outputBaseDirs(directory, config) {
|
|
12
12
|
return [outputBaseDir(directory, config, "pr")];
|
package/dist/config/resolve.js
CHANGED
|
@@ -44,20 +44,22 @@ export function mergePermissions(base, override) {
|
|
|
44
44
|
return merged;
|
|
45
45
|
}
|
|
46
46
|
export function resolveReviewerPermission(agents, reviewer) {
|
|
47
|
-
return mergePermissions(mergePermissions(DEFAULT_REVIEWER_PERMISSION, agents.permissions), reviewer.
|
|
47
|
+
return mergePermissions(mergePermissions(DEFAULT_REVIEWER_PERMISSION, agents.permissions), reviewer.permissions);
|
|
48
48
|
}
|
|
49
49
|
export function resolveEditorPermission(agents, editor) {
|
|
50
|
-
return mergePermissions(mergePermissions(DEFAULT_EDITOR_PERMISSION, agents.permissions), editor.
|
|
50
|
+
return mergePermissions(mergePermissions(DEFAULT_EDITOR_PERMISSION, agents.permissions), editor.permissions);
|
|
51
51
|
}
|
|
52
|
-
export function resolveAgents(
|
|
52
|
+
export function resolveAgents(config) {
|
|
53
|
+
const agents = config.agents ?? {};
|
|
54
|
+
const editor = config.merge?.editor;
|
|
53
55
|
return {
|
|
54
|
-
editor:
|
|
56
|
+
editor: editor
|
|
55
57
|
? {
|
|
56
|
-
...
|
|
57
|
-
permission: resolveEditorPermission(agents,
|
|
58
|
+
...editor,
|
|
59
|
+
permission: resolveEditorPermission(agents, editor),
|
|
58
60
|
}
|
|
59
61
|
: undefined,
|
|
60
|
-
reviewers: (agents
|
|
62
|
+
reviewers: (config.review?.agents ?? []).map((reviewer, index) => ({
|
|
61
63
|
...reviewer,
|
|
62
64
|
key: reviewerKey(reviewer, index),
|
|
63
65
|
index,
|
|
@@ -72,20 +74,21 @@ export function resolveRepository(config) {
|
|
|
72
74
|
throw new Error("github.repo is required");
|
|
73
75
|
return {
|
|
74
76
|
alias: config.github.repo,
|
|
75
|
-
agents: resolveAgents(config
|
|
77
|
+
agents: resolveAgents(config),
|
|
76
78
|
automation: {
|
|
77
|
-
close: config.automation?.close ??
|
|
78
|
-
merge: config.automation?.merge ?? true,
|
|
79
|
+
close: config.merge?.automation?.close ?? false,
|
|
80
|
+
merge: config.merge?.automation?.merge ?? true,
|
|
79
81
|
},
|
|
80
82
|
checks: {
|
|
81
|
-
exclude: config.checks?.exclude ?? [],
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
exclude: config.review?.checks?.exclude ?? [],
|
|
84
|
+
retryFailedJobs: config.review?.checks?.retryFailedJobs ?? 3,
|
|
85
|
+
wait: config.review?.checks?.wait ?? true,
|
|
86
|
+
waitAfterEdit: config.merge?.checks?.wait ?? true,
|
|
87
|
+
waitBeforeReview: config.review?.checks?.wait ?? true,
|
|
85
88
|
},
|
|
86
89
|
concurrency: {
|
|
87
|
-
runs: config.concurrency?.runs ?? 3,
|
|
88
|
-
reviewers: config.concurrency?.reviewers ?? 3,
|
|
90
|
+
runs: config.review?.concurrency?.runs ?? 3,
|
|
91
|
+
reviewers: config.review?.concurrency?.reviewers ?? 3,
|
|
89
92
|
},
|
|
90
93
|
github: {
|
|
91
94
|
apiRetryAttempts: config.github.apiRetryAttempts ?? 3,
|
|
@@ -95,19 +98,35 @@ export function resolveRepository(config) {
|
|
|
95
98
|
},
|
|
96
99
|
language: config.language,
|
|
97
100
|
merge: {
|
|
98
|
-
approvalPolicy: config.merge?.approvalPolicy ?? "majority",
|
|
99
|
-
method: config.merge?.method ?? "squash",
|
|
100
|
-
auto: config.merge?.auto ?? true,
|
|
101
|
-
deleteBranch: config.merge?.deleteBranch ?? true,
|
|
102
|
-
|
|
101
|
+
approvalPolicy: config.review?.merge?.approvalPolicy ?? "majority",
|
|
102
|
+
method: config.review?.merge?.method ?? "squash",
|
|
103
|
+
auto: config.review?.merge?.auto ?? true,
|
|
104
|
+
deleteBranch: config.review?.merge?.deleteBranch ?? true,
|
|
105
|
+
queue: config.review?.merge?.queue ?? false,
|
|
106
|
+
mergeQueue: config.review?.merge?.queue ?? false,
|
|
103
107
|
maxThreadResolutionCycles: config.merge?.maxThreadResolutionCycles ?? 5,
|
|
104
108
|
},
|
|
105
|
-
prompts:
|
|
109
|
+
prompts: {
|
|
110
|
+
ciClassification: config.review?.prompts?.ciClassification,
|
|
111
|
+
ciClassificationAfterEdit: config.merge?.prompts?.ciClassification,
|
|
112
|
+
closeReconsideration: config.review?.prompts?.closeReconsideration,
|
|
113
|
+
edit: config.merge?.prompts?.edit,
|
|
114
|
+
editGuidelines: config.merge?.prompts?.editGuidelines,
|
|
115
|
+
findingValidation: config.review?.prompts?.findingValidation,
|
|
116
|
+
rereview: config.review?.prompts?.rereview,
|
|
117
|
+
rereviewCloseReconsideration: config.review?.prompts?.closeReconsideration,
|
|
118
|
+
review: config.review?.prompts?.review,
|
|
119
|
+
reviewGuidelines: config.review?.prompts?.reviewGuidelines,
|
|
120
|
+
},
|
|
121
|
+
reviewAutomation: {
|
|
122
|
+
close: config.review?.automation?.close ?? false,
|
|
123
|
+
merge: config.review?.automation?.merge ?? false,
|
|
124
|
+
},
|
|
106
125
|
safety: {
|
|
107
|
-
allowAuthors: config.safety?.allowAuthors ?? [],
|
|
108
|
-
blockedPaths: config.safety?.blockedPaths ?? [],
|
|
109
|
-
maxChangedFiles: config.safety?.maxChangedFiles,
|
|
110
|
-
requiredLabels: config.safety?.requiredLabels ?? [],
|
|
126
|
+
allowAuthors: config.review?.safety?.allowAuthors ?? [],
|
|
127
|
+
blockedPaths: config.review?.safety?.blockedPaths ?? [],
|
|
128
|
+
maxChangedFiles: config.review?.safety?.maxChangedFiles,
|
|
129
|
+
requiredLabels: config.review?.safety?.requiredLabels ?? [],
|
|
111
130
|
},
|
|
112
131
|
};
|
|
113
132
|
}
|
package/dist/config/validate.js
CHANGED
|
@@ -12,25 +12,20 @@ const validateSchema = AJV.compile(schema);
|
|
|
12
12
|
const CONFIG_KEYS = new Set([
|
|
13
13
|
"$schema",
|
|
14
14
|
"agents",
|
|
15
|
-
"automation",
|
|
16
15
|
"clear",
|
|
17
|
-
"checks",
|
|
18
|
-
"concurrency",
|
|
19
16
|
"github",
|
|
20
17
|
"language",
|
|
21
18
|
"merge",
|
|
22
19
|
"output",
|
|
23
|
-
"
|
|
24
|
-
"safety",
|
|
25
|
-
"worktree",
|
|
20
|
+
"review",
|
|
26
21
|
]);
|
|
27
|
-
const AGENTS_KEYS = new Set(["
|
|
22
|
+
const AGENTS_KEYS = new Set(["permissions"]);
|
|
28
23
|
const REVIEWER_KEYS = new Set([
|
|
29
24
|
"account",
|
|
30
25
|
"id",
|
|
31
26
|
"model",
|
|
32
27
|
"options",
|
|
33
|
-
"
|
|
28
|
+
"permissions",
|
|
34
29
|
"persona",
|
|
35
30
|
]);
|
|
36
31
|
const EDITOR_KEYS = new Set([
|
|
@@ -38,51 +33,61 @@ const EDITOR_KEYS = new Set([
|
|
|
38
33
|
"author",
|
|
39
34
|
"model",
|
|
40
35
|
"options",
|
|
41
|
-
"
|
|
36
|
+
"permissions",
|
|
42
37
|
"persona",
|
|
43
38
|
]);
|
|
44
39
|
const AUTHOR_KEYS = new Set(["email", "name"]);
|
|
45
40
|
const GITHUB_KEYS = new Set(["apiRetryAttempts", "host", "owner", "repo"]);
|
|
41
|
+
const REVIEW_KEYS = new Set([
|
|
42
|
+
"agents",
|
|
43
|
+
"automation",
|
|
44
|
+
"checks",
|
|
45
|
+
"concurrency",
|
|
46
|
+
"merge",
|
|
47
|
+
"output",
|
|
48
|
+
"prompts",
|
|
49
|
+
"safety",
|
|
50
|
+
"worktree",
|
|
51
|
+
]);
|
|
46
52
|
const MERGE_KEYS = new Set([
|
|
53
|
+
"automation",
|
|
54
|
+
"checks",
|
|
55
|
+
"editor",
|
|
56
|
+
"maxThreadResolutionCycles",
|
|
57
|
+
"prompts",
|
|
58
|
+
]);
|
|
59
|
+
const REVIEW_MERGE_KEYS = new Set([
|
|
47
60
|
"approvalPolicy",
|
|
48
61
|
"auto",
|
|
49
62
|
"deleteBranch",
|
|
50
|
-
"maxThreadResolutionCycles",
|
|
51
|
-
"mergeQueue",
|
|
52
63
|
"method",
|
|
64
|
+
"queue",
|
|
53
65
|
]);
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
"retryFailedJobs",
|
|
57
|
-
"waitAfterEdit",
|
|
58
|
-
"waitBeforeReview",
|
|
59
|
-
]);
|
|
66
|
+
const REVIEW_CHECKS_KEYS = new Set(["exclude", "retryFailedJobs", "wait"]);
|
|
67
|
+
const MERGE_CHECKS_KEYS = new Set(["wait"]);
|
|
60
68
|
const AUTOMATION_KEYS = new Set(["close", "merge"]);
|
|
61
69
|
const CLEAR_KEYS = new Set(["branch", "output", "session", "worktree"]);
|
|
62
70
|
const CONCURRENCY_KEYS = new Set(["reviewers", "runs"]);
|
|
63
|
-
const OUTPUT_KEYS = new Set(["
|
|
64
|
-
const OUTPUT_DIR_KEYS = new Set(["pr"]);
|
|
65
|
-
const WORKTREE_KEYS = new Set(["dirs"]);
|
|
66
|
-
const WORKTREE_DIR_KEYS = new Set(["pr"]);
|
|
71
|
+
const OUTPUT_KEYS = new Set(["repairAttempts"]);
|
|
67
72
|
const SAFETY_KEYS = new Set([
|
|
68
73
|
"allowAuthors",
|
|
69
74
|
"blockedPaths",
|
|
70
75
|
"maxChangedFiles",
|
|
71
76
|
"requiredLabels",
|
|
72
77
|
]);
|
|
73
|
-
const
|
|
78
|
+
const REVIEW_PROMPT_KEYS = new Set([
|
|
74
79
|
"ciClassification",
|
|
75
|
-
"ciClassificationAfterEdit",
|
|
76
80
|
"closeReconsideration",
|
|
77
|
-
"edit",
|
|
78
|
-
"editGuidelines",
|
|
79
81
|
"findingValidation",
|
|
80
|
-
"report",
|
|
81
82
|
"rereview",
|
|
82
|
-
"rereviewCloseReconsideration",
|
|
83
83
|
"review",
|
|
84
84
|
"reviewGuidelines",
|
|
85
85
|
]);
|
|
86
|
+
const MERGE_PROMPT_KEYS = new Set([
|
|
87
|
+
"ciClassification",
|
|
88
|
+
"edit",
|
|
89
|
+
"editGuidelines",
|
|
90
|
+
]);
|
|
86
91
|
function githubHost(config) {
|
|
87
92
|
return config.github?.host ?? "github.com";
|
|
88
93
|
}
|
|
@@ -211,7 +216,7 @@ function validateReviewerList(reviewers, path, errors, catalog) {
|
|
|
211
216
|
validateString(reviewer.persona, `${path}[${index}].persona`, errors);
|
|
212
217
|
if (reviewer.options != null && !isPlainObject(reviewer.options))
|
|
213
218
|
errors.push(`${path}[${index}].options must be an object`);
|
|
214
|
-
validatePermissionConfig(reviewer.
|
|
219
|
+
validatePermissionConfig(reviewer.permissions, `${path}[${index}].permissions`, errors);
|
|
215
220
|
if (reviewer.id) {
|
|
216
221
|
if (!validateReviewerId(reviewer.id)) {
|
|
217
222
|
errors.push(`${path}[${index}].id may contain only letters, numbers, underscores, and hyphens`);
|
|
@@ -234,7 +239,51 @@ function validateResolvedReviewers(reviewers, path, errors) {
|
|
|
234
239
|
accounts.add(reviewer.account);
|
|
235
240
|
}
|
|
236
241
|
}
|
|
242
|
+
function validateEditor(editor, path, errors, catalog) {
|
|
243
|
+
if (!editor)
|
|
244
|
+
return;
|
|
245
|
+
if (!isPlainObject(editor)) {
|
|
246
|
+
errors.push(`${path} must be an object`);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (!editor.model)
|
|
250
|
+
errors.push(`${path}.model is required`);
|
|
251
|
+
validateKnownKeys(editor, path, EDITOR_KEYS, errors);
|
|
252
|
+
validateString(editor.model, `${path}.model`, errors);
|
|
253
|
+
validateString(editor.account, `${path}.account`, errors);
|
|
254
|
+
validateString(editor.persona, `${path}.persona`, errors);
|
|
255
|
+
validateModel(editor.model, `${path}.model`, errors, catalog);
|
|
256
|
+
if (!editor.account)
|
|
257
|
+
errors.push(`${path}.account is required`);
|
|
258
|
+
if (editor.options != null && !isPlainObject(editor.options)) {
|
|
259
|
+
errors.push(`${path}.options must be an object`);
|
|
260
|
+
}
|
|
261
|
+
validatePermissionConfig(editor.permissions, `${path}.permissions`, errors);
|
|
262
|
+
const author = editor.author;
|
|
263
|
+
if (!author || !isPlainObject(author)) {
|
|
264
|
+
if (author != null)
|
|
265
|
+
errors.push(`${path}.author must be an object`);
|
|
266
|
+
errors.push(`${path}.author.name is required`);
|
|
267
|
+
errors.push(`${path}.author.email is required`);
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
validateKnownKeys(author, `${path}.author`, AUTHOR_KEYS, errors);
|
|
271
|
+
if (!author.name) {
|
|
272
|
+
errors.push(`${path}.author.name is required`);
|
|
273
|
+
}
|
|
274
|
+
else if (typeof author.name !== "string") {
|
|
275
|
+
errors.push(`${path}.author.name must be a string`);
|
|
276
|
+
}
|
|
277
|
+
if (!author.email) {
|
|
278
|
+
errors.push(`${path}.author.email is required`);
|
|
279
|
+
}
|
|
280
|
+
else if (typeof author.email !== "string") {
|
|
281
|
+
errors.push(`${path}.author.email must be a string`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
237
285
|
function validateMerge(config, errors, options) {
|
|
286
|
+
const merge = config.merge;
|
|
238
287
|
if (options.requireGithub ?? true) {
|
|
239
288
|
if (!config.github?.owner)
|
|
240
289
|
errors.push("github.owner is required");
|
|
@@ -248,102 +297,102 @@ function validateMerge(config, errors, options) {
|
|
|
248
297
|
if (config.github != null && !isPlainObject(config.github)) {
|
|
249
298
|
errors.push("github must be an object");
|
|
250
299
|
}
|
|
251
|
-
if (config.merge != null && !isPlainObject(config.merge)) {
|
|
252
|
-
errors.push("merge must be an object");
|
|
253
|
-
}
|
|
254
|
-
validateKnownKeys(config.merge, "merge", MERGE_KEYS, errors);
|
|
255
|
-
validateBoolean(config.merge?.auto, "merge.auto", errors);
|
|
256
|
-
validateBoolean(config.merge?.deleteBranch, "merge.deleteBranch", errors);
|
|
257
|
-
validateBoolean(config.merge?.mergeQueue, "merge.mergeQueue", errors);
|
|
258
300
|
if (config.github?.apiRetryAttempts != null &&
|
|
259
301
|
(typeof config.github.apiRetryAttempts !== "number" ||
|
|
260
302
|
!Number.isInteger(config.github.apiRetryAttempts) ||
|
|
261
303
|
config.github.apiRetryAttempts < 0)) {
|
|
262
304
|
errors.push("github.apiRetryAttempts must be a non-negative integer");
|
|
263
305
|
}
|
|
264
|
-
if (
|
|
265
|
-
(
|
|
266
|
-
!["merge", "rebase", "squash"].includes(config.merge.method))) {
|
|
267
|
-
errors.push("merge.method must be merge, squash, or rebase");
|
|
268
|
-
}
|
|
269
|
-
if (config.merge?.approvalPolicy != null &&
|
|
270
|
-
(typeof config.merge.approvalPolicy !== "string" ||
|
|
271
|
-
!["majority", "unanimous"].includes(config.merge.approvalPolicy))) {
|
|
272
|
-
errors.push("merge.approvalPolicy must be majority or unanimous");
|
|
306
|
+
if (merge != null && !isPlainObject(merge)) {
|
|
307
|
+
errors.push("merge must be an object");
|
|
273
308
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
309
|
+
validateKnownKeys(merge, "merge", MERGE_KEYS, errors);
|
|
310
|
+
validateBooleanObject(merge?.automation, "merge.automation", AUTOMATION_KEYS, errors);
|
|
311
|
+
const checks = merge?.checks;
|
|
312
|
+
validateKnownKeys(checks, "merge.checks", MERGE_CHECKS_KEYS, errors);
|
|
313
|
+
validateBoolean(checks?.wait, "merge.checks.wait", errors);
|
|
314
|
+
validateEditor(merge?.editor, "merge.editor", errors, options.modelCatalog);
|
|
315
|
+
if (merge?.maxThreadResolutionCycles != null &&
|
|
316
|
+
(typeof merge.maxThreadResolutionCycles !== "number" ||
|
|
317
|
+
!Number.isInteger(merge.maxThreadResolutionCycles) ||
|
|
318
|
+
merge.maxThreadResolutionCycles < 0)) {
|
|
278
319
|
errors.push("merge.maxThreadResolutionCycles must be a non-negative integer");
|
|
279
320
|
}
|
|
321
|
+
if (options.requireEditor && !merge?.editor)
|
|
322
|
+
errors.push("merge.editor is required");
|
|
323
|
+
}
|
|
324
|
+
function validateReviewMerge(config, errors) {
|
|
325
|
+
const merge = config.review?.merge;
|
|
326
|
+
if (merge != null && !isPlainObject(merge)) {
|
|
327
|
+
errors.push("review.merge must be an object");
|
|
328
|
+
}
|
|
329
|
+
validateKnownKeys(merge, "review.merge", REVIEW_MERGE_KEYS, errors);
|
|
330
|
+
validateBoolean(merge?.auto, "review.merge.auto", errors);
|
|
331
|
+
validateBoolean(merge?.deleteBranch, "review.merge.deleteBranch", errors);
|
|
332
|
+
validateBoolean(merge?.queue, "review.merge.queue", errors);
|
|
333
|
+
if (merge?.method != null &&
|
|
334
|
+
(typeof merge.method !== "string" ||
|
|
335
|
+
!["merge", "rebase", "squash"].includes(merge.method))) {
|
|
336
|
+
errors.push("review.merge.method must be merge, squash, or rebase");
|
|
337
|
+
}
|
|
338
|
+
if (merge?.approvalPolicy != null &&
|
|
339
|
+
(typeof merge.approvalPolicy !== "string" ||
|
|
340
|
+
!["majority", "unanimous"].includes(merge.approvalPolicy))) {
|
|
341
|
+
errors.push("review.merge.approvalPolicy must be majority or unanimous");
|
|
342
|
+
}
|
|
280
343
|
}
|
|
281
344
|
function validateConcurrency(config, errors) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
345
|
+
const concurrency = config.review?.concurrency;
|
|
346
|
+
if (concurrency != null && !isPlainObject(concurrency)) {
|
|
347
|
+
errors.push("review.concurrency must be an object");
|
|
348
|
+
}
|
|
349
|
+
validateKnownKeys(concurrency, "review.concurrency", CONCURRENCY_KEYS, errors);
|
|
350
|
+
if (concurrency?.runs != null) {
|
|
351
|
+
if (typeof concurrency.runs !== "number" ||
|
|
352
|
+
!Number.isInteger(concurrency.runs) ||
|
|
353
|
+
concurrency.runs < 1) {
|
|
354
|
+
errors.push("review.concurrency.runs must be a positive integer");
|
|
291
355
|
}
|
|
292
356
|
}
|
|
293
|
-
if (
|
|
294
|
-
if (typeof
|
|
295
|
-
!Number.isInteger(
|
|
296
|
-
|
|
297
|
-
errors.push("concurrency.reviewers must be a positive integer");
|
|
357
|
+
if (concurrency?.reviewers != null) {
|
|
358
|
+
if (typeof concurrency.reviewers !== "number" ||
|
|
359
|
+
!Number.isInteger(concurrency.reviewers) ||
|
|
360
|
+
concurrency.reviewers < 1) {
|
|
361
|
+
errors.push("review.concurrency.reviewers must be a positive integer");
|
|
298
362
|
}
|
|
299
363
|
}
|
|
300
364
|
}
|
|
301
365
|
function validateAutomation(config, errors) {
|
|
302
|
-
|
|
303
|
-
errors.push("automation must be an object");
|
|
304
|
-
}
|
|
305
|
-
validateKnownKeys(config.automation, "automation", AUTOMATION_KEYS, errors);
|
|
306
|
-
if (config.automation?.merge != null &&
|
|
307
|
-
typeof config.automation.merge !== "boolean") {
|
|
308
|
-
errors.push("automation.merge must be a boolean");
|
|
309
|
-
}
|
|
310
|
-
if (config.automation?.close != null &&
|
|
311
|
-
typeof config.automation.close !== "boolean") {
|
|
312
|
-
errors.push("automation.close must be a boolean");
|
|
313
|
-
}
|
|
366
|
+
validateBooleanObject(config.review?.automation, "review.automation", AUTOMATION_KEYS, errors);
|
|
314
367
|
}
|
|
315
368
|
function validateClear(config, errors) {
|
|
316
369
|
validateBooleanObject(config.clear, "clear", CLEAR_KEYS, errors);
|
|
317
370
|
}
|
|
318
371
|
function validateChecks(config, errors) {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
372
|
+
const checks = config.review?.checks;
|
|
373
|
+
if (checks != null && !isPlainObject(checks)) {
|
|
374
|
+
errors.push("review.checks must be an object");
|
|
375
|
+
}
|
|
376
|
+
validateKnownKeys(checks, "review.checks", REVIEW_CHECKS_KEYS, errors);
|
|
377
|
+
if (checks?.exclude != null) {
|
|
378
|
+
if (!Array.isArray(checks.exclude)) {
|
|
379
|
+
errors.push("review.checks.exclude must be an array");
|
|
326
380
|
}
|
|
327
381
|
else {
|
|
328
|
-
|
|
382
|
+
checks.exclude.forEach((item, index) => {
|
|
329
383
|
if (typeof item !== "string")
|
|
330
|
-
errors.push(`checks.exclude[${index}] must be a string`);
|
|
384
|
+
errors.push(`review.checks.exclude[${index}] must be a string`);
|
|
331
385
|
});
|
|
332
386
|
}
|
|
333
387
|
}
|
|
334
|
-
if (
|
|
335
|
-
|
|
336
|
-
errors.push("checks.waitBeforeReview must be a boolean");
|
|
388
|
+
if (checks?.wait != null && typeof checks.wait !== "boolean") {
|
|
389
|
+
errors.push("review.checks.wait must be a boolean");
|
|
337
390
|
}
|
|
338
|
-
if (
|
|
339
|
-
typeof
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
(typeof config.checks.retryFailedJobs !== "number" ||
|
|
344
|
-
!Number.isInteger(config.checks.retryFailedJobs) ||
|
|
345
|
-
config.checks.retryFailedJobs < 0)) {
|
|
346
|
-
errors.push("checks.retryFailedJobs must be a non-negative integer");
|
|
391
|
+
if (checks?.retryFailedJobs != null &&
|
|
392
|
+
(typeof checks.retryFailedJobs !== "number" ||
|
|
393
|
+
!Number.isInteger(checks.retryFailedJobs) ||
|
|
394
|
+
checks.retryFailedJobs < 0)) {
|
|
395
|
+
errors.push("review.checks.retryFailedJobs must be a non-negative integer");
|
|
347
396
|
}
|
|
348
397
|
}
|
|
349
398
|
function validateStringArray(value, path, errors) {
|
|
@@ -359,33 +408,44 @@ function validateStringArray(value, path, errors) {
|
|
|
359
408
|
});
|
|
360
409
|
}
|
|
361
410
|
function validateSafety(config, errors) {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
validateStringArray(
|
|
368
|
-
validateStringArray(
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
411
|
+
const safety = config.review?.safety;
|
|
412
|
+
if (safety != null && !isPlainObject(safety)) {
|
|
413
|
+
errors.push("review.safety must be an object");
|
|
414
|
+
}
|
|
415
|
+
validateKnownKeys(safety, "review.safety", SAFETY_KEYS, errors);
|
|
416
|
+
validateStringArray(safety?.allowAuthors, "review.safety.allowAuthors", errors);
|
|
417
|
+
validateStringArray(safety?.blockedPaths, "review.safety.blockedPaths", errors);
|
|
418
|
+
validateStringArray(safety?.requiredLabels, "review.safety.requiredLabels", errors);
|
|
419
|
+
if (safety?.maxChangedFiles != null &&
|
|
420
|
+
(typeof safety.maxChangedFiles !== "number" ||
|
|
421
|
+
!Number.isInteger(safety.maxChangedFiles) ||
|
|
422
|
+
safety.maxChangedFiles < 0)) {
|
|
423
|
+
errors.push("review.safety.maxChangedFiles must be a non-negative integer");
|
|
374
424
|
}
|
|
375
425
|
}
|
|
376
|
-
|
|
377
|
-
if (
|
|
426
|
+
function validatePromptObject(prompts, path, keys, errors) {
|
|
427
|
+
if (prompts == null)
|
|
378
428
|
return;
|
|
379
|
-
if (!isPlainObject(
|
|
380
|
-
errors.push(
|
|
429
|
+
if (!isPlainObject(prompts)) {
|
|
430
|
+
errors.push(`${path} must be an object`);
|
|
381
431
|
return;
|
|
382
432
|
}
|
|
383
|
-
validateKnownKeys(
|
|
384
|
-
|
|
385
|
-
if (typeof value !== "string")
|
|
386
|
-
errors.push(
|
|
433
|
+
validateKnownKeys(prompts, path, keys, errors);
|
|
434
|
+
for (const [key, value] of Object.entries(prompts)) {
|
|
435
|
+
if (typeof value !== "string")
|
|
436
|
+
errors.push(`${path}.${key} must be a string`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
async function validatePrompts(config, errors, directory) {
|
|
440
|
+
validatePromptObject(config.review?.prompts, "review.prompts", REVIEW_PROMPT_KEYS, errors);
|
|
441
|
+
validatePromptObject(config.merge?.prompts, "merge.prompts", MERGE_PROMPT_KEYS, errors);
|
|
442
|
+
const promptEntries = [
|
|
443
|
+
...Object.entries(config.review?.prompts ?? {}).map(([key, value]) => [`review.prompts.${key}`, value]),
|
|
444
|
+
...Object.entries(config.merge?.prompts ?? {}).map(([key, value]) => [`merge.prompts.${key}`, value]),
|
|
445
|
+
];
|
|
446
|
+
await Promise.all(promptEntries.map(async ([path, value]) => {
|
|
447
|
+
if (typeof value !== "string")
|
|
387
448
|
return;
|
|
388
|
-
}
|
|
389
449
|
if (!directory)
|
|
390
450
|
return;
|
|
391
451
|
const fullPath = promptPath(directory, value);
|
|
@@ -393,13 +453,13 @@ async function validatePrompts(config, errors, directory) {
|
|
|
393
453
|
await access(fullPath, constants.R_OK);
|
|
394
454
|
}
|
|
395
455
|
catch {
|
|
396
|
-
errors.push(
|
|
456
|
+
errors.push(`${path} file is not readable: ${value}`);
|
|
397
457
|
}
|
|
398
458
|
}));
|
|
399
459
|
}
|
|
400
460
|
async function validateAuth(config, exec, errors) {
|
|
401
461
|
const accounts = new Set();
|
|
402
|
-
const agents = resolveAgents(config
|
|
462
|
+
const agents = resolveAgents(config);
|
|
403
463
|
for (const reviewer of agents.reviewers)
|
|
404
464
|
accounts.add(reviewer.account);
|
|
405
465
|
if (agents.editor)
|
|
@@ -421,7 +481,7 @@ async function fetchPermissions(config, exec, account) {
|
|
|
421
481
|
async function validateRepositoryPermissions(config, exec, errors, warnings) {
|
|
422
482
|
if (!config.github?.owner || !config.github.repo)
|
|
423
483
|
return;
|
|
424
|
-
const agents = resolveAgents(config
|
|
484
|
+
const agents = resolveAgents(config);
|
|
425
485
|
await Promise.all(agents.reviewers.map(async (reviewer) => {
|
|
426
486
|
try {
|
|
427
487
|
const permissions = await fetchPermissions(config, exec, reviewer.account);
|
|
@@ -455,65 +515,32 @@ export async function validateConfig(config, options = {}) {
|
|
|
455
515
|
validateKnownKeys(config, "config", CONFIG_KEYS, errors);
|
|
456
516
|
validateString(config.$schema, "$schema", errors);
|
|
457
517
|
validateString(config.language, "language", errors);
|
|
458
|
-
if (!config.agents) {
|
|
459
|
-
errors.push("agents
|
|
518
|
+
if (config.agents != null && !isPlainObject(config.agents)) {
|
|
519
|
+
errors.push("agents must be an object");
|
|
460
520
|
}
|
|
461
521
|
else {
|
|
462
|
-
|
|
463
|
-
|
|
522
|
+
validateKnownKeys(config.agents, "agents", AGENTS_KEYS, errors);
|
|
523
|
+
validatePermissionConfig(config.agents?.permissions, "agents.permissions", errors);
|
|
524
|
+
}
|
|
525
|
+
if (!config.review) {
|
|
526
|
+
errors.push("review is required");
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
if (!isPlainObject(config.review)) {
|
|
530
|
+
errors.push("review must be an object");
|
|
464
531
|
}
|
|
465
532
|
else {
|
|
466
|
-
validateKnownKeys(config.
|
|
467
|
-
}
|
|
468
|
-
validatePermissionConfig(config.agents.permissions, "agents.permissions", errors);
|
|
469
|
-
if (!config.agents.reviewers)
|
|
470
|
-
errors.push("agents.reviewers is required");
|
|
471
|
-
validateReviewerList(config.agents.reviewers, "agents.reviewers", errors, options.modelCatalog);
|
|
472
|
-
if (options.requireEditor && !config.agents.editor)
|
|
473
|
-
errors.push("agents.editor is required");
|
|
474
|
-
if (config.agents.editor) {
|
|
475
|
-
if (!config.agents.editor.model)
|
|
476
|
-
errors.push("agents.editor.model is required");
|
|
477
|
-
validateKnownKeys(config.agents.editor, "agents.editor", EDITOR_KEYS, errors);
|
|
478
|
-
validateString(config.agents.editor.model, "agents.editor.model", errors);
|
|
479
|
-
validateString(config.agents.editor.account, "agents.editor.account", errors);
|
|
480
|
-
validateString(config.agents.editor.persona, "agents.editor.persona", errors);
|
|
481
|
-
validateModel(config.agents.editor.model, "agents.editor.model", errors, options.modelCatalog);
|
|
482
|
-
if (!config.agents.editor.account)
|
|
483
|
-
errors.push("agents.editor.account is required");
|
|
484
|
-
if (config.agents.editor.options != null &&
|
|
485
|
-
!isPlainObject(config.agents.editor.options)) {
|
|
486
|
-
errors.push("agents.editor.options must be an object");
|
|
487
|
-
}
|
|
488
|
-
validatePermissionConfig(config.agents.editor.permission, "agents.editor.permission", errors);
|
|
489
|
-
const author = config.agents.editor.author;
|
|
490
|
-
if (!author || !isPlainObject(author)) {
|
|
491
|
-
if (author != null)
|
|
492
|
-
errors.push("agents.editor.author must be an object");
|
|
493
|
-
errors.push("agents.editor.author.name is required");
|
|
494
|
-
errors.push("agents.editor.author.email is required");
|
|
495
|
-
}
|
|
496
|
-
else {
|
|
497
|
-
validateKnownKeys(author, "agents.editor.author", AUTHOR_KEYS, errors);
|
|
498
|
-
if (!author.name) {
|
|
499
|
-
errors.push("agents.editor.author.name is required");
|
|
500
|
-
}
|
|
501
|
-
else if (typeof author.name !== "string") {
|
|
502
|
-
errors.push("agents.editor.author.name must be a string");
|
|
503
|
-
}
|
|
504
|
-
if (!author.email) {
|
|
505
|
-
errors.push("agents.editor.author.email is required");
|
|
506
|
-
}
|
|
507
|
-
else if (typeof author.email !== "string") {
|
|
508
|
-
errors.push("agents.editor.author.email must be a string");
|
|
509
|
-
}
|
|
510
|
-
}
|
|
533
|
+
validateKnownKeys(config.review, "review", REVIEW_KEYS, errors);
|
|
511
534
|
}
|
|
512
|
-
if (
|
|
513
|
-
|
|
535
|
+
if (!config.review.agents)
|
|
536
|
+
errors.push("review.agents is required");
|
|
537
|
+
validateReviewerList(config.review.agents, "review.agents", errors, options.modelCatalog);
|
|
538
|
+
if (Array.isArray(config.review.agents)) {
|
|
539
|
+
validateResolvedReviewers(resolveAgents(config).reviewers, "review.resolvedAgents", errors);
|
|
514
540
|
}
|
|
515
541
|
}
|
|
516
542
|
validateMerge(config, errors, options);
|
|
543
|
+
validateReviewMerge(config, errors);
|
|
517
544
|
validateAutomation(config, errors);
|
|
518
545
|
validateClear(config, errors);
|
|
519
546
|
validateChecks(config, errors);
|
|
@@ -531,40 +558,8 @@ export async function validateConfig(config, options = {}) {
|
|
|
531
558
|
errors.push("output.repairAttempts must be a non-negative integer");
|
|
532
559
|
}
|
|
533
560
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
errors.push("output.dirs must be an object");
|
|
537
|
-
}
|
|
538
|
-
else {
|
|
539
|
-
validateKnownKeys(config.output.dirs, "output.dirs", OUTPUT_DIR_KEYS, errors);
|
|
540
|
-
const dirs = config.output.dirs;
|
|
541
|
-
for (const key of OUTPUT_DIR_KEYS) {
|
|
542
|
-
const value = dirs[key];
|
|
543
|
-
if (value != null && typeof value !== "string") {
|
|
544
|
-
errors.push(`output.dirs.${key} must be a string`);
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
if (config.worktree != null && !isPlainObject(config.worktree)) {
|
|
550
|
-
errors.push("worktree must be an object");
|
|
551
|
-
}
|
|
552
|
-
validateKnownKeys(config.worktree, "worktree", WORKTREE_KEYS, errors);
|
|
553
|
-
if (config.worktree?.dirs != null) {
|
|
554
|
-
if (!isPlainObject(config.worktree.dirs)) {
|
|
555
|
-
errors.push("worktree.dirs must be an object");
|
|
556
|
-
}
|
|
557
|
-
else {
|
|
558
|
-
validateKnownKeys(config.worktree.dirs, "worktree.dirs", WORKTREE_DIR_KEYS, errors);
|
|
559
|
-
const dirs = config.worktree.dirs;
|
|
560
|
-
for (const key of WORKTREE_DIR_KEYS) {
|
|
561
|
-
const value = dirs[key];
|
|
562
|
-
if (value != null && typeof value !== "string") {
|
|
563
|
-
errors.push(`worktree.dirs.${key} must be a string`);
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
}
|
|
561
|
+
validateString(config.review?.output, "review.output", errors);
|
|
562
|
+
validateString(config.review?.worktree, "review.worktree", errors);
|
|
568
563
|
if (options.checkAuth && !errors.length) {
|
|
569
564
|
if (!options.exec) {
|
|
570
565
|
errors.push("validateConfig requires exec when checkAuth is true");
|
package/dist/config/worktree.js
CHANGED
|
@@ -6,7 +6,7 @@ function resolvePath(directory, path) {
|
|
|
6
6
|
return isAbsolute(path) ? path : join(directory, path);
|
|
7
7
|
}
|
|
8
8
|
export function worktreeBaseDir(directory, config, kind) {
|
|
9
|
-
return resolvePath(directory, config.worktree
|
|
9
|
+
return resolvePath(directory, config.review?.worktree ?? DEFAULT_WORKTREE_DIRS[kind]);
|
|
10
10
|
}
|
|
11
11
|
export function worktreeBaseDirs(directory, config = {}) {
|
|
12
12
|
return [worktreeBaseDir(directory, config, "pr")];
|
package/dist/index.js
CHANGED
|
@@ -190,7 +190,7 @@ export async function validateMagiConfigFiles(directory, options = {}) {
|
|
|
190
190
|
? withGitHubApiRetry(options.exec, mergedConfig.github?.apiRetryAttempts ?? 3)
|
|
191
191
|
: undefined,
|
|
192
192
|
modelCatalog: options.modelCatalog,
|
|
193
|
-
requireGithub: hasProjectConfig && Boolean(mergedConfig.agents
|
|
193
|
+
requireGithub: hasProjectConfig && Boolean(mergedConfig.review?.agents),
|
|
194
194
|
});
|
|
195
195
|
loadedFrom = existing.map((status) => status.path).join(", ");
|
|
196
196
|
errors.push(...validation.errors);
|
|
@@ -571,6 +571,7 @@ export async function runMerge(input) {
|
|
|
571
571
|
...abortableInput,
|
|
572
572
|
allowAlreadyReviewed: true,
|
|
573
573
|
approvalPolicy: input.repository.merge.approvalPolicy,
|
|
574
|
+
enableReviewAutomation: false,
|
|
574
575
|
onProgress: (progress) => input.onProgress?.(progress),
|
|
575
576
|
runId: input.runId,
|
|
576
577
|
dryRun: input.dryRun,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { createWorktree, fetchPullRequest, fetchPullRequestCommits, fetchPullRequestReviews, fetchUnresolvedThreads, postApproval, postChangesRequested, postCloseComment, postReply, removeWorktree, resolveThread, } from "../github/commands";
|
|
3
|
+
import { createWorktree, fetchPullRequest, fetchPullRequestCommits, fetchPullRequestReviews, fetchUnresolvedThreads, closePullRequest, mergePullRequest, postApproval, postChangesRequested, postCloseComment, postReply, removeWorktree, resolveThread, } from "../github/commands";
|
|
4
4
|
import { composeFindingValidationPrompt, composeCloseReconsiderationPrompt, composeRereviewPrompt, composeReviewPrompt, } from "../prompts/compose";
|
|
5
5
|
import { prRunOutputDir } from "../config/output";
|
|
6
6
|
import { worktreeBaseDir } from "../config/worktree";
|
|
@@ -112,6 +112,9 @@ function reviewStateToVerdict(state) {
|
|
|
112
112
|
return "CHANGES_REQUESTED";
|
|
113
113
|
return "CLOSE";
|
|
114
114
|
}
|
|
115
|
+
function hasBlockingCiReports(reports) {
|
|
116
|
+
return reports.some((report) => report.scopeInside.length || report.scopeOutsideUnresolved.length);
|
|
117
|
+
}
|
|
115
118
|
function previousReviewText(review) {
|
|
116
119
|
return JSON.stringify({
|
|
117
120
|
body: review.body ?? "",
|
|
@@ -753,6 +756,30 @@ export async function runReview(input) {
|
|
|
753
756
|
: await postReviewOutput({ ...input, exec }, key, output),
|
|
754
757
|
]))),
|
|
755
758
|
};
|
|
759
|
+
const automationAccount = input.repository.agents.reviewers[0]?.account;
|
|
760
|
+
const enableReviewAutomation = input.enableReviewAutomation ?? true;
|
|
761
|
+
if (enableReviewAutomation &&
|
|
762
|
+
verdict === "MERGE" &&
|
|
763
|
+
input.repository.reviewAutomation?.merge) {
|
|
764
|
+
await input.onProgress?.({ phase: "merging PR", type: "phase" });
|
|
765
|
+
posted.automation = hasBlockingCiReports(ciReports)
|
|
766
|
+
? "skipped: unresolved CI"
|
|
767
|
+
: input.dryRun
|
|
768
|
+
? "dry-run:would-merge"
|
|
769
|
+
: automationAccount
|
|
770
|
+
? await mergePullRequest(input.exec, input.repository, input.pr, automationAccount)
|
|
771
|
+
: "skipped: no review automation account";
|
|
772
|
+
}
|
|
773
|
+
if (enableReviewAutomation &&
|
|
774
|
+
verdict === "CLOSE" &&
|
|
775
|
+
input.repository.reviewAutomation?.close) {
|
|
776
|
+
await input.onProgress?.({ phase: "closing PR", type: "phase" });
|
|
777
|
+
posted.automation = input.dryRun
|
|
778
|
+
? "dry-run:would-close"
|
|
779
|
+
: automationAccount
|
|
780
|
+
? await closePullRequest(input.exec, input.repository, input.pr, automationAccount)
|
|
781
|
+
: "skipped: no review automation account";
|
|
782
|
+
}
|
|
756
783
|
await writeFile(join(outputDir, "majority.json"), JSON.stringify({
|
|
757
784
|
approvalPolicy: input.approvalPolicy ?? "majority",
|
|
758
785
|
verdict,
|
package/dist/prompts/compose.js
CHANGED
|
@@ -34,7 +34,7 @@ function repositoryValues(repository) {
|
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
function reviewValues(input) {
|
|
37
|
-
const ciFailureContext = input.ciFailureContext?.trim() ??
|
|
37
|
+
const ciFailureContext = input.ciFailureContext?.trim() ?? "";
|
|
38
38
|
return {
|
|
39
39
|
...repositoryValues(input.repository),
|
|
40
40
|
baseSha: input.baseSha,
|
|
@@ -42,10 +42,6 @@ function reviewValues(input) {
|
|
|
42
42
|
ciFailureContextBlock: ciFailureContext
|
|
43
43
|
? `<ci_failure_context>\n${ciFailureContext}\n</ci_failure_context>`
|
|
44
44
|
: "",
|
|
45
|
-
ciFailureLogs: ciFailureContext,
|
|
46
|
-
ciFailureLogsBlock: ciFailureContext
|
|
47
|
-
? `<ci_failure_context>\n${ciFailureContext}\n</ci_failure_context>`
|
|
48
|
-
: "",
|
|
49
45
|
headSha: input.headSha,
|
|
50
46
|
jsonEncodedWorktreePath: JSON.stringify(input.worktreePath),
|
|
51
47
|
pr: String(input.pr),
|
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-20260519070815",
|
|
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
|
@@ -5,13 +5,11 @@
|
|
|
5
5
|
"additionalProperties": false,
|
|
6
6
|
"properties": {
|
|
7
7
|
"$schema": { "type": "string" },
|
|
8
|
-
"agents": {
|
|
9
|
-
"automation": {
|
|
8
|
+
"agents": {
|
|
10
9
|
"type": "object",
|
|
11
10
|
"additionalProperties": false,
|
|
12
11
|
"properties": {
|
|
13
|
-
"
|
|
14
|
-
"close": { "type": "boolean", "default": true }
|
|
12
|
+
"permissions": { "$ref": "#/$defs/permissions" }
|
|
15
13
|
}
|
|
16
14
|
},
|
|
17
15
|
"clear": {
|
|
@@ -24,24 +22,6 @@
|
|
|
24
22
|
"branch": { "type": "boolean", "default": true }
|
|
25
23
|
}
|
|
26
24
|
},
|
|
27
|
-
"checks": {
|
|
28
|
-
"type": "object",
|
|
29
|
-
"additionalProperties": false,
|
|
30
|
-
"properties": {
|
|
31
|
-
"exclude": { "type": "array", "items": { "type": "string" } },
|
|
32
|
-
"waitAfterEdit": { "type": "boolean" },
|
|
33
|
-
"waitBeforeReview": { "type": "boolean" },
|
|
34
|
-
"retryFailedJobs": { "type": "integer", "minimum": 0, "default": 3 }
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
"concurrency": {
|
|
38
|
-
"type": "object",
|
|
39
|
-
"additionalProperties": false,
|
|
40
|
-
"properties": {
|
|
41
|
-
"runs": { "type": "integer", "minimum": 1, "default": 3 },
|
|
42
|
-
"reviewers": { "type": "integer", "minimum": 1, "default": 3 }
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
25
|
"github": {
|
|
46
26
|
"type": "object",
|
|
47
27
|
"required": ["owner", "repo"],
|
|
@@ -59,64 +39,15 @@
|
|
|
59
39
|
}
|
|
60
40
|
},
|
|
61
41
|
"language": { "type": "string" },
|
|
62
|
-
"merge": {
|
|
63
|
-
"type": "object",
|
|
64
|
-
"additionalProperties": false,
|
|
65
|
-
"properties": {
|
|
66
|
-
"approvalPolicy": {
|
|
67
|
-
"enum": ["majority", "unanimous"],
|
|
68
|
-
"default": "majority"
|
|
69
|
-
},
|
|
70
|
-
"method": { "enum": ["merge", "squash", "rebase"] },
|
|
71
|
-
"auto": { "type": "boolean" },
|
|
72
|
-
"deleteBranch": { "type": "boolean" },
|
|
73
|
-
"mergeQueue": { "type": "boolean", "default": false },
|
|
74
|
-
"maxThreadResolutionCycles": {
|
|
75
|
-
"type": "integer",
|
|
76
|
-
"minimum": 0,
|
|
77
|
-
"default": 5,
|
|
78
|
-
"description": "Maximum resolution attempts per unresolved review thread. Set 0 for unlimited attempts."
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
},
|
|
42
|
+
"merge": { "$ref": "#/$defs/merge" },
|
|
82
43
|
"output": {
|
|
83
44
|
"type": "object",
|
|
84
45
|
"additionalProperties": false,
|
|
85
46
|
"properties": {
|
|
86
|
-
"dirs": {
|
|
87
|
-
"type": "object",
|
|
88
|
-
"additionalProperties": false,
|
|
89
|
-
"properties": {
|
|
90
|
-
"pr": { "type": "string" }
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
47
|
"repairAttempts": { "type": "integer", "minimum": 0, "default": 3 }
|
|
94
48
|
}
|
|
95
49
|
},
|
|
96
|
-
"
|
|
97
|
-
"safety": {
|
|
98
|
-
"type": "object",
|
|
99
|
-
"additionalProperties": false,
|
|
100
|
-
"properties": {
|
|
101
|
-
"allowAuthors": { "type": "array", "items": { "type": "string" } },
|
|
102
|
-
"blockedPaths": { "type": "array", "items": { "type": "string" } },
|
|
103
|
-
"maxChangedFiles": { "type": "integer", "minimum": 0 },
|
|
104
|
-
"requiredLabels": { "type": "array", "items": { "type": "string" } }
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
"worktree": {
|
|
108
|
-
"type": "object",
|
|
109
|
-
"additionalProperties": false,
|
|
110
|
-
"properties": {
|
|
111
|
-
"dirs": {
|
|
112
|
-
"type": "object",
|
|
113
|
-
"additionalProperties": false,
|
|
114
|
-
"properties": {
|
|
115
|
-
"pr": { "type": "string" }
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
50
|
+
"review": { "$ref": "#/$defs/review" }
|
|
120
51
|
},
|
|
121
52
|
"$defs": {
|
|
122
53
|
"reviewer": {
|
|
@@ -128,7 +59,7 @@
|
|
|
128
59
|
"model": { "type": "string", "minLength": 1 },
|
|
129
60
|
"options": { "type": "object", "additionalProperties": true },
|
|
130
61
|
"account": { "type": "string", "minLength": 1 },
|
|
131
|
-
"
|
|
62
|
+
"permissions": { "$ref": "#/$defs/permissions" },
|
|
132
63
|
"persona": { "type": "string" }
|
|
133
64
|
}
|
|
134
65
|
},
|
|
@@ -149,21 +80,120 @@
|
|
|
149
80
|
"email": { "type": "string", "minLength": 1 }
|
|
150
81
|
}
|
|
151
82
|
},
|
|
152
|
-
"
|
|
83
|
+
"permissions": { "$ref": "#/$defs/permissions" },
|
|
153
84
|
"persona": { "type": "string" }
|
|
154
85
|
}
|
|
155
86
|
},
|
|
156
|
-
"
|
|
87
|
+
"automation": {
|
|
88
|
+
"type": "object",
|
|
89
|
+
"additionalProperties": false,
|
|
90
|
+
"properties": {
|
|
91
|
+
"merge": { "type": "boolean" },
|
|
92
|
+
"close": { "type": "boolean" }
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
"reviewChecks": {
|
|
96
|
+
"type": "object",
|
|
97
|
+
"additionalProperties": false,
|
|
98
|
+
"properties": {
|
|
99
|
+
"exclude": { "type": "array", "items": { "type": "string" } },
|
|
100
|
+
"wait": { "type": "boolean", "default": true },
|
|
101
|
+
"retryFailedJobs": { "type": "integer", "minimum": 0, "default": 3 }
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"mergeChecks": {
|
|
157
105
|
"type": "object",
|
|
158
106
|
"additionalProperties": false,
|
|
159
107
|
"properties": {
|
|
160
|
-
"
|
|
161
|
-
|
|
108
|
+
"wait": { "type": "boolean", "default": true }
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
"concurrency": {
|
|
112
|
+
"type": "object",
|
|
113
|
+
"additionalProperties": false,
|
|
114
|
+
"properties": {
|
|
115
|
+
"runs": { "type": "integer", "minimum": 1, "default": 3 },
|
|
116
|
+
"reviewers": { "type": "integer", "minimum": 1, "default": 3 }
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
"safety": {
|
|
120
|
+
"type": "object",
|
|
121
|
+
"additionalProperties": false,
|
|
122
|
+
"properties": {
|
|
123
|
+
"allowAuthors": { "type": "array", "items": { "type": "string" } },
|
|
124
|
+
"blockedPaths": { "type": "array", "items": { "type": "string" } },
|
|
125
|
+
"maxChangedFiles": { "type": "integer", "minimum": 0 },
|
|
126
|
+
"requiredLabels": { "type": "array", "items": { "type": "string" } }
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
"reviewPrompts": {
|
|
130
|
+
"type": "object",
|
|
131
|
+
"additionalProperties": false,
|
|
132
|
+
"properties": {
|
|
133
|
+
"review": { "type": "string" },
|
|
134
|
+
"rereview": { "type": "string" },
|
|
135
|
+
"reviewGuidelines": { "type": "string" },
|
|
136
|
+
"ciClassification": { "type": "string" },
|
|
137
|
+
"findingValidation": { "type": "string" },
|
|
138
|
+
"closeReconsideration": { "type": "string" }
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
"mergePrompts": {
|
|
142
|
+
"type": "object",
|
|
143
|
+
"additionalProperties": false,
|
|
144
|
+
"properties": {
|
|
145
|
+
"edit": { "type": "string" },
|
|
146
|
+
"editGuidelines": { "type": "string" },
|
|
147
|
+
"ciClassification": { "type": "string" }
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
"reviewMerge": {
|
|
151
|
+
"type": "object",
|
|
152
|
+
"additionalProperties": false,
|
|
153
|
+
"properties": {
|
|
154
|
+
"approvalPolicy": {
|
|
155
|
+
"enum": ["majority", "unanimous"],
|
|
156
|
+
"default": "majority"
|
|
157
|
+
},
|
|
158
|
+
"method": { "enum": ["merge", "squash", "rebase"] },
|
|
159
|
+
"auto": { "type": "boolean" },
|
|
160
|
+
"deleteBranch": { "type": "boolean" },
|
|
161
|
+
"queue": { "type": "boolean", "default": false }
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
"review": {
|
|
165
|
+
"type": "object",
|
|
166
|
+
"additionalProperties": false,
|
|
167
|
+
"properties": {
|
|
168
|
+
"agents": {
|
|
162
169
|
"type": "array",
|
|
163
170
|
"minItems": 3,
|
|
164
171
|
"items": { "$ref": "#/$defs/reviewer" }
|
|
165
172
|
},
|
|
166
|
-
"
|
|
173
|
+
"prompts": { "$ref": "#/$defs/reviewPrompts" },
|
|
174
|
+
"checks": { "$ref": "#/$defs/reviewChecks" },
|
|
175
|
+
"safety": { "$ref": "#/$defs/safety" },
|
|
176
|
+
"automation": { "$ref": "#/$defs/automation" },
|
|
177
|
+
"concurrency": { "$ref": "#/$defs/concurrency" },
|
|
178
|
+
"merge": { "$ref": "#/$defs/reviewMerge" },
|
|
179
|
+
"output": { "type": "string" },
|
|
180
|
+
"worktree": { "type": "string" }
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
"merge": {
|
|
184
|
+
"type": "object",
|
|
185
|
+
"additionalProperties": false,
|
|
186
|
+
"properties": {
|
|
187
|
+
"editor": { "$ref": "#/$defs/editor" },
|
|
188
|
+
"checks": { "$ref": "#/$defs/mergeChecks" },
|
|
189
|
+
"prompts": { "$ref": "#/$defs/mergePrompts" },
|
|
190
|
+
"automation": { "$ref": "#/$defs/automation" },
|
|
191
|
+
"maxThreadResolutionCycles": {
|
|
192
|
+
"type": "integer",
|
|
193
|
+
"minimum": 0,
|
|
194
|
+
"default": 5,
|
|
195
|
+
"description": "Maximum resolution attempts per unresolved review thread. Set 0 for unlimited attempts."
|
|
196
|
+
}
|
|
167
197
|
}
|
|
168
198
|
},
|
|
169
199
|
"permissionAction": { "enum": ["allow", "ask", "deny"] },
|
|
@@ -176,7 +206,7 @@
|
|
|
176
206
|
}
|
|
177
207
|
]
|
|
178
208
|
},
|
|
179
|
-
"
|
|
209
|
+
"permissions": {
|
|
180
210
|
"oneOf": [
|
|
181
211
|
{ "$ref": "#/$defs/permissionAction" },
|
|
182
212
|
{
|
|
@@ -184,23 +214,6 @@
|
|
|
184
214
|
"additionalProperties": { "$ref": "#/$defs/permissionRule" }
|
|
185
215
|
}
|
|
186
216
|
]
|
|
187
|
-
},
|
|
188
|
-
"prompts": {
|
|
189
|
-
"type": "object",
|
|
190
|
-
"additionalProperties": false,
|
|
191
|
-
"properties": {
|
|
192
|
-
"review": { "type": "string" },
|
|
193
|
-
"rereview": { "type": "string" },
|
|
194
|
-
"edit": { "type": "string" },
|
|
195
|
-
"editGuidelines": { "type": "string" },
|
|
196
|
-
"findingValidation": { "type": "string" },
|
|
197
|
-
"closeReconsideration": { "type": "string" },
|
|
198
|
-
"rereviewCloseReconsideration": { "type": "string" },
|
|
199
|
-
"ciClassification": { "type": "string" },
|
|
200
|
-
"ciClassificationAfterEdit": { "type": "string" },
|
|
201
|
-
"report": { "type": "string" },
|
|
202
|
-
"reviewGuidelines": { "type": "string" }
|
|
203
|
-
}
|
|
204
217
|
}
|
|
205
218
|
}
|
|
206
219
|
}
|