opencode-magi 0.0.0-dev-20260526012523 → 0.0.0-dev-20260630031336

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.
Files changed (126) hide show
  1. package/README.ja.md +254 -0
  2. package/README.md +33 -28
  3. package/dist/commands.js +15 -9
  4. package/dist/config/index.js +2 -0
  5. package/dist/config/resolve.js +105 -246
  6. package/dist/config/validate.js +97 -1110
  7. package/dist/constant.js +142 -0
  8. package/dist/graphql/index.generated.js +117 -0
  9. package/dist/graphql/index.js +49 -0
  10. package/dist/index.js +12 -800
  11. package/dist/magi.js +312 -0
  12. package/dist/permissions/common.json +22 -22
  13. package/dist/permissions/editor.json +12 -12
  14. package/dist/prompts/index.js +93 -0
  15. package/dist/prompts/merge/ci-classification/output-contract.md +22 -0
  16. package/dist/prompts/merge/ci-classification/task.md +10 -0
  17. package/dist/prompts/merge/ci-classification/validate.json +20 -0
  18. package/dist/prompts/merge/conflict/output-contract.md +12 -0
  19. package/dist/prompts/merge/conflict/task.md +9 -0
  20. package/dist/prompts/merge/conflict/validate.json +4 -0
  21. package/dist/prompts/merge/edit/output-contract.md +24 -0
  22. package/dist/prompts/merge/edit/task.md +10 -0
  23. package/dist/prompts/merge/edit/validate.json +57 -0
  24. package/dist/prompts/review/ci-classification/output-contract.md +21 -0
  25. package/dist/prompts/review/ci-classification/task.md +7 -0
  26. package/dist/prompts/review/ci-classification/validate.json +20 -0
  27. package/dist/prompts/review/close-reconsideration/output-contract.md +20 -0
  28. package/dist/prompts/review/close-reconsideration/task.md +6 -0
  29. package/dist/prompts/review/close-reconsideration/validate.json +38 -0
  30. package/dist/prompts/review/comment/output-contract.md +11 -0
  31. package/dist/prompts/review/comment/task.md +3 -0
  32. package/dist/prompts/review/comment/validate.json +8 -0
  33. package/dist/prompts/review/finding-validation/output-contract.md +25 -0
  34. package/dist/prompts/{templates/review/finding-validation.md → review/finding-validation/task.md} +2 -3
  35. package/dist/prompts/review/finding-validation/validate.json +21 -0
  36. package/dist/prompts/review/rereview/output-contract.md +28 -0
  37. package/dist/prompts/{templates/review/rereview.md → review/rereview/task.md} +6 -10
  38. package/dist/prompts/review/rereview/validate.json +80 -0
  39. package/dist/prompts/review/review/output-contract.md +24 -0
  40. package/dist/prompts/{templates/review/review.md → review/review/task.md} +2 -5
  41. package/dist/prompts/review/review/validate.json +45 -0
  42. package/dist/prompts/triage/acceptance/output-contract.md +16 -0
  43. package/dist/prompts/triage/acceptance/validate.json +23 -0
  44. package/dist/prompts/triage/category/output-contract.md +16 -0
  45. package/dist/prompts/triage/category/validate.json +23 -0
  46. package/dist/prompts/triage/comment-classification/output-contract.md +15 -0
  47. package/dist/prompts/triage/comment-classification/validate.json +28 -0
  48. package/dist/prompts/triage/create/output-contract.md +27 -0
  49. package/dist/prompts/triage/create/validate.json +67 -0
  50. package/dist/prompts/triage/deplicate/output-contract.md +16 -0
  51. package/dist/prompts/triage/deplicate/validate.json +20 -0
  52. package/dist/prompts/triage/existing/output-contract.md +16 -0
  53. package/dist/prompts/triage/existing/validate.json +13 -0
  54. package/dist/prompts/triage/reconsider/output-contract.md +16 -0
  55. package/dist/prompts/triage/reconsider/validate.json +23 -0
  56. package/dist/prompts/triage/signal/output-contract.md +19 -0
  57. package/dist/prompts/triage/signal/validate.json +19 -0
  58. package/dist/tools/cancel/index.js +30 -0
  59. package/dist/tools/clear/index.js +21 -0
  60. package/dist/tools/index.js +14 -0
  61. package/dist/tools/merge/action.js +50 -0
  62. package/dist/tools/merge/context.js +124 -0
  63. package/dist/tools/merge/editor.js +260 -0
  64. package/dist/tools/merge/index.js +168 -0
  65. package/dist/tools/merge/index.type.js +1 -0
  66. package/dist/tools/merge/merge.js +68 -0
  67. package/dist/tools/merge/report.js +73 -0
  68. package/dist/tools/review/action.js +436 -0
  69. package/dist/tools/review/check.js +302 -0
  70. package/dist/tools/review/context.js +266 -0
  71. package/dist/tools/review/index.js +121 -0
  72. package/dist/tools/review/index.type.js +1 -0
  73. package/dist/tools/review/report.js +141 -0
  74. package/dist/tools/review/review.js +140 -0
  75. package/dist/tools/review/reviewer.js +368 -0
  76. package/dist/tools/triage/index.js +17 -0
  77. package/dist/tools/validate/index.js +28 -0
  78. package/dist/utils/array.js +6 -0
  79. package/dist/utils/assertion.js +38 -0
  80. package/dist/utils/exec.js +14 -0
  81. package/dist/utils/function.js +22 -0
  82. package/dist/utils/github.js +63 -0
  83. package/dist/utils/index.js +9 -0
  84. package/dist/utils/index.type.js +1 -0
  85. package/dist/utils/object.js +21 -0
  86. package/dist/utils/opencode.js +14 -0
  87. package/dist/utils/string.js +38 -0
  88. package/dist/utils/worker.js +30 -0
  89. package/package.json +16 -4
  90. package/schema.json +6 -4
  91. package/dist/config/load.js +0 -62
  92. package/dist/config/output.js +0 -25
  93. package/dist/config/worktree.js +0 -25
  94. package/dist/github/commands.js +0 -905
  95. package/dist/github/retry.js +0 -44
  96. package/dist/orchestrator/abort.js +0 -9
  97. package/dist/orchestrator/ci.js +0 -579
  98. package/dist/orchestrator/findings.js +0 -88
  99. package/dist/orchestrator/inline-comments.js +0 -73
  100. package/dist/orchestrator/majority.js +0 -62
  101. package/dist/orchestrator/merge.js +0 -1260
  102. package/dist/orchestrator/model.js +0 -216
  103. package/dist/orchestrator/pool.js +0 -15
  104. package/dist/orchestrator/report.js +0 -175
  105. package/dist/orchestrator/review-context.js +0 -342
  106. package/dist/orchestrator/review.js +0 -1480
  107. package/dist/orchestrator/run-manager.js +0 -2132
  108. package/dist/orchestrator/safety.js +0 -44
  109. package/dist/orchestrator/triage.js +0 -1370
  110. package/dist/prompts/compose.js +0 -473
  111. package/dist/prompts/contracts.js +0 -300
  112. package/dist/prompts/output.js +0 -401
  113. package/dist/prompts/templates/merge/ci-classification.md +0 -16
  114. package/dist/prompts/templates/merge/conflict.md +0 -10
  115. package/dist/prompts/templates/merge/edit.md +0 -15
  116. package/dist/prompts/templates/review/ci-classification.md +0 -9
  117. package/dist/prompts/templates/review/close-reconsideration.md +0 -6
  118. /package/dist/{types.js → config/index.type.js} +0 -0
  119. /package/dist/prompts/{templates/triage/acceptance.md → triage/acceptance/task.md} +0 -0
  120. /package/dist/prompts/{templates/triage/category.md → triage/category/task.md} +0 -0
  121. /package/dist/prompts/{templates/triage/comment-classification.md → triage/comment-classification/task.md} +0 -0
  122. /package/dist/prompts/{templates/triage/create.md → triage/create/task.md} +0 -0
  123. /package/dist/prompts/{templates/triage/duplicate.md → triage/deplicate/task.md} +0 -0
  124. /package/dist/prompts/{templates/triage/existing-pr.md → triage/existing/task.md} +0 -0
  125. /package/dist/prompts/{templates/triage/reconsider.md → triage/reconsider/task.md} +0 -0
  126. /package/dist/prompts/{templates/triage/signal.md → triage/signal/task.md} +0 -0
@@ -1,254 +1,113 @@
1
- import editorPermission from "../permissions/editor.json" with { type: "json" };
2
- import commonPermission from "../permissions/common.json" with { type: "json" };
3
- const ID_PATTERN = /^[A-Za-z0-9_-]+$/;
4
- const DEFAULT_COMMON_PERMISSION = commonPermission;
5
- const DEFAULT_REVIEWER_PERMISSION = DEFAULT_COMMON_PERMISSION;
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
- ];
27
- export const DEFAULT_TRIAGE_LABEL_RULES = [
28
- { remove: ["triage"], when: { disposition: "accepted" } },
29
- {
30
- add: ["duplicate"],
31
- remove: ["triage"],
32
- when: { disposition: "duplicate" },
33
- },
34
- {
35
- add: ["duplicate"],
36
- remove: ["triage"],
37
- when: { disposition: "already_handled" },
38
- },
39
- {
40
- add: ["wontfix"],
41
- remove: ["triage"],
42
- when: { disposition: "rejected" },
43
- },
44
- {
45
- add: ["invalid"],
46
- remove: ["triage"],
47
- when: { disposition: "invalid" },
48
- },
49
- { add: ["question"], when: { disposition: "needs_category" } },
50
- { add: ["question"], when: { disposition: "needs_acceptance" } },
51
- ];
52
- export function reviewerKey(reviewer, index) {
53
- return reviewer.id ?? `reviewer-${index + 1}`;
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { CONFIG_PATH, DEFAULT_CONFIG } from "@/constant";
4
+ import { filterEmpty, getModels, isArray, isObject, isString, merge, } from "@/utils";
5
+ export function resolvePermissions(permissions) {
6
+ if (!permissions)
7
+ return undefined;
8
+ if (isString(permissions))
9
+ return [
10
+ "read",
11
+ "edit",
12
+ "glob",
13
+ "grep",
14
+ "bash",
15
+ "task",
16
+ "skill",
17
+ "lsp",
18
+ "webfetch",
19
+ "websearch",
20
+ "external_directory",
21
+ "doom_loop",
22
+ ].map((permission) => ({
23
+ action: permissions,
24
+ pattern: "*",
25
+ permission,
26
+ }));
27
+ else
28
+ return Object.entries(permissions).flatMap(([permission, rule]) => {
29
+ if (isString(rule))
30
+ return [{ action: rule, pattern: "*", permission }];
31
+ else
32
+ return Object.entries(rule).map(([pattern, action]) => ({
33
+ action,
34
+ pattern,
35
+ permission,
36
+ }));
37
+ });
54
38
  }
55
- export function triageAgentKey(agent, index) {
56
- return agent.id ?? `voter-${index + 1}`;
57
- }
58
- export function validateReviewerId(id) {
59
- return ID_PATTERN.test(id);
39
+ function mergePermissions(base, override) {
40
+ if (!override)
41
+ return base;
42
+ else if (isString(override) || isString(base))
43
+ return override;
44
+ else
45
+ return merge(base, override);
60
46
  }
61
- function normalizedModel(model) {
62
- if (typeof model !== "string") {
63
- throw new Error("model must be normalized before resolving agents");
64
- }
65
- return model;
47
+ function mergeAgent(base, agent) {
48
+ if (base.agents.refs && agent.ref)
49
+ agent = { ...base.agents.refs[agent.ref], ...agent };
50
+ agent.permissions = mergePermissions(base.agents.permissions, agent.permissions);
51
+ if (base.account)
52
+ agent.account ??= base.account;
53
+ return agent;
66
54
  }
67
- function clonePermissionValue(value) {
68
- return typeof value === "string" ? value : { ...value };
55
+ function resolveModel(models, model) {
56
+ if (isArray(model))
57
+ model = model.find((model) => isString(model) ? models.includes(model) : models.includes(model.id));
58
+ if (!model)
59
+ return undefined;
60
+ if (isString(model))
61
+ return models.includes(model) ? { id: model } : undefined;
62
+ else
63
+ return models.includes(model.id) ? model : undefined;
69
64
  }
70
- export function mergePermissions(base, override) {
71
- if (!override) {
72
- return typeof base === "string"
73
- ? base
74
- : Object.fromEntries(Object.entries(base).map(([key, value]) => [
75
- key,
76
- clonePermissionValue(value),
77
- ]));
65
+ async function readConfig(path) {
66
+ try {
67
+ const config = JSON.parse(await readFile(path, "utf8"));
68
+ if (!isObject(config))
69
+ throw new Error(`Config must be a JSON object: ${path}`);
70
+ return config;
78
71
  }
79
- if (typeof override === "string")
80
- return override;
81
- if (typeof base === "string") {
82
- return Object.fromEntries(Object.entries(override).map(([key, value]) => [
83
- key,
84
- clonePermissionValue(value),
85
- ]));
86
- }
87
- const merged = Object.fromEntries(Object.entries(base).map(([key, value]) => [
88
- key,
89
- clonePermissionValue(value),
90
- ]));
91
- for (const [permission, value] of Object.entries(override)) {
92
- const existing = merged[permission];
93
- merged[permission] =
94
- existing && typeof existing !== "string" && typeof value !== "string"
95
- ? { ...existing, ...value }
96
- : clonePermissionValue(value);
72
+ catch (e) {
73
+ if (e.code === "ENOENT")
74
+ return undefined;
75
+ throw e;
97
76
  }
98
- return merged;
99
- }
100
- export function resolveReviewerPermission(agents, reviewer) {
101
- return mergePermissions(mergePermissions(DEFAULT_REVIEWER_PERMISSION, agents.permissions), reviewer.permissions);
102
- }
103
- export function resolveEditorPermission(agents, editor) {
104
- return mergePermissions(mergePermissions(DEFAULT_EDITOR_PERMISSION, agents.permissions), editor.permissions);
105
- }
106
- export function resolveTriageAgentPermission(agents, agent) {
107
- return mergePermissions(mergePermissions(DEFAULT_REVIEWER_PERMISSION, agents.permissions), agent.permissions);
108
- }
109
- export function resolveTriageCreatorPermission(agents, creator) {
110
- return mergePermissions(mergePermissions(DEFAULT_EDITOR_PERMISSION, agents.permissions), creator.permissions);
111
- }
112
- export function resolveAgents(config) {
113
- const agents = config.agents ?? {};
114
- const editor = config.merge?.editor;
115
- const creator = config.triage?.creator;
116
- const singleReviewAccount = config.review && config.mode !== "multi" ? config.account : undefined;
117
- const singleTriageAccount = config.triage && config.mode !== "multi" ? config.account : undefined;
118
- return {
119
- editor: editor
120
- ? {
121
- ...editor,
122
- model: normalizedModel(editor.model),
123
- permission: resolveEditorPermission(agents, editor),
124
- }
125
- : undefined,
126
- reviewers: (config.review?.reviewers ?? []).map((reviewer, index) => ({
127
- ...reviewer,
128
- account: singleReviewAccount ?? reviewer.account ?? "",
129
- key: reviewerKey(reviewer, index),
130
- index,
131
- model: normalizedModel(reviewer.model),
132
- permission: resolveReviewerPermission(agents, reviewer),
133
- })),
134
- triage: (config.triage?.voters ?? []).map((agent, index) => ({
135
- ...agent,
136
- account: singleTriageAccount ?? agent.account ?? "",
137
- key: triageAgentKey(agent, index),
138
- index,
139
- model: normalizedModel(agent.model),
140
- permission: resolveTriageAgentPermission(agents, agent),
141
- })),
142
- triageCreator: creator
143
- ? {
144
- ...creator,
145
- account: singleTriageAccount ?? creator.account ?? "",
146
- model: normalizedModel(creator.model),
147
- permission: resolveTriageCreatorPermission(agents, creator),
148
- }
149
- : undefined,
150
- };
151
- }
152
- function resolveTriageCategories(config) {
153
- return (config.triage?.categories ?? DEFAULT_TRIAGE_CATEGORIES).map((category) => ({
154
- description: category.description,
155
- id: category.id ?? "",
156
- labels: category.labels ?? [],
157
- types: category.types ?? [],
158
- }));
159
77
  }
160
- export function resolveRepository(config) {
161
- if (!config.github?.owner)
162
- throw new Error("github.owner is required");
163
- if (!config.github?.repo)
164
- throw new Error("github.repo is required");
165
- return {
166
- alias: config.github.repo,
167
- agents: resolveAgents(config),
168
- automation: {
169
- close: config.merge?.automation?.close ?? false,
170
- conflict: config.merge?.automation?.conflict ?? false,
171
- merge: config.merge?.automation?.merge ?? true,
172
- },
173
- checks: {
174
- exclude: config.review?.checks?.exclude ?? [],
175
- retryFailedJobs: config.review?.checks?.retryFailedJobs ?? 3,
176
- wait: config.review?.checks?.wait ?? true,
177
- waitAfterEdit: config.merge?.checks?.wait ?? true,
178
- waitBeforeReview: config.review?.checks?.wait ?? true,
179
- },
180
- concurrency: {
181
- runs: config.review?.concurrency?.runs ?? 3,
182
- reviewers: config.review?.concurrency?.reviewers ?? 3,
183
- },
184
- github: {
185
- apiRetryAttempts: config.github.apiRetryAttempts ?? 3,
186
- host: config.github.host ?? "github.com",
187
- owner: config.github.owner,
188
- repo: config.github.repo,
189
- },
190
- language: config.language,
191
- account: config.account,
192
- mode: config.mode ?? "single",
193
- merge: {
194
- approvalPolicy: config.review?.merge?.approvalPolicy ?? "majority",
195
- method: config.review?.merge?.method ?? "squash",
196
- auto: config.review?.merge?.auto ?? true,
197
- deleteBranch: config.review?.merge?.deleteBranch ?? true,
198
- queue: config.review?.merge?.queue ?? false,
199
- mergeQueue: config.review?.merge?.queue ?? false,
200
- maxThreadResolutionCycles: config.merge?.maxThreadResolutionCycles ?? 5,
201
- },
202
- prompts: {
203
- ciClassification: config.review?.prompts?.ciClassification,
204
- ciClassificationAfterEdit: config.merge?.prompts?.ciClassification,
205
- closeReconsideration: config.review?.prompts?.closeReconsideration,
206
- edit: config.merge?.prompts?.edit,
207
- editGuidelines: config.merge?.prompts?.editGuidelines,
208
- findingValidation: config.review?.prompts?.findingValidation,
209
- rereview: config.review?.prompts?.rereview,
210
- review: config.review?.prompts?.review,
211
- reviewGuidelines: config.review?.prompts?.reviewGuidelines,
212
- },
213
- reviewAutomation: {
214
- close: config.review?.automation?.close ?? false,
215
- merge: config.review?.automation?.merge ?? true,
216
- },
217
- safety: {
218
- allowAuthors: config.review?.safety?.allowAuthors ?? [],
219
- blockedPaths: config.review?.safety?.blockedPaths ?? [],
220
- maxChangedFiles: config.review?.safety?.maxChangedFiles,
221
- requiredLabels: config.review?.safety?.requiredLabels ?? [],
222
- },
223
- triage: {
224
- automation: {
225
- close: config.triage?.automation?.close ?? false,
226
- create: config.triage?.automation?.create ?? false,
227
- label: config.triage?.automation?.label ?? DEFAULT_TRIAGE_LABEL_RULES,
228
- merge: config.triage?.automation?.merge ?? false,
229
- review: config.triage?.automation?.review ?? false,
230
- },
231
- categories: resolveTriageCategories(config),
232
- concurrency: {
233
- runs: config.triage?.concurrency?.runs ?? 3,
234
- },
235
- output: config.triage?.output,
236
- prompts: config.triage?.prompts ?? {},
237
- reporter: config.triage?.reporter,
238
- safety: {
239
- allowAuthors: config.triage?.safety?.allowAuthors ?? [],
240
- allowMentionActors: config.triage?.safety?.allowMentionActors ?? [],
241
- allowMentionRoles: config.triage?.safety?.allowMentionRoles ?? [
242
- "AUTHOR",
243
- "OWNER",
244
- "MEMBER",
245
- "COLLABORATOR",
246
- ],
247
- blockedLabels: config.triage?.safety?.blockedLabels ?? [],
248
- requiredLabels: config.triage?.safety?.requiredLabels ?? ["triage"],
249
- },
250
- signals: config.triage?.signals ?? [],
251
- worktree: config.triage?.worktree,
252
- },
253
- };
78
+ export async function getConfig(input) {
79
+ const projectPath = join(input.directory, CONFIG_PATH.PROJECT);
80
+ const [models, ...data] = await Promise.all([
81
+ getModels(input),
82
+ readConfig(CONFIG_PATH.GLOBAL),
83
+ readConfig(projectPath),
84
+ ]);
85
+ const results = filterEmpty(data);
86
+ if (!results.length)
87
+ throw new Error(`No Magi config found. Expected ${CONFIG_PATH.GLOBAL} or ${projectPath}.`);
88
+ const config = results.reduce((prev, current) => merge(prev, current), DEFAULT_CONFIG);
89
+ config.github.url = `https://${config.github.host}/${config.github.owner}/${config.github.repo}`;
90
+ if (config.review.reviewers)
91
+ config.review.reviewers = config.review.reviewers.map((agent, index) => {
92
+ const { id, model, ...rest } = mergeAgent(config, agent);
93
+ return {
94
+ ...rest,
95
+ id: id || `reviewer-${index + 1}`,
96
+ model: resolveModel(models, model),
97
+ };
98
+ });
99
+ config.merge.editor = mergeAgent(config, config.merge.editor);
100
+ config.merge.editor.model = resolveModel(models, config.merge.editor.model);
101
+ if (config.triage.voters)
102
+ config.triage.voters = config.triage.voters.map((agent, index) => {
103
+ const { id, model, ...rest } = mergeAgent(config, agent);
104
+ return {
105
+ ...rest,
106
+ id: id || `voter-${index + 1}`,
107
+ model: resolveModel(models, model),
108
+ };
109
+ });
110
+ config.triage.creator = mergeAgent(config, config.triage.creator);
111
+ config.triage.creator.model = resolveModel(models, config.triage.creator.model);
112
+ return config;
254
113
  }