opencode-magi 0.1.0 → 0.3.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 +33 -10
- package/dist/commands.js +4 -0
- package/dist/config/output.js +11 -2
- package/dist/config/resolve.js +124 -26
- package/dist/config/validate.js +486 -191
- package/dist/config/worktree.js +19 -0
- package/dist/github/commands.js +349 -17
- package/dist/index.js +257 -27
- package/dist/orchestrator/ci.js +1 -1
- package/dist/orchestrator/findings.js +4 -3
- package/dist/orchestrator/inline-comments.js +73 -0
- package/dist/orchestrator/majority.js +14 -0
- package/dist/orchestrator/merge.js +24 -4
- package/dist/orchestrator/report.js +15 -1
- package/dist/orchestrator/review-context.js +309 -0
- package/dist/orchestrator/review.js +78 -10
- package/dist/orchestrator/run-manager.js +418 -20
- package/dist/orchestrator/triage.js +1119 -0
- package/dist/permissions/editor.json +8 -1
- package/dist/prompts/compose.js +172 -15
- package/dist/prompts/contracts.js +119 -12
- package/dist/prompts/output.js +149 -14
- package/dist/prompts/templates/{close-reconsideration.md → review/close-reconsideration.md} +1 -2
- package/dist/prompts/templates/review/review.md +13 -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 +28 -27
- package/schema.json +234 -90
- package/dist/prompts/templates/rereview-close-reconsideration.md +0 -6
- package/dist/prompts/templates/review.md +0 -7
- /package/dist/prompts/templates/{ci-classification-after-edit.md → merge/ci-classification.md} +0 -0
- /package/dist/prompts/templates/{edit.md → merge/edit.md} +0 -0
- /package/dist/prompts/templates/{ci-classification.md → review/ci-classification.md} +0 -0
- /package/dist/prompts/templates/{finding-validation.md → review/finding-validation.md} +0 -0
- /package/dist/prompts/templates/{rereview.md → review/rereview.md} +0 -0
package/README.md
CHANGED
|
@@ -36,6 +36,8 @@ Add the plugin to `opencode.json`.
|
|
|
36
36
|
}
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
Restart OpenCode. Done.
|
|
40
|
+
|
|
39
41
|
### Configure
|
|
40
42
|
|
|
41
43
|
Configure global defaults in `~/.config/opencode/magi.json` and project overrides in `<project>/.opencode/magi.json`.
|
|
@@ -58,9 +60,9 @@ Add the following content to the configuration file.
|
|
|
58
60
|
|
|
59
61
|
```json
|
|
60
62
|
{
|
|
61
|
-
"$schema": "https://raw.githubusercontent.com/
|
|
62
|
-
"
|
|
63
|
-
"
|
|
63
|
+
"$schema": "https://raw.githubusercontent.com/magi-ai/opencode-magi/main/schema.json",
|
|
64
|
+
"review": {
|
|
65
|
+
"agents": [
|
|
64
66
|
{
|
|
65
67
|
"account": "your-account-1",
|
|
66
68
|
"model": "openai/gpt-5.5"
|
|
@@ -78,7 +80,7 @@ Add the following content to the configuration file.
|
|
|
78
80
|
}
|
|
79
81
|
```
|
|
80
82
|
|
|
81
|
-
`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.
|
|
82
84
|
|
|
83
85
|
#### Set project config
|
|
84
86
|
|
|
@@ -94,13 +96,13 @@ Add the following content to the configuration file.
|
|
|
94
96
|
|
|
95
97
|
```json
|
|
96
98
|
{
|
|
97
|
-
"$schema": "https://raw.githubusercontent.com/
|
|
99
|
+
"$schema": "https://raw.githubusercontent.com/magi-ai/opencode-magi/main/schema.json",
|
|
98
100
|
"github": {
|
|
99
101
|
"owner": "your-owner",
|
|
100
102
|
"repo": "your-repo"
|
|
101
103
|
},
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
+
"review": {
|
|
105
|
+
"agents": [
|
|
104
106
|
{
|
|
105
107
|
"account": "your-account-1",
|
|
106
108
|
"model": "openai/gpt-5.5"
|
|
@@ -113,7 +115,9 @@ Add the following content to the configuration file.
|
|
|
113
115
|
"account": "your-account-3",
|
|
114
116
|
"model": "opencode/kimi-k2-6"
|
|
115
117
|
}
|
|
116
|
-
]
|
|
118
|
+
]
|
|
119
|
+
},
|
|
120
|
+
"merge": {
|
|
117
121
|
"editor": {
|
|
118
122
|
"account": "your-editor-account",
|
|
119
123
|
"model": "openai/gpt-5.5",
|
|
@@ -122,11 +126,28 @@ Add the following content to the configuration file.
|
|
|
122
126
|
"email": "your-email@example.com"
|
|
123
127
|
}
|
|
124
128
|
}
|
|
129
|
+
},
|
|
130
|
+
"triage": {
|
|
131
|
+
"account": "your-triage-account",
|
|
132
|
+
"agents": [
|
|
133
|
+
{
|
|
134
|
+
"id": "general",
|
|
135
|
+
"model": "openai/gpt-5.5"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"id": "maintenance",
|
|
139
|
+
"model": "anthropic/claude-opus-4-7"
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"id": "product",
|
|
143
|
+
"model": "opencode/kimi-k2-6"
|
|
144
|
+
}
|
|
145
|
+
]
|
|
125
146
|
}
|
|
126
147
|
}
|
|
127
148
|
```
|
|
128
149
|
|
|
129
|
-
`agents
|
|
150
|
+
`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.
|
|
130
151
|
|
|
131
152
|
#### Validate config
|
|
132
153
|
|
|
@@ -145,6 +166,8 @@ Run commands from OpenCode.
|
|
|
145
166
|
/magi:review --dry-run 123
|
|
146
167
|
/magi:merge 123
|
|
147
168
|
/magi:merge --dry-run 123
|
|
169
|
+
/magi:triage 47 48
|
|
170
|
+
/magi:triage --dry-run 47
|
|
148
171
|
/magi:clear
|
|
149
172
|
```
|
|
150
173
|
|
|
@@ -152,7 +175,7 @@ Run commands from OpenCode.
|
|
|
152
175
|
|
|
153
176
|
- [Commands](docs/commands/index.md)
|
|
154
177
|
- [Config](docs/config.md)
|
|
155
|
-
- [Prompts](docs/prompts.md)
|
|
178
|
+
- [Prompts](docs/prompts/index.md)
|
|
156
179
|
|
|
157
180
|
## Contributing
|
|
158
181
|
|
package/dist/commands.js
CHANGED
|
@@ -7,6 +7,10 @@ export const MAGI_COMMANDS = {
|
|
|
7
7
|
description: "Review and merge pull requests with Magi",
|
|
8
8
|
template: [`Call the \`magi_merge\` tool.`, "PR: $ARGUMENTS"].join("\n"),
|
|
9
9
|
},
|
|
10
|
+
"magi:triage": {
|
|
11
|
+
description: "Triage GitHub issues with Magi",
|
|
12
|
+
template: [`Call the \`magi_triage\` tool.`, "Issue: $ARGUMENTS"].join("\n"),
|
|
13
|
+
},
|
|
10
14
|
"magi:review": {
|
|
11
15
|
description: "Review pull requests with Magi",
|
|
12
16
|
template: [`Call the \`magi_review\` tool.`, "PR: $ARGUMENTS"].join("\n"),
|
package/dist/config/output.js
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
import { isAbsolute, join } from "node:path";
|
|
2
2
|
const DEFAULT_OUTPUT_DIRS = {
|
|
3
|
+
issue: ".magi/runs/issue",
|
|
3
4
|
pr: ".magi/runs/pr",
|
|
4
5
|
};
|
|
5
6
|
function resolvePath(directory, path) {
|
|
6
7
|
return isAbsolute(path) ? path : join(directory, path);
|
|
7
8
|
}
|
|
8
9
|
export function outputBaseDir(directory, config, kind) {
|
|
9
|
-
return resolvePath(directory,
|
|
10
|
+
return resolvePath(directory, kind === "issue"
|
|
11
|
+
? (config.triage?.output ?? DEFAULT_OUTPUT_DIRS[kind])
|
|
12
|
+
: (config.review?.output ?? DEFAULT_OUTPUT_DIRS[kind]));
|
|
10
13
|
}
|
|
11
14
|
export function outputBaseDirs(directory, config) {
|
|
12
|
-
return [
|
|
15
|
+
return [
|
|
16
|
+
outputBaseDir(directory, config, "pr"),
|
|
17
|
+
outputBaseDir(directory, config, "issue"),
|
|
18
|
+
];
|
|
13
19
|
}
|
|
14
20
|
export function prRunOutputDir(input) {
|
|
15
21
|
return join(outputBaseDir(input.directory, input.config, "pr"), String(input.pr), ...(input.runId ? [input.runId] : []));
|
|
16
22
|
}
|
|
23
|
+
export function issueRunOutputDir(input) {
|
|
24
|
+
return join(outputBaseDir(input.directory, input.config, "issue"), String(input.issue), ...(input.runId ? [input.runId] : []));
|
|
25
|
+
}
|
package/dist/config/resolve.js
CHANGED
|
@@ -4,9 +4,32 @@ const ID_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
|
4
4
|
const DEFAULT_COMMON_PERMISSION = commonPermission;
|
|
5
5
|
const DEFAULT_REVIEWER_PERMISSION = DEFAULT_COMMON_PERMISSION;
|
|
6
6
|
const DEFAULT_EDITOR_PERMISSION = mergePermissions(DEFAULT_COMMON_PERMISSION, editorPermission);
|
|
7
|
+
const DEFAULT_TRIAGE_CATEGORIES = [
|
|
8
|
+
{
|
|
9
|
+
description: "Something is broken or behaves incorrectly.",
|
|
10
|
+
id: "bug",
|
|
11
|
+
labels: ["bug"],
|
|
12
|
+
types: ["Bug"],
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
description: "Maintenance, refactoring, chores, or planned work.",
|
|
16
|
+
id: "task",
|
|
17
|
+
labels: ["task"],
|
|
18
|
+
types: ["Task"],
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
description: "New or improved user-facing capability.",
|
|
22
|
+
id: "feature",
|
|
23
|
+
labels: ["enhancement"],
|
|
24
|
+
types: ["Feature"],
|
|
25
|
+
},
|
|
26
|
+
];
|
|
7
27
|
export function reviewerKey(reviewer, index) {
|
|
8
28
|
return reviewer.id ?? `reviewer-${index + 1}`;
|
|
9
29
|
}
|
|
30
|
+
export function triageAgentKey(agent, index) {
|
|
31
|
+
return agent.id ?? `triage-${index + 1}`;
|
|
32
|
+
}
|
|
10
33
|
export function validateReviewerId(id) {
|
|
11
34
|
return ID_PATTERN.test(id);
|
|
12
35
|
}
|
|
@@ -44,27 +67,57 @@ export function mergePermissions(base, override) {
|
|
|
44
67
|
return merged;
|
|
45
68
|
}
|
|
46
69
|
export function resolveReviewerPermission(agents, reviewer) {
|
|
47
|
-
return mergePermissions(mergePermissions(DEFAULT_REVIEWER_PERMISSION, agents.permissions), reviewer.
|
|
70
|
+
return mergePermissions(mergePermissions(DEFAULT_REVIEWER_PERMISSION, agents.permissions), reviewer.permissions);
|
|
48
71
|
}
|
|
49
72
|
export function resolveEditorPermission(agents, editor) {
|
|
50
|
-
return mergePermissions(mergePermissions(DEFAULT_EDITOR_PERMISSION, agents.permissions), editor.
|
|
73
|
+
return mergePermissions(mergePermissions(DEFAULT_EDITOR_PERMISSION, agents.permissions), editor.permissions);
|
|
74
|
+
}
|
|
75
|
+
export function resolveTriageAgentPermission(agents, agent) {
|
|
76
|
+
return mergePermissions(mergePermissions(DEFAULT_REVIEWER_PERMISSION, agents.permissions), agent.permissions);
|
|
77
|
+
}
|
|
78
|
+
export function resolveTriageCreatorPermission(agents, creator) {
|
|
79
|
+
return mergePermissions(mergePermissions(DEFAULT_EDITOR_PERMISSION, agents.permissions), creator.permissions);
|
|
51
80
|
}
|
|
52
|
-
export function resolveAgents(
|
|
81
|
+
export function resolveAgents(config) {
|
|
82
|
+
const agents = config.agents ?? {};
|
|
83
|
+
const editor = config.merge?.editor;
|
|
84
|
+
const creator = config.triage?.creator;
|
|
53
85
|
return {
|
|
54
|
-
editor:
|
|
86
|
+
editor: editor
|
|
55
87
|
? {
|
|
56
|
-
...
|
|
57
|
-
permission: resolveEditorPermission(agents,
|
|
88
|
+
...editor,
|
|
89
|
+
permission: resolveEditorPermission(agents, editor),
|
|
58
90
|
}
|
|
59
91
|
: undefined,
|
|
60
|
-
reviewers: (agents
|
|
92
|
+
reviewers: (config.review?.agents ?? []).map((reviewer, index) => ({
|
|
61
93
|
...reviewer,
|
|
62
94
|
key: reviewerKey(reviewer, index),
|
|
63
95
|
index,
|
|
64
96
|
permission: resolveReviewerPermission(agents, reviewer),
|
|
65
97
|
})),
|
|
98
|
+
triage: (config.triage?.agents ?? []).map((agent, index) => ({
|
|
99
|
+
...agent,
|
|
100
|
+
key: triageAgentKey(agent, index),
|
|
101
|
+
index,
|
|
102
|
+
permission: resolveTriageAgentPermission(agents, agent),
|
|
103
|
+
})),
|
|
104
|
+
triageCreator: creator
|
|
105
|
+
? {
|
|
106
|
+
...creator,
|
|
107
|
+
account: creator.account ?? config.triage?.account ?? "",
|
|
108
|
+
permission: resolveTriageCreatorPermission(agents, creator),
|
|
109
|
+
}
|
|
110
|
+
: undefined,
|
|
66
111
|
};
|
|
67
112
|
}
|
|
113
|
+
function resolveTriageCategories(config) {
|
|
114
|
+
return (config.triage?.categories ?? DEFAULT_TRIAGE_CATEGORIES).map((category) => ({
|
|
115
|
+
description: category.description,
|
|
116
|
+
id: category.id ?? "",
|
|
117
|
+
labels: category.labels ?? [],
|
|
118
|
+
types: category.types ?? [],
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
68
121
|
export function resolveRepository(config) {
|
|
69
122
|
if (!config.github?.owner)
|
|
70
123
|
throw new Error("github.owner is required");
|
|
@@ -72,20 +125,21 @@ export function resolveRepository(config) {
|
|
|
72
125
|
throw new Error("github.repo is required");
|
|
73
126
|
return {
|
|
74
127
|
alias: config.github.repo,
|
|
75
|
-
agents: resolveAgents(config
|
|
128
|
+
agents: resolveAgents(config),
|
|
76
129
|
automation: {
|
|
77
|
-
close: config.automation?.close ??
|
|
78
|
-
merge: config.automation?.merge ?? true,
|
|
130
|
+
close: config.merge?.automation?.close ?? false,
|
|
131
|
+
merge: config.merge?.automation?.merge ?? true,
|
|
79
132
|
},
|
|
80
133
|
checks: {
|
|
81
|
-
exclude: config.checks?.exclude ?? [],
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
134
|
+
exclude: config.review?.checks?.exclude ?? [],
|
|
135
|
+
retryFailedJobs: config.review?.checks?.retryFailedJobs ?? 3,
|
|
136
|
+
wait: config.review?.checks?.wait ?? true,
|
|
137
|
+
waitAfterEdit: config.merge?.checks?.wait ?? true,
|
|
138
|
+
waitBeforeReview: config.review?.checks?.wait ?? true,
|
|
85
139
|
},
|
|
86
140
|
concurrency: {
|
|
87
|
-
runs: config.concurrency?.runs ?? 3,
|
|
88
|
-
reviewers: config.concurrency?.reviewers ?? 3,
|
|
141
|
+
runs: config.review?.concurrency?.runs ?? 3,
|
|
142
|
+
reviewers: config.review?.concurrency?.reviewers ?? 3,
|
|
89
143
|
},
|
|
90
144
|
github: {
|
|
91
145
|
apiRetryAttempts: config.github.apiRetryAttempts ?? 3,
|
|
@@ -95,19 +149,63 @@ export function resolveRepository(config) {
|
|
|
95
149
|
},
|
|
96
150
|
language: config.language,
|
|
97
151
|
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
|
-
|
|
152
|
+
approvalPolicy: config.review?.merge?.approvalPolicy ?? "majority",
|
|
153
|
+
method: config.review?.merge?.method ?? "squash",
|
|
154
|
+
auto: config.review?.merge?.auto ?? true,
|
|
155
|
+
deleteBranch: config.review?.merge?.deleteBranch ?? true,
|
|
156
|
+
queue: config.review?.merge?.queue ?? false,
|
|
157
|
+
mergeQueue: config.review?.merge?.queue ?? false,
|
|
103
158
|
maxThreadResolutionCycles: config.merge?.maxThreadResolutionCycles ?? 5,
|
|
104
159
|
},
|
|
105
|
-
prompts:
|
|
160
|
+
prompts: {
|
|
161
|
+
ciClassification: config.review?.prompts?.ciClassification,
|
|
162
|
+
ciClassificationAfterEdit: config.merge?.prompts?.ciClassification,
|
|
163
|
+
closeReconsideration: config.review?.prompts?.closeReconsideration,
|
|
164
|
+
edit: config.merge?.prompts?.edit,
|
|
165
|
+
editGuidelines: config.merge?.prompts?.editGuidelines,
|
|
166
|
+
findingValidation: config.review?.prompts?.findingValidation,
|
|
167
|
+
rereview: config.review?.prompts?.rereview,
|
|
168
|
+
review: config.review?.prompts?.review,
|
|
169
|
+
reviewGuidelines: config.review?.prompts?.reviewGuidelines,
|
|
170
|
+
},
|
|
171
|
+
reviewAutomation: {
|
|
172
|
+
close: config.review?.automation?.close ?? false,
|
|
173
|
+
merge: config.review?.automation?.merge ?? true,
|
|
174
|
+
},
|
|
106
175
|
safety: {
|
|
107
|
-
allowAuthors: config.safety?.allowAuthors ?? [],
|
|
108
|
-
blockedPaths: config.safety?.blockedPaths ?? [],
|
|
109
|
-
maxChangedFiles: config.safety?.maxChangedFiles,
|
|
110
|
-
requiredLabels: config.safety?.requiredLabels ?? [],
|
|
176
|
+
allowAuthors: config.review?.safety?.allowAuthors ?? [],
|
|
177
|
+
blockedPaths: config.review?.safety?.blockedPaths ?? [],
|
|
178
|
+
maxChangedFiles: config.review?.safety?.maxChangedFiles,
|
|
179
|
+
requiredLabels: config.review?.safety?.requiredLabels ?? [],
|
|
180
|
+
},
|
|
181
|
+
triage: {
|
|
182
|
+
account: config.triage?.account,
|
|
183
|
+
automation: {
|
|
184
|
+
clear: config.triage?.automation?.clear ?? ["triage"],
|
|
185
|
+
close: config.triage?.automation?.close ?? false,
|
|
186
|
+
create: config.triage?.automation?.create ?? false,
|
|
187
|
+
merge: config.triage?.automation?.merge ?? false,
|
|
188
|
+
review: config.triage?.automation?.review ?? false,
|
|
189
|
+
},
|
|
190
|
+
categories: resolveTriageCategories(config),
|
|
191
|
+
concurrency: {
|
|
192
|
+
runs: config.triage?.concurrency?.runs ?? 3,
|
|
193
|
+
},
|
|
194
|
+
output: config.triage?.output,
|
|
195
|
+
prompts: config.triage?.prompts ?? {},
|
|
196
|
+
safety: {
|
|
197
|
+
allowAuthors: config.triage?.safety?.allowAuthors ?? [],
|
|
198
|
+
allowMentionActors: config.triage?.safety?.allowMentionActors ?? [],
|
|
199
|
+
allowMentionRoles: config.triage?.safety?.allowMentionRoles ?? [
|
|
200
|
+
"AUTHOR",
|
|
201
|
+
"OWNER",
|
|
202
|
+
"MEMBER",
|
|
203
|
+
"COLLABORATOR",
|
|
204
|
+
],
|
|
205
|
+
blockedLabels: config.triage?.safety?.blockedLabels ?? [],
|
|
206
|
+
requiredLabels: config.triage?.safety?.requiredLabels ?? ["triage"],
|
|
207
|
+
},
|
|
208
|
+
worktree: config.triage?.worktree,
|
|
111
209
|
},
|
|
112
210
|
};
|
|
113
211
|
}
|