opencode-magi 0.10.0 → 0.11.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.
Files changed (126) hide show
  1. package/README.ja.md +254 -0
  2. package/README.md +31 -26
  3. package/dist/commands.js +11 -9
  4. package/dist/config/index.js +2 -0
  5. package/dist/config/resolve.js +120 -246
  6. package/dist/config/validate.js +103 -1110
  7. package/dist/constant.js +135 -0
  8. package/dist/graphql/index.generated.js +125 -0
  9. package/dist/graphql/index.js +49 -0
  10. package/dist/index.js +12 -800
  11. package/dist/magi.js +357 -0
  12. package/dist/permissions/common.json +22 -22
  13. package/dist/permissions/editor.json +12 -12
  14. package/dist/prompts/index.js +98 -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 +26 -0
  22. package/dist/prompts/merge/edit/task.md +10 -0
  23. package/dist/prompts/merge/edit/validate.json +60 -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 +21 -0
  28. package/dist/prompts/review/close-reconsideration/task.md +6 -0
  29. package/dist/prompts/review/close-reconsideration/validate.json +44 -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 +30 -0
  37. package/dist/prompts/{templates/review/rereview.md → review/rereview/task.md} +8 -11
  38. package/dist/prompts/review/rereview/validate.json +87 -0
  39. package/dist/prompts/review/review/output-contract.md +25 -0
  40. package/dist/prompts/{templates/review/review.md → review/review/task.md} +2 -5
  41. package/dist/prompts/review/review/validate.json +54 -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 +28 -0
  49. package/dist/prompts/triage/create/validate.json +73 -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 +18 -0
  58. package/dist/tools/clear/index.js +21 -0
  59. package/dist/tools/index.js +12 -0
  60. package/dist/tools/merge/action.js +47 -0
  61. package/dist/tools/merge/context.js +118 -0
  62. package/dist/tools/merge/editor.js +264 -0
  63. package/dist/tools/merge/index.js +151 -0
  64. package/dist/tools/merge/index.type.js +1 -0
  65. package/dist/tools/merge/merge.js +42 -0
  66. package/dist/tools/merge/report.js +73 -0
  67. package/dist/tools/review/action.js +429 -0
  68. package/dist/tools/review/check.js +295 -0
  69. package/dist/tools/review/context.js +258 -0
  70. package/dist/tools/review/index.js +104 -0
  71. package/dist/tools/review/index.type.js +1 -0
  72. package/dist/tools/review/report.js +143 -0
  73. package/dist/tools/review/review.js +145 -0
  74. package/dist/tools/review/reviewer.js +409 -0
  75. package/dist/tools/triage/index.js +16 -0
  76. package/dist/tools/validate/index.js +28 -0
  77. package/dist/utils/array.js +6 -0
  78. package/dist/utils/assertion.js +38 -0
  79. package/dist/utils/exec.js +14 -0
  80. package/dist/utils/fs.js +24 -0
  81. package/dist/utils/function.js +37 -0
  82. package/dist/utils/github.js +63 -0
  83. package/dist/utils/index.js +10 -0
  84. package/dist/utils/index.type.js +1 -0
  85. package/dist/utils/object.js +21 -0
  86. package/dist/utils/opencode.js +25 -0
  87. package/dist/utils/string.js +38 -0
  88. package/dist/utils/worker.js +30 -0
  89. package/package.json +20 -8
  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,342 +0,0 @@
1
- import { fetchIssue, fetchIssueCommentPage, fetchPullRequestClosingIssues, fetchPullRequestCommentPage, fetchPullRequestReviewThreadPage, fetchPullRequestSafetyMeta, } from "../github/commands";
2
- const LIMITS = {
3
- closingIssueComments: 20,
4
- commentBody: 4000,
5
- prComments: 20,
6
- referencedIssueComments: 10,
7
- reviewThreadComments: 20,
8
- reviewThreads: 50,
9
- };
10
- function escapeRegExp(value) {
11
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
12
- }
13
- function truncateBody(body) {
14
- if (body.length <= LIMITS.commentBody)
15
- return { body };
16
- return {
17
- body: `${body.slice(0, LIMITS.commentBody)}\n[truncated after ${LIMITS.commentBody} characters]`,
18
- truncated: true,
19
- };
20
- }
21
- function boundedComments(comments, limit) {
22
- return [...comments]
23
- .sort((a, b) => a.createdAt.localeCompare(b.createdAt))
24
- .slice(-limit)
25
- .map((comment) => ({
26
- author: comment.author,
27
- createdAt: comment.createdAt,
28
- id: comment.id,
29
- url: comment.url,
30
- ...truncateBody(comment.body),
31
- }));
32
- }
33
- function omittedCommentCount(input) {
34
- return input.omitted + Math.max(0, input.comments.length - input.limit);
35
- }
36
- function quoteEvidence(value) {
37
- const compact = value.replaceAll(/\s+/g, " ").trim();
38
- return compact.length > 120 ? `${compact.slice(0, 117)}...` : compact;
39
- }
40
- function errorText(error) {
41
- if (!error || typeof error !== "object")
42
- return String(error);
43
- const value = error;
44
- return [value.message, value.stderr, value.stdout]
45
- .filter((item) => typeof item === "string")
46
- .join("\n");
47
- }
48
- function isIssueLookupFailure(error) {
49
- const text = errorText(error);
50
- return (/could not resolve to an issue/i.test(text) ||
51
- /could not fetch issue #\d+/i.test(text) ||
52
- /not an issue/i.test(text));
53
- }
54
- function isIssueUrl(url) {
55
- return /\/issues\/\d+(?:$|[/?#])/i.test(url);
56
- }
57
- function issueReferencePattern(repository) {
58
- const host = escapeRegExp(repository.github.host || "github.com");
59
- const owner = escapeRegExp(repository.github.owner);
60
- const repo = escapeRegExp(repository.github.repo);
61
- return new RegExp(`(?:https?://${host}/${owner}/${repo}/issues/(\\d+)|#(\\d+))`, "gi");
62
- }
63
- function issueNumberFromMatch(match) {
64
- return Number(match[1] ?? match[2]);
65
- }
66
- function addRelationship(relationships, number, relationship, source) {
67
- const current = relationships.get(number);
68
- const nextRelationship = current?.relationship === "closing" || relationship === "closing"
69
- ? "closing"
70
- : "referenced";
71
- const sources = current?.sources ?? [];
72
- relationships.set(number, {
73
- number,
74
- relationship: nextRelationship,
75
- sources: sources.includes(source) ? sources : [...sources, source],
76
- });
77
- }
78
- function scanRelationshipText(input) {
79
- const referencePattern = issueReferencePattern(input.repository);
80
- const closingPattern = new RegExp(`\\b(?:close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)\\b[\\s\\S]{0,80}?${referencePattern.source}`, "gi");
81
- for (const match of input.text.matchAll(referencePattern)) {
82
- const number = issueNumberFromMatch(match);
83
- if (number === input.currentPr)
84
- continue;
85
- addRelationship(input.relationships, number, "referenced", `${input.label} "${quoteEvidence(match[0])}"`);
86
- }
87
- for (const match of input.text.matchAll(closingPattern)) {
88
- const number = Number(match[1] ?? match[2]);
89
- if (!number || number === input.currentPr)
90
- continue;
91
- addRelationship(input.relationships, number, "closing", `${input.label} "${quoteEvidence(match[0])}"`);
92
- }
93
- }
94
- export function collectIssueRelationships(input) {
95
- const relationships = new Map();
96
- for (const issue of input.closingIssues) {
97
- if (issue.number === input.pr.number)
98
- continue;
99
- addRelationship(relationships, issue.number, "closing", "GitHub closingIssuesReferences");
100
- }
101
- scanRelationshipText({
102
- currentPr: input.pr.number,
103
- label: "PR title",
104
- relationships,
105
- repository: input.repository,
106
- text: input.pr.title,
107
- });
108
- scanRelationshipText({
109
- currentPr: input.pr.number,
110
- label: "PR body",
111
- relationships,
112
- repository: input.repository,
113
- text: input.pr.body ?? "",
114
- });
115
- for (const comment of input.prComments) {
116
- scanRelationshipText({
117
- currentPr: input.pr.number,
118
- label: `PR comment ${comment.id}`,
119
- relationships,
120
- repository: input.repository,
121
- text: comment.body,
122
- });
123
- }
124
- for (const thread of input.reviewThreads) {
125
- for (const comment of thread.comments) {
126
- scanRelationshipText({
127
- currentPr: input.pr.number,
128
- label: `review thread ${thread.threadId} comment ${comment.commentId}`,
129
- relationships,
130
- repository: input.repository,
131
- text: comment.body,
132
- });
133
- }
134
- }
135
- return [...relationships.values()].sort((a, b) => a.number - b.number);
136
- }
137
- async function contextIssue(input) {
138
- const issue = input.issue ??
139
- (await fetchIssue(input.exec, input.repository, input.relationship.number));
140
- if (!isIssueUrl(issue.url)) {
141
- throw new Error(`Reference #${issue.number} resolved to ${issue.url}, not an Issue`);
142
- }
143
- const commentPage = await fetchIssueCommentPage(input.exec, input.repository, issue.number, input.limit);
144
- return {
145
- author: issue.author,
146
- body: issue.body,
147
- comments: boundedComments(commentPage.comments, input.limit),
148
- commentsOmitted: omittedCommentCount({
149
- comments: commentPage.comments,
150
- limit: input.limit,
151
- omitted: commentPage.omitted,
152
- }),
153
- number: issue.number,
154
- relationship: input.relationship.relationship,
155
- source: input.relationship.sources.join("; "),
156
- state: issue.state,
157
- title: issue.title,
158
- url: issue.url,
159
- };
160
- }
161
- async function contextIssueIfIssue(input) {
162
- try {
163
- return await contextIssue(input);
164
- }
165
- catch (error) {
166
- if (isIssueLookupFailure(error))
167
- return undefined;
168
- throw error;
169
- }
170
- }
171
- function presentIssue(issue) {
172
- return Boolean(issue);
173
- }
174
- function orderReviewThreads(threads) {
175
- return [...threads]
176
- .sort((a, b) => {
177
- if (a.isResolved !== b.isResolved)
178
- return a.isResolved ? 1 : -1;
179
- const aLatest = a.comments.at(-1)?.createdAt ?? "";
180
- const bLatest = b.comments.at(-1)?.createdAt ?? "";
181
- return bLatest.localeCompare(aLatest);
182
- })
183
- .slice(0, LIMITS.reviewThreads)
184
- .map((thread) => ({
185
- ...thread,
186
- comments: thread.comments
187
- .slice(-LIMITS.reviewThreadComments)
188
- .map((comment) => ({
189
- ...comment,
190
- ...truncateBody(comment.body),
191
- })),
192
- }));
193
- }
194
- export async function buildReviewContextSnapshot(input) {
195
- const [prCommentPage, reviewThreadPage, safetyMeta, closingIssues] = await Promise.all([
196
- fetchPullRequestCommentPage(input.exec, input.repository, input.pr.number, LIMITS.prComments),
197
- fetchPullRequestReviewThreadPage(input.exec, input.repository, input.pr.number, LIMITS.reviewThreads, LIMITS.reviewThreadComments),
198
- fetchPullRequestSafetyMeta(input.exec, input.repository, input.pr.number),
199
- fetchPullRequestClosingIssues(input.exec, input.repository, input.pr.number).catch(() => []),
200
- ]);
201
- const prComments = prCommentPage.comments;
202
- const orderedReviewThreads = orderReviewThreads(reviewThreadPage.threads);
203
- const prCommentsOmitted = omittedCommentCount({
204
- comments: prComments,
205
- limit: LIMITS.prComments,
206
- omitted: prCommentPage.omitted,
207
- });
208
- const relationships = collectIssueRelationships({
209
- closingIssues,
210
- pr: input.pr,
211
- prComments,
212
- repository: input.repository,
213
- reviewThreads: orderedReviewThreads,
214
- });
215
- const closingIssueMap = new Map(closingIssues.map((issue) => [issue.number, issue]));
216
- const closingRelationships = relationships.filter((relationship) => relationship.relationship === "closing");
217
- const referencedRelationships = relationships.filter((relationship) => relationship.relationship === "referenced");
218
- return {
219
- closingIssues: (await Promise.all(closingRelationships.map((relationship) => contextIssueIfIssue({
220
- exec: input.exec,
221
- issue: closingIssueMap.get(relationship.number),
222
- limit: LIMITS.closingIssueComments,
223
- relationship,
224
- repository: input.repository,
225
- })))).filter(presentIssue),
226
- pullRequest: {
227
- author: input.pr.author?.login ?? safetyMeta.author,
228
- baseRef: input.pr.baseRefName,
229
- baseSha: input.pr.baseRefOid,
230
- body: input.pr.body ?? "",
231
- changedFiles: safetyMeta.files,
232
- comments: boundedComments(prComments, LIMITS.prComments),
233
- commentsOmitted: prCommentsOmitted,
234
- headRef: input.pr.headRefName,
235
- headSha: input.pr.headRefOid,
236
- number: input.pr.number,
237
- relationship: "target",
238
- source: "/magi:review input",
239
- state: input.pr.state ?? "",
240
- title: input.pr.title,
241
- url: input.pr.url,
242
- },
243
- referencedIssues: (await Promise.all(referencedRelationships.map((relationship) => contextIssueIfIssue({
244
- exec: input.exec,
245
- limit: LIMITS.referencedIssueComments,
246
- relationship,
247
- repository: input.repository,
248
- })))).filter(presentIssue),
249
- reviewDiscussion: {
250
- prComments: boundedComments(prComments, LIMITS.prComments),
251
- prCommentsOmitted,
252
- reviewThreads: orderedReviewThreads,
253
- reviewThreadsOmitted: reviewThreadPage.omitted,
254
- },
255
- };
256
- }
257
- function indented(value) {
258
- return value.trim() ? value : "(empty)";
259
- }
260
- function renderOmissionNote(omitted, label, limit) {
261
- return omitted > 0
262
- ? `\n[omitted ${omitted} older ${label} due to limit ${limit}]`
263
- : "";
264
- }
265
- function renderComments(comments, omitted = 0, limit = comments.length) {
266
- if (!comments.length)
267
- return `(none)${renderOmissionNote(omitted, "comments", limit)}`;
268
- return (comments
269
- .map((comment) => {
270
- const suffix = comment.truncated ? " [truncated]" : "";
271
- return `- ${comment.createdAt} @${comment.author} (${comment.id})${suffix}\n${indented(comment.body)}`;
272
- })
273
- .join("\n") + renderOmissionNote(omitted, "comments", limit));
274
- }
275
- function renderIssue(issue) {
276
- return `<issue>
277
- number: ${issue.number}
278
- title: ${issue.title}
279
- url: ${issue.url}
280
- state: ${issue.state}
281
- author: ${issue.author}
282
- relationship: ${issue.relationship}
283
- source: ${issue.source}
284
- body:
285
- ${indented(issue.body)}
286
- comments:
287
- ${renderComments(issue.comments, issue.commentsOmitted, issue.relationship === "closing" ? LIMITS.closingIssueComments : LIMITS.referencedIssueComments)}
288
- </issue>`;
289
- }
290
- function renderThreads(threads, omitted = 0) {
291
- if (!threads.length) {
292
- return `(none)${renderOmissionNote(omitted, "review threads", LIMITS.reviewThreads)}`;
293
- }
294
- return (threads
295
- .map((thread) => {
296
- const comments = thread.comments
297
- .map((comment) => {
298
- const suffix = comment.truncated ? " [truncated]" : "";
299
- return ` - ${comment.createdAt} @${comment.author} (${comment.commentId})${suffix}\n${indented(comment.body)}`;
300
- })
301
- .join("\n") +
302
- renderOmissionNote(thread.omittedComments ?? 0, "thread comments", LIMITS.reviewThreadComments);
303
- return `- threadId: ${thread.threadId}\n resolved: ${Boolean(thread.isResolved)}\n path: ${thread.path}:${thread.line}\n comments:\n${comments}`;
304
- })
305
- .join("\n") +
306
- renderOmissionNote(omitted, "review threads", LIMITS.reviewThreads));
307
- }
308
- export function renderReviewContext(snapshot) {
309
- return [
310
- `<pull_request_context>
311
- number: ${snapshot.pullRequest.number}
312
- title: ${snapshot.pullRequest.title}
313
- url: ${snapshot.pullRequest.url}
314
- state: ${snapshot.pullRequest.state}
315
- author: ${snapshot.pullRequest.author}
316
- relationship: ${snapshot.pullRequest.relationship}
317
- source: ${snapshot.pullRequest.source}
318
- baseRef: ${snapshot.pullRequest.baseRef}
319
- headRef: ${snapshot.pullRequest.headRef}
320
- baseSha: ${snapshot.pullRequest.baseSha}
321
- headSha: ${snapshot.pullRequest.headSha}
322
- body:
323
- ${indented(snapshot.pullRequest.body)}
324
- comments:
325
- ${renderComments(snapshot.pullRequest.comments, snapshot.pullRequest.commentsOmitted, LIMITS.prComments)}
326
- changedFiles:
327
- ${snapshot.pullRequest.changedFiles.length ? snapshot.pullRequest.changedFiles.map((file) => `- ${file}`).join("\n") : "(none)"}
328
- </pull_request_context>`,
329
- `<closing_issues>
330
- ${snapshot.closingIssues.length ? snapshot.closingIssues.map(renderIssue).join("\n") : "(none)"}
331
- </closing_issues>`,
332
- `<referenced_issues>
333
- ${snapshot.referencedIssues.length ? snapshot.referencedIssues.map(renderIssue).join("\n") : "(none)"}
334
- </referenced_issues>`,
335
- `<review_discussion>
336
- prComments:
337
- ${renderComments(snapshot.reviewDiscussion.prComments, snapshot.reviewDiscussion.prCommentsOmitted, LIMITS.prComments)}
338
- reviewThreads:
339
- ${renderThreads(snapshot.reviewDiscussion.reviewThreads, snapshot.reviewDiscussion.reviewThreadsOmitted)}
340
- </review_discussion>`,
341
- ].join("\n\n");
342
- }