opencode-magi 0.6.0 → 0.6.1
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/dist/config/validate.js +0 -2
- package/dist/github/commands.js +44 -20
- package/dist/orchestrator/ci.js +11 -7
- package/dist/orchestrator/triage.js +3 -3
- package/dist/prompts/compose.js +0 -32
- package/dist/prompts/templates/triage/existing-pr.md +1 -1
- package/package.json +1 -1
- package/schema.json +0 -2
- package/dist/prompts/templates/triage/comment.md +0 -5
- package/dist/prompts/templates/triage/question.md +0 -5
package/dist/config/validate.js
CHANGED
|
@@ -138,13 +138,11 @@ const MERGE_PROMPT_KEYS = new Set([
|
|
|
138
138
|
const TRIAGE_PROMPT_KEYS = new Set([
|
|
139
139
|
"acceptance",
|
|
140
140
|
"category",
|
|
141
|
-
"comment",
|
|
142
141
|
"commentClassification",
|
|
143
142
|
"create",
|
|
144
143
|
"createGuidelines",
|
|
145
144
|
"duplicate",
|
|
146
145
|
"existingPr",
|
|
147
|
-
"question",
|
|
148
146
|
"reconsider",
|
|
149
147
|
]);
|
|
150
148
|
function githubHost(config) {
|
package/dist/github/commands.js
CHANGED
|
@@ -20,6 +20,20 @@ function errorText(error) {
|
|
|
20
20
|
.filter((item) => typeof item === "string")
|
|
21
21
|
.join("\n");
|
|
22
22
|
}
|
|
23
|
+
function isIssueTypeUnavailableText(text) {
|
|
24
|
+
return (/cannot query field ["']?issueType["']? on type ["']?Issue["']?/i.test(text) ||
|
|
25
|
+
/field ["']?issueType["']?.*(does not exist|doesn't exist|is not defined|not found).*type ["']?Issue["']?/i.test(text) ||
|
|
26
|
+
/undefinedField.*issueType/i.test(text) ||
|
|
27
|
+
/issueType.*unsupported field|unsupported field.*issueType/i.test(text));
|
|
28
|
+
}
|
|
29
|
+
function isIssueTypeUnavailableError(error) {
|
|
30
|
+
return isIssueTypeUnavailableText(errorText(error));
|
|
31
|
+
}
|
|
32
|
+
function isIssueTypeUnavailableGraphqlResponse(data) {
|
|
33
|
+
return (data.errors?.some((error) => isIssueTypeUnavailableText([error.message, error.type]
|
|
34
|
+
.filter((item) => typeof item === "string")
|
|
35
|
+
.join("\n"))) ?? false);
|
|
36
|
+
}
|
|
23
37
|
async function localCommitExists(exec, worktreePath, sha) {
|
|
24
38
|
try {
|
|
25
39
|
await exec(`git cat-file -e ${shellQuote(`${sha}^{commit}`)}`, {
|
|
@@ -184,26 +198,34 @@ export async function fetchPullRequestClosingIssues(exec, repository, pr) {
|
|
|
184
198
|
}
|
|
185
199
|
export async function fetchIssue(exec, repository, issue) {
|
|
186
200
|
const query = `query($owner: String!, $repo: String!, $issue: Int!) { repository(owner: $owner, name: $repo) { issue(number: $issue) { number title body url state author { login } labels(first: 100) { nodes { name } } issueType { name } } } }`;
|
|
201
|
+
let raw;
|
|
187
202
|
try {
|
|
188
|
-
|
|
189
|
-
const data = JSON.parse(raw);
|
|
190
|
-
const graphqlIssue = data.data?.repository?.issue;
|
|
191
|
-
if (!graphqlIssue)
|
|
192
|
-
throw new Error(`Could not fetch issue #${issue}`);
|
|
193
|
-
return {
|
|
194
|
-
author: graphqlIssue.author?.login ?? "",
|
|
195
|
-
body: graphqlIssue.body ?? "",
|
|
196
|
-
labels: graphqlIssue.labels?.nodes?.map((label) => label.name) ?? [],
|
|
197
|
-
number: graphqlIssue.number,
|
|
198
|
-
state: graphqlIssue.state,
|
|
199
|
-
title: graphqlIssue.title,
|
|
200
|
-
type: graphqlIssue.issueType?.name,
|
|
201
|
-
url: graphqlIssue.url,
|
|
202
|
-
};
|
|
203
|
+
raw = await exec(`gh api${ghHostOption(repository)} graphql -f query=${shellQuote(query)} -F owner=${shellQuote(repository.github.owner)} -F repo=${shellQuote(repository.github.repo)} -F issue=${issue}`);
|
|
203
204
|
}
|
|
204
|
-
catch {
|
|
205
|
-
|
|
205
|
+
catch (error) {
|
|
206
|
+
if (isIssueTypeUnavailableError(error)) {
|
|
207
|
+
return fetchIssueWithCli(exec, repository, issue);
|
|
208
|
+
}
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
const data = JSON.parse(raw);
|
|
212
|
+
const graphqlIssue = data.data?.repository?.issue;
|
|
213
|
+
if (!graphqlIssue) {
|
|
214
|
+
if (isIssueTypeUnavailableGraphqlResponse(data)) {
|
|
215
|
+
return fetchIssueWithCli(exec, repository, issue);
|
|
216
|
+
}
|
|
217
|
+
throw new Error(`Could not fetch issue #${issue}`);
|
|
206
218
|
}
|
|
219
|
+
return {
|
|
220
|
+
author: graphqlIssue.author?.login ?? "",
|
|
221
|
+
body: graphqlIssue.body ?? "",
|
|
222
|
+
labels: graphqlIssue.labels?.nodes?.map((label) => label.name) ?? [],
|
|
223
|
+
number: graphqlIssue.number,
|
|
224
|
+
state: graphqlIssue.state,
|
|
225
|
+
title: graphqlIssue.title,
|
|
226
|
+
type: graphqlIssue.issueType?.name,
|
|
227
|
+
url: graphqlIssue.url,
|
|
228
|
+
};
|
|
207
229
|
}
|
|
208
230
|
async function fetchIssueWithCli(exec, repository, issue) {
|
|
209
231
|
const raw = await exec(`gh issue view ${issue} --repo ${shellQuote(repoSpecifier(repository))} --json number,title,body,url,state,author,labels`);
|
|
@@ -458,8 +480,9 @@ export async function fetchPullRequestSafetyMeta(exec, repository, pr) {
|
|
|
458
480
|
}
|
|
459
481
|
return { author, changedFiles, files, labels };
|
|
460
482
|
}
|
|
461
|
-
export async function watchChecks(exec, repository, pr) {
|
|
462
|
-
|
|
483
|
+
export async function watchChecks(exec, repository, pr, options = {}) {
|
|
484
|
+
const requiredFlag = options.requiredOnly ? " --required" : "";
|
|
485
|
+
await exec(`gh pr checks ${pr} --repo ${shellQuote(repoSpecifier(repository))} --watch${requiredFlag}`);
|
|
463
486
|
}
|
|
464
487
|
export function isCancelledCheck(check) {
|
|
465
488
|
return check.bucket === "cancel" || check.state === "CANCELLED";
|
|
@@ -469,8 +492,9 @@ export function isFailedCheck(check) {
|
|
|
469
492
|
}
|
|
470
493
|
export async function fetchPullRequestChecks(exec, repository, pr, options = {}) {
|
|
471
494
|
let raw;
|
|
495
|
+
const requiredFlag = options.requiredOnly ? " --required" : "";
|
|
472
496
|
try {
|
|
473
|
-
raw = await exec(`gh pr checks ${pr} --repo ${shellQuote(repoSpecifier(repository))} --json name,state,bucket,link,workflow`);
|
|
497
|
+
raw = await exec(`gh pr checks ${pr} --repo ${shellQuote(repoSpecifier(repository))} --json name,state,bucket,link,workflow${requiredFlag}`);
|
|
474
498
|
}
|
|
475
499
|
catch (error) {
|
|
476
500
|
if (options.tolerateMissingChecks &&
|
package/dist/orchestrator/ci.js
CHANGED
|
@@ -229,7 +229,7 @@ async function watchRerunRuns(exec, repository, checks) {
|
|
|
229
229
|
await Promise.all(runIds.map((runId) => watchRun(exec, repository, runId)));
|
|
230
230
|
}
|
|
231
231
|
async function checksForHead(input) {
|
|
232
|
-
const checks = await fetchPullRequestChecks(input.exec, input.repository, input.pr, { tolerateMissingChecks: Boolean(input.headSha) });
|
|
232
|
+
const checks = await fetchPullRequestChecks(input.exec, input.repository, input.pr, { requiredOnly: true, tolerateMissingChecks: Boolean(input.headSha) });
|
|
233
233
|
const targetChecks = [];
|
|
234
234
|
let hasAnyActionCheck = false;
|
|
235
235
|
let hasTargetActionCheck = false;
|
|
@@ -254,7 +254,6 @@ async function checksForHead(input) {
|
|
|
254
254
|
return {
|
|
255
255
|
blocking: targetChecks.filter((check) => isFailedCheck(check) || isCancelledCheck(check)),
|
|
256
256
|
hasAnyActionCheck,
|
|
257
|
-
hasAnyCheck: checks.length > 0,
|
|
258
257
|
hasPending: targetChecks.some(isPendingCheck),
|
|
259
258
|
hasTargetActionCheck,
|
|
260
259
|
};
|
|
@@ -463,15 +462,17 @@ export async function waitForChecksWithClassification(input) {
|
|
|
463
462
|
await input.onProgress?.("waiting for CI checks");
|
|
464
463
|
for (let attempt = 0;; attempt += 1) {
|
|
465
464
|
try {
|
|
466
|
-
await watchChecks(input.exec, input.repository, input.pr
|
|
465
|
+
await watchChecks(input.exec, input.repository, input.pr, {
|
|
466
|
+
requiredOnly: true,
|
|
467
|
+
});
|
|
467
468
|
}
|
|
468
469
|
catch {
|
|
469
470
|
// gh exits non-zero for pending checks too; re-read check state below.
|
|
470
471
|
}
|
|
471
472
|
const target = await readTargetChecks();
|
|
472
473
|
const waitingForTargetHead = Boolean(input.headSha) &&
|
|
473
|
-
|
|
474
|
-
|
|
474
|
+
target.hasAnyActionCheck &&
|
|
475
|
+
!target.hasTargetActionCheck;
|
|
475
476
|
if (!waitingForTargetHead && !target.hasPending) {
|
|
476
477
|
await assignBlockingChecks(target.blocking);
|
|
477
478
|
break;
|
|
@@ -552,8 +553,11 @@ export async function waitForChecksWithClassification(input) {
|
|
|
552
553
|
try {
|
|
553
554
|
await input.onProgress?.("waiting for rerun CI checks");
|
|
554
555
|
await watchRerunRuns(input.exec, input.repository, rerunnable);
|
|
555
|
-
if (input.wait)
|
|
556
|
-
await watchChecks(input.exec, input.repository, input.pr
|
|
556
|
+
if (input.wait) {
|
|
557
|
+
await watchChecks(input.exec, input.repository, input.pr, {
|
|
558
|
+
requiredOnly: true,
|
|
559
|
+
});
|
|
560
|
+
}
|
|
557
561
|
}
|
|
558
562
|
catch {
|
|
559
563
|
// Re-read the PR checks below so stale failed checks are not trusted.
|
|
@@ -156,7 +156,7 @@ async function runVote(input) {
|
|
|
156
156
|
parse: input.parse,
|
|
157
157
|
permission: input.agent.permission,
|
|
158
158
|
prompt,
|
|
159
|
-
repairAttempts: 3,
|
|
159
|
+
repairAttempts: input.run.config.output?.repairAttempts ?? 3,
|
|
160
160
|
schemaName: input.schemaName,
|
|
161
161
|
signal: input.signal,
|
|
162
162
|
title: `Magi triage ${input.schemaName} #${input.issue} (${input.agent.key})`,
|
|
@@ -484,7 +484,7 @@ async function classifyMentionReplies(input) {
|
|
|
484
484
|
parse: parseTriageCommentClassificationOutput,
|
|
485
485
|
permission: agent.permission,
|
|
486
486
|
prompt,
|
|
487
|
-
repairAttempts: 3,
|
|
487
|
+
repairAttempts: input.input.config.output?.repairAttempts ?? 3,
|
|
488
488
|
schemaName: "triage comment classification",
|
|
489
489
|
signal: input.input.signal,
|
|
490
490
|
title: `Magi triage comment classification #${input.input.issue} (${agent.key})`,
|
|
@@ -823,7 +823,7 @@ async function createImplementationPr(input) {
|
|
|
823
823
|
parse: parseTriageCreatePrOutput,
|
|
824
824
|
permission: creator.permission,
|
|
825
825
|
prompt,
|
|
826
|
-
repairAttempts: 3,
|
|
826
|
+
repairAttempts: input.input.config.output?.repairAttempts ?? 3,
|
|
827
827
|
schemaName: "triage create PR",
|
|
828
828
|
signal: input.input.signal,
|
|
829
829
|
title: `Magi triage create PR #${input.issue.number}`,
|
package/dist/prompts/compose.js
CHANGED
|
@@ -339,38 +339,6 @@ async function composeTriageVotePrompt(input) {
|
|
|
339
339
|
.filter(Boolean)
|
|
340
340
|
.join("\n\n");
|
|
341
341
|
}
|
|
342
|
-
export async function composeTriageCommentPrompt(input) {
|
|
343
|
-
const values = triageValues(input);
|
|
344
|
-
const task = await taskBlock({
|
|
345
|
-
builtin: "triage/comment",
|
|
346
|
-
customPath: input.repository.triage?.prompts.comment,
|
|
347
|
-
directory: input.directory,
|
|
348
|
-
values,
|
|
349
|
-
});
|
|
350
|
-
return [
|
|
351
|
-
task,
|
|
352
|
-
languageBlock(input.repository.language),
|
|
353
|
-
`<context>\n${input.context}\n</context>`,
|
|
354
|
-
]
|
|
355
|
-
.filter(Boolean)
|
|
356
|
-
.join("\n\n");
|
|
357
|
-
}
|
|
358
|
-
export async function composeTriageQuestionPrompt(input) {
|
|
359
|
-
const values = triageValues(input);
|
|
360
|
-
const task = await taskBlock({
|
|
361
|
-
builtin: "triage/question",
|
|
362
|
-
customPath: input.repository.triage?.prompts.question,
|
|
363
|
-
directory: input.directory,
|
|
364
|
-
values,
|
|
365
|
-
});
|
|
366
|
-
return [
|
|
367
|
-
task,
|
|
368
|
-
languageBlock(input.repository.language),
|
|
369
|
-
`<context>\n${input.context}\n</context>`,
|
|
370
|
-
]
|
|
371
|
-
.filter(Boolean)
|
|
372
|
-
.join("\n\n");
|
|
373
|
-
}
|
|
374
342
|
export async function composeTriageCreatePrPrompt(input) {
|
|
375
343
|
const values = triageValues(input);
|
|
376
344
|
const task = await taskBlock({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Decide whether an existing related pull request already handles issue #{issue} in {owner}/{repo}.
|
|
2
2
|
|
|
3
|
-
Use only the provided context. Return
|
|
3
|
+
Use only the provided context. Return RELATED_PR_HANDLES_ISSUE only when the PR clearly addresses the issue. Otherwise return RELATED_PR_DOES_NOT_HANDLE_ISSUE.
|
|
4
4
|
|
|
5
5
|
<context>
|
|
6
6
|
{context}
|
package/package.json
CHANGED
package/schema.json
CHANGED
|
@@ -229,8 +229,6 @@
|
|
|
229
229
|
"duplicate": { "type": "string" },
|
|
230
230
|
"category": { "type": "string" },
|
|
231
231
|
"acceptance": { "type": "string" },
|
|
232
|
-
"question": { "type": "string" },
|
|
233
|
-
"comment": { "type": "string" },
|
|
234
232
|
"commentClassification": { "type": "string" },
|
|
235
233
|
"reconsider": { "type": "string" },
|
|
236
234
|
"create": { "type": "string" },
|