opencode-magi 0.2.0 → 0.4.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 +19 -0
- package/dist/commands.js +4 -0
- package/dist/config/output.js +11 -2
- package/dist/config/resolve.js +81 -1
- package/dist/config/validate.js +341 -3
- package/dist/config/worktree.js +8 -2
- package/dist/github/commands.js +381 -19
- package/dist/index.js +252 -26
- package/dist/orchestrator/ci.js +1 -1
- package/dist/orchestrator/findings.js +4 -3
- package/dist/orchestrator/inline-comments.js +79 -0
- package/dist/orchestrator/majority.js +14 -0
- package/dist/orchestrator/merge.js +108 -34
- package/dist/orchestrator/report.js +25 -7
- package/dist/orchestrator/review-context.js +309 -0
- package/dist/orchestrator/review.js +122 -14
- package/dist/orchestrator/run-manager.js +408 -17
- package/dist/orchestrator/triage.js +1119 -0
- package/dist/permissions/editor.json +8 -1
- package/dist/prompts/compose.js +163 -1
- package/dist/prompts/contracts.js +131 -18
- package/dist/prompts/output.js +173 -22
- package/dist/prompts/templates/merge/edit.md +12 -5
- package/dist/prompts/templates/review/review.md +6 -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 +5 -2
- package/schema.json +162 -5
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"bash": {
|
|
3
|
+
"bun *": "allow",
|
|
4
|
+
"bunx *": "allow",
|
|
5
|
+
"corepack *": "allow",
|
|
3
6
|
"git add*": "allow",
|
|
4
|
-
"git commit*": "allow"
|
|
7
|
+
"git commit*": "allow",
|
|
8
|
+
"npm *": "allow",
|
|
9
|
+
"npx *": "allow",
|
|
10
|
+
"pnpm *": "allow",
|
|
11
|
+
"yarn *": "allow"
|
|
5
12
|
},
|
|
6
13
|
"edit": "allow"
|
|
7
14
|
}
|
package/dist/prompts/compose.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { isAbsolute, join } from "node:path";
|
|
4
|
-
import { ciClassificationAfterEditOutputContract, ciClassificationOutputContract, closeReconsiderationOutputContract, editOutputContract, findingValidationOutputContract, rereviewCloseReconsiderationOutputContract, rereviewOutputContract, reviewOutputContract, } from "./contracts";
|
|
4
|
+
import { ciClassificationAfterEditOutputContract, ciClassificationOutputContract, closeReconsiderationOutputContract, editOutputContract, findingValidationOutputContract, rereviewCloseReconsiderationOutputContract, rereviewOutputContract, reviewOutputContract, triageActionOutputContract, triageCommentClassificationOutputContract, triageCreatePrOutputContract, triageDuplicateOutputContract, triageVoteOutputContract, } from "./contracts";
|
|
5
5
|
async function readOptionalPrompt(directory, path, values = {}) {
|
|
6
6
|
if (!path)
|
|
7
7
|
return "";
|
|
@@ -45,6 +45,7 @@ function reviewValues(input) {
|
|
|
45
45
|
headSha: input.headSha,
|
|
46
46
|
jsonEncodedWorktreePath: JSON.stringify(input.worktreePath),
|
|
47
47
|
pr: String(input.pr),
|
|
48
|
+
reviewContext: input.reviewContext ?? "",
|
|
48
49
|
worktreePath: input.worktreePath,
|
|
49
50
|
};
|
|
50
51
|
}
|
|
@@ -61,10 +62,27 @@ function editValues(input) {
|
|
|
61
62
|
return {
|
|
62
63
|
...repositoryValues(input.repository),
|
|
63
64
|
pr: String(input.pr),
|
|
65
|
+
reviewFindings: input.reviewFindings,
|
|
64
66
|
unresolvedThreads: input.unresolvedThreads,
|
|
65
67
|
worktreePath: input.worktreePath,
|
|
66
68
|
};
|
|
67
69
|
}
|
|
70
|
+
function triageValues(input) {
|
|
71
|
+
const categories = input.repository.triage?.categories ?? [];
|
|
72
|
+
const categoryOptions = categories
|
|
73
|
+
.map((category) => category.description
|
|
74
|
+
? `- ${category.id}: ${category.description}`
|
|
75
|
+
: `- ${category.id}`)
|
|
76
|
+
.join("\n");
|
|
77
|
+
return {
|
|
78
|
+
...repositoryValues(input.repository),
|
|
79
|
+
author: input.author ?? "",
|
|
80
|
+
categoryOptions,
|
|
81
|
+
context: input.context,
|
|
82
|
+
issue: String(input.issue),
|
|
83
|
+
worktreePath: input.worktreePath ?? "",
|
|
84
|
+
};
|
|
85
|
+
}
|
|
68
86
|
function personaBlock(persona) {
|
|
69
87
|
return persona ? `<persona>\n${persona}\n</persona>` : "";
|
|
70
88
|
}
|
|
@@ -76,6 +94,9 @@ function previousReviewBlock(previousReview) {
|
|
|
76
94
|
? `<previous_review>\n${previousReview.trim()}\n</previous_review>`
|
|
77
95
|
: "";
|
|
78
96
|
}
|
|
97
|
+
function reviewContextBlock(reviewContext) {
|
|
98
|
+
return reviewContext?.trim() ? reviewContext.trim() : "";
|
|
99
|
+
}
|
|
79
100
|
async function reviewGuidelinesBlock(input) {
|
|
80
101
|
const body = (await readOptionalPrompt(input.directory, input.path, input.values)).trim();
|
|
81
102
|
return body ? `<review_guidelines>\n${body}\n</review_guidelines>` : "";
|
|
@@ -84,8 +105,15 @@ async function editGuidelinesBlock(input) {
|
|
|
84
105
|
const body = (await readOptionalPrompt(input.directory, input.path, input.values)).trim();
|
|
85
106
|
return body ? `<edit_guidelines>\n${body}\n</edit_guidelines>` : "";
|
|
86
107
|
}
|
|
108
|
+
async function createGuidelinesBlock(input) {
|
|
109
|
+
const body = (await readOptionalPrompt(input.directory, input.path, input.values)).trim();
|
|
110
|
+
return body ? `<create_guidelines>\n${body}\n</create_guidelines>` : "";
|
|
111
|
+
}
|
|
87
112
|
async function sessionContextBlocks(input) {
|
|
88
113
|
return [
|
|
114
|
+
input.includeSessionContext
|
|
115
|
+
? reviewContextBlock(input.values.reviewContext)
|
|
116
|
+
: "",
|
|
89
117
|
input.includeSessionContext ? languageBlock(input.repository.language) : "",
|
|
90
118
|
input.includeSessionContext ? personaBlock(input.reviewer.persona) : "",
|
|
91
119
|
input.includeReviewGuidelines
|
|
@@ -107,6 +135,7 @@ export async function composeReviewPrompt(input) {
|
|
|
107
135
|
});
|
|
108
136
|
return [
|
|
109
137
|
task,
|
|
138
|
+
reviewContextBlock(input.reviewContext),
|
|
110
139
|
languageBlock(input.repository.language),
|
|
111
140
|
personaBlock(input.reviewer.persona),
|
|
112
141
|
await reviewGuidelinesBlock({
|
|
@@ -129,6 +158,7 @@ export async function composeRereviewPrompt(input) {
|
|
|
129
158
|
});
|
|
130
159
|
return [
|
|
131
160
|
task,
|
|
161
|
+
reviewContextBlock(input.reviewContext),
|
|
132
162
|
input.includeSessionContext === false
|
|
133
163
|
? ""
|
|
134
164
|
: languageBlock(input.repository.language),
|
|
@@ -292,3 +322,135 @@ export async function composeCiClassificationAfterEditPrompt(input) {
|
|
|
292
322
|
.filter(Boolean)
|
|
293
323
|
.join("\n\n");
|
|
294
324
|
}
|
|
325
|
+
async function composeTriageVotePrompt(input) {
|
|
326
|
+
const values = triageValues(input);
|
|
327
|
+
const task = await taskBlock({
|
|
328
|
+
builtin: `triage/${input.builtin}`,
|
|
329
|
+
customPath: input.customPath,
|
|
330
|
+
directory: input.directory,
|
|
331
|
+
values,
|
|
332
|
+
});
|
|
333
|
+
return [
|
|
334
|
+
task,
|
|
335
|
+
languageBlock(input.repository.language),
|
|
336
|
+
personaBlock(input.reviewer.persona),
|
|
337
|
+
input.outputContract,
|
|
338
|
+
]
|
|
339
|
+
.filter(Boolean)
|
|
340
|
+
.join("\n\n");
|
|
341
|
+
}
|
|
342
|
+
export async function composeTriageActionPrompt(input) {
|
|
343
|
+
return composeTriageVotePrompt({
|
|
344
|
+
...input,
|
|
345
|
+
builtin: "action",
|
|
346
|
+
customPath: input.repository.triage?.prompts.action,
|
|
347
|
+
outputContract: triageActionOutputContract,
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
export async function composeTriageCommentPrompt(input) {
|
|
351
|
+
const values = triageValues(input);
|
|
352
|
+
const task = await taskBlock({
|
|
353
|
+
builtin: "triage/comment",
|
|
354
|
+
customPath: input.repository.triage?.prompts.comment,
|
|
355
|
+
directory: input.directory,
|
|
356
|
+
values,
|
|
357
|
+
});
|
|
358
|
+
return [
|
|
359
|
+
task,
|
|
360
|
+
languageBlock(input.repository.language),
|
|
361
|
+
`<context>\n${input.context}\n</context>`,
|
|
362
|
+
]
|
|
363
|
+
.filter(Boolean)
|
|
364
|
+
.join("\n\n");
|
|
365
|
+
}
|
|
366
|
+
export async function composeTriageQuestionPrompt(input) {
|
|
367
|
+
const values = triageValues(input);
|
|
368
|
+
const task = await taskBlock({
|
|
369
|
+
builtin: "triage/question",
|
|
370
|
+
customPath: input.repository.triage?.prompts.question,
|
|
371
|
+
directory: input.directory,
|
|
372
|
+
values,
|
|
373
|
+
});
|
|
374
|
+
return [
|
|
375
|
+
task,
|
|
376
|
+
languageBlock(input.repository.language),
|
|
377
|
+
`<context>\n${input.context}\n</context>`,
|
|
378
|
+
]
|
|
379
|
+
.filter(Boolean)
|
|
380
|
+
.join("\n\n");
|
|
381
|
+
}
|
|
382
|
+
export async function composeTriageCreatePrPrompt(input) {
|
|
383
|
+
const values = triageValues(input);
|
|
384
|
+
const task = await taskBlock({
|
|
385
|
+
builtin: "triage/create",
|
|
386
|
+
customPath: input.repository.triage?.prompts.create,
|
|
387
|
+
directory: input.directory,
|
|
388
|
+
values,
|
|
389
|
+
});
|
|
390
|
+
const persona = input.repository.agents.triageCreator?.persona;
|
|
391
|
+
return [
|
|
392
|
+
task,
|
|
393
|
+
languageBlock(input.repository.language),
|
|
394
|
+
personaBlock(persona),
|
|
395
|
+
await createGuidelinesBlock({
|
|
396
|
+
directory: input.directory,
|
|
397
|
+
path: input.repository.triage?.prompts.createGuidelines,
|
|
398
|
+
values,
|
|
399
|
+
}),
|
|
400
|
+
triageCreatePrOutputContract,
|
|
401
|
+
]
|
|
402
|
+
.filter(Boolean)
|
|
403
|
+
.join("\n\n");
|
|
404
|
+
}
|
|
405
|
+
export async function composeTriageExistingPrPrompt(input) {
|
|
406
|
+
return composeTriageVotePrompt({
|
|
407
|
+
...input,
|
|
408
|
+
builtin: "existing-pr",
|
|
409
|
+
customPath: input.repository.triage?.prompts.existingPr,
|
|
410
|
+
outputContract: triageVoteOutputContract('"RELATED_PR_HANDLES_ISSUE" | "RELATED_PR_DOES_NOT_HANDLE_ISSUE"'),
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
export async function composeTriageDuplicatePrompt(input) {
|
|
414
|
+
return composeTriageVotePrompt({
|
|
415
|
+
...input,
|
|
416
|
+
builtin: "duplicate",
|
|
417
|
+
customPath: input.repository.triage?.prompts.duplicate,
|
|
418
|
+
outputContract: triageDuplicateOutputContract,
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
export async function composeTriageCategoryPrompt(input) {
|
|
422
|
+
const categories = input.repository.triage?.categories ?? [];
|
|
423
|
+
const votes = ["ASK", ...categories.map((category) => category.id)]
|
|
424
|
+
.map((vote) => JSON.stringify(vote))
|
|
425
|
+
.join(" | ");
|
|
426
|
+
return composeTriageVotePrompt({
|
|
427
|
+
...input,
|
|
428
|
+
builtin: "category",
|
|
429
|
+
customPath: input.repository.triage?.prompts.category,
|
|
430
|
+
outputContract: triageVoteOutputContract(votes),
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
export async function composeTriageAcceptancePrompt(input) {
|
|
434
|
+
return composeTriageVotePrompt({
|
|
435
|
+
...input,
|
|
436
|
+
builtin: "acceptance",
|
|
437
|
+
customPath: input.repository.triage?.prompts.acceptance,
|
|
438
|
+
outputContract: triageVoteOutputContract('"YES" | "NO" | "ASK"'),
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
export async function composeTriageCommentClassificationPrompt(input) {
|
|
442
|
+
return composeTriageVotePrompt({
|
|
443
|
+
...input,
|
|
444
|
+
builtin: "comment-classification",
|
|
445
|
+
customPath: input.repository.triage?.prompts.commentClassification,
|
|
446
|
+
outputContract: triageCommentClassificationOutputContract,
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
export async function composeTriageReconsiderPrompt(input) {
|
|
450
|
+
return composeTriageVotePrompt({
|
|
451
|
+
...input,
|
|
452
|
+
builtin: "reconsider",
|
|
453
|
+
customPath: input.repository.triage?.prompts.reconsider,
|
|
454
|
+
outputContract: triageVoteOutputContract('"YES" | "NO" | "ASK"'),
|
|
455
|
+
});
|
|
456
|
+
}
|
|
@@ -15,16 +15,26 @@ The object must match this shape:
|
|
|
15
15
|
"perspective": "Optional review perspective."
|
|
16
16
|
}
|
|
17
17
|
],
|
|
18
|
+
"requirementFindings": [
|
|
19
|
+
{
|
|
20
|
+
"issueNumber": 47,
|
|
21
|
+
"requirement": "Required closing-issue behavior that is missing.",
|
|
22
|
+
"evidence": "Why the PR does not satisfy the requirement.",
|
|
23
|
+
"fix": "How to satisfy the requirement."
|
|
24
|
+
}
|
|
25
|
+
],
|
|
18
26
|
"reason": "Required only for CLOSE."
|
|
19
27
|
}
|
|
20
28
|
|
|
21
29
|
Rules:
|
|
22
|
-
- MERGE requires
|
|
23
|
-
- CHANGES_REQUESTED requires at least one finding.
|
|
24
|
-
- CLOSE requires a reason and
|
|
30
|
+
- MERGE requires empty findings and requirementFindings arrays.
|
|
31
|
+
- CHANGES_REQUESTED requires at least one finding or requirementFinding.
|
|
32
|
+
- CLOSE requires a reason and empty findings and requirementFindings arrays.
|
|
25
33
|
- path must be repository-relative.
|
|
26
|
-
- line
|
|
27
|
-
-
|
|
34
|
+
- line is optional. Include line only when the finding targets a valid line inside the PR diff hunk.
|
|
35
|
+
- startLine is allowed only when line is present and must also refer to a line inside the PR diff hunk.
|
|
36
|
+
- Omit startLine for single-line findings and omit line for file-level or body-only findings.
|
|
37
|
+
- Use requirementFindings only for missing closing-issue requirements; use findings for ordinary file-level issues that do not map cleanly to a diff line.
|
|
28
38
|
</output_contract>`.trim();
|
|
29
39
|
export const rereviewOutputContract = `
|
|
30
40
|
<output_contract>
|
|
@@ -36,15 +46,18 @@ The object must match this shape:
|
|
|
36
46
|
"resolve": [{ "commentId": 123, "threadId": "..." }],
|
|
37
47
|
"followUps": [{ "commentId": 123, "body": "..." }],
|
|
38
48
|
"newFindings": [{ "path": "relative/path.ext", "line": 123, "startLine": 120, "body": "..." }],
|
|
49
|
+
"requirementFindings": [{ "issueNumber": 47, "requirement": "Missing requirement.", "evidence": "Why it is missing.", "fix": "How to fix it." }],
|
|
39
50
|
"reason": "Required only for CLOSE."
|
|
40
51
|
}
|
|
41
52
|
|
|
42
53
|
Rules:
|
|
43
|
-
- MERGE requires empty followUps and
|
|
44
|
-
- CHANGES_REQUESTED requires at least one followUp or
|
|
45
|
-
- CLOSE requires a reason and empty followUps and
|
|
46
|
-
- line
|
|
47
|
-
-
|
|
54
|
+
- MERGE requires empty followUps, newFindings, and requirementFindings arrays.
|
|
55
|
+
- CHANGES_REQUESTED requires at least one followUp, newFinding, or requirementFinding.
|
|
56
|
+
- CLOSE requires a reason and empty followUps, newFindings, and requirementFindings arrays.
|
|
57
|
+
- line is optional. Include line only when the newFinding targets a valid line inside the latest PR diff hunk.
|
|
58
|
+
- startLine is allowed only when line is present and must also refer to a line inside the latest PR diff hunk.
|
|
59
|
+
- Omit startLine for single-line findings and omit line for file-level or body-only findings.
|
|
60
|
+
- Use requirementFindings only for missing closing-issue requirements; use newFindings for ordinary file-level issues that do not map cleanly to a diff line.
|
|
48
61
|
</output_contract>`.trim();
|
|
49
62
|
export const findingValidationOutputContract = `
|
|
50
63
|
<output_contract>
|
|
@@ -83,14 +96,17 @@ The object must match this shape:
|
|
|
83
96
|
"issue": "What is wrong.",
|
|
84
97
|
"fix": "How to fix it."
|
|
85
98
|
}
|
|
86
|
-
]
|
|
99
|
+
],
|
|
100
|
+
"requirementFindings": [{ "issueNumber": 47, "requirement": "Missing requirement.", "evidence": "Why it is missing.", "fix": "How to fix it." }]
|
|
87
101
|
}
|
|
88
102
|
|
|
89
103
|
Rules:
|
|
90
|
-
- MERGE requires
|
|
91
|
-
- CHANGES_REQUESTED requires at least one finding.
|
|
104
|
+
- MERGE requires empty findings and requirementFindings arrays.
|
|
105
|
+
- CHANGES_REQUESTED requires at least one finding or requirementFinding.
|
|
92
106
|
- CLOSE is not allowed in this reconsideration step.
|
|
93
|
-
-
|
|
107
|
+
- line is optional. Include line only when the finding targets a valid line inside the PR diff hunk.
|
|
108
|
+
- startLine is allowed only when line is present.
|
|
109
|
+
- Omit startLine for single-line findings and omit line for file-level or body-only findings.
|
|
94
110
|
</output_contract>`.trim();
|
|
95
111
|
export const rereviewCloseReconsiderationOutputContract = `
|
|
96
112
|
<output_contract>
|
|
@@ -101,14 +117,17 @@ The object must match this shape:
|
|
|
101
117
|
"verdict": "MERGE" | "CHANGES_REQUESTED",
|
|
102
118
|
"resolve": [{ "commentId": 123, "threadId": "..." }],
|
|
103
119
|
"followUps": [{ "commentId": 123, "body": "..." }],
|
|
104
|
-
"newFindings": [{ "path": "relative/path.ext", "line": 123, "startLine": 120, "body": "..." }]
|
|
120
|
+
"newFindings": [{ "path": "relative/path.ext", "line": 123, "startLine": 120, "body": "..." }],
|
|
121
|
+
"requirementFindings": [{ "issueNumber": 47, "requirement": "Missing requirement.", "evidence": "Why it is missing.", "fix": "How to fix it." }]
|
|
105
122
|
}
|
|
106
123
|
|
|
107
124
|
Rules:
|
|
108
|
-
- MERGE requires empty followUps and
|
|
109
|
-
- CHANGES_REQUESTED requires at least one followUp or
|
|
125
|
+
- MERGE requires empty followUps, newFindings, and requirementFindings arrays.
|
|
126
|
+
- CHANGES_REQUESTED requires at least one followUp, newFinding, or requirementFinding.
|
|
110
127
|
- CLOSE is not allowed in this reconsideration step.
|
|
111
|
-
-
|
|
128
|
+
- line is optional. Include line only when the newFinding targets a valid line inside the latest PR diff hunk.
|
|
129
|
+
- startLine is allowed only when line is present.
|
|
130
|
+
- Omit startLine for single-line findings and omit line for file-level or body-only findings.
|
|
112
131
|
</output_contract>`.trim();
|
|
113
132
|
export const editOutputContract = `
|
|
114
133
|
<output_contract>
|
|
@@ -135,6 +154,32 @@ Rules:
|
|
|
135
154
|
- responses must include a reply for each thread you addressed.
|
|
136
155
|
- REPLIED requires filesTouched to be empty and at least one DISAGREE or ASK response.
|
|
137
156
|
</output_contract>`.trim();
|
|
157
|
+
export const triageCreatePrOutputContract = `
|
|
158
|
+
<output_contract>
|
|
159
|
+
Return exactly one JSON object and nothing else. Do not wrap it in markdown.
|
|
160
|
+
|
|
161
|
+
The object must match this shape:
|
|
162
|
+
{
|
|
163
|
+
"mode": "EDITED" | "REPLIED",
|
|
164
|
+
"commitSha": "full sha, required only when mode is EDITED; omit when mode is REPLIED",
|
|
165
|
+
"commitMessage": "fix(scope): short description, required only when mode is EDITED; omit when mode is REPLIED",
|
|
166
|
+
"filesTouched": ["relative/path.ext"],
|
|
167
|
+
"pullRequest": {
|
|
168
|
+
"title": "PR title, required only when mode is EDITED; omit when mode is REPLIED",
|
|
169
|
+
"body": "PR body, required only when mode is EDITED; omit when mode is REPLIED"
|
|
170
|
+
},
|
|
171
|
+
"responses": [{ "commentId": 123, "action": "FIXED" | "DISAGREE" | "ASK", "body": "Fixed." }]
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
Rules:
|
|
175
|
+
- Use EDITED only when you edited files, staged changes, and committed.
|
|
176
|
+
- Use REPLIED when you only replied without code changes.
|
|
177
|
+
- For EDITED, pullRequest.title and pullRequest.body must be non-empty and follow the repository's PR conventions.
|
|
178
|
+
- Do not push or create the PR. The orchestrator pushes and creates the PR using pullRequest exactly as provided.
|
|
179
|
+
- filesTouched must include every final changed file.
|
|
180
|
+
- responses may be empty when no review threads were addressed.
|
|
181
|
+
- REPLIED requires filesTouched to be empty and at least one DISAGREE or ASK response.
|
|
182
|
+
</output_contract>`.trim();
|
|
138
183
|
export const ciClassificationOutputContract = `
|
|
139
184
|
<output_contract>
|
|
140
185
|
Return exactly one JSON object and nothing else. Do not wrap it in markdown.
|
|
@@ -171,6 +216,66 @@ Rules:
|
|
|
171
216
|
- SCOPE_OUT means the failure is likely flaky, external, or infrastructure-related and may be rerun.
|
|
172
217
|
- If uncertain, choose SCOPE_IN.
|
|
173
218
|
</output_contract>`.trim();
|
|
219
|
+
export function triageVoteOutputContract(votes) {
|
|
220
|
+
return `
|
|
221
|
+
<output_contract>
|
|
222
|
+
Return exactly one JSON object and nothing else. Do not wrap it in markdown.
|
|
223
|
+
|
|
224
|
+
The object must match this shape:
|
|
225
|
+
{
|
|
226
|
+
"vote": ${votes},
|
|
227
|
+
"reason": "Short rationale."
|
|
228
|
+
}
|
|
229
|
+
</output_contract>`.trim();
|
|
230
|
+
}
|
|
231
|
+
export const triageDuplicateOutputContract = `
|
|
232
|
+
<output_contract>
|
|
233
|
+
Return exactly one JSON object and nothing else. Do not wrap it in markdown.
|
|
234
|
+
|
|
235
|
+
The object must match this shape:
|
|
236
|
+
{
|
|
237
|
+
"vote": "DUPLICATE" | "NOT_DUPLICATE",
|
|
238
|
+
"duplicateOf": 123,
|
|
239
|
+
"reason": "Short rationale."
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
Rules:
|
|
243
|
+
- duplicateOf is required only when vote is DUPLICATE.
|
|
244
|
+
- duplicateOf must be one of the provided duplicate candidate issue numbers.
|
|
245
|
+
</output_contract>`.trim();
|
|
246
|
+
export const triageCommentClassificationOutputContract = `
|
|
247
|
+
<output_contract>
|
|
248
|
+
Return exactly one JSON object and nothing else. Do not wrap it in markdown.
|
|
249
|
+
|
|
250
|
+
The object must match this shape:
|
|
251
|
+
{
|
|
252
|
+
"comments": [
|
|
253
|
+
{
|
|
254
|
+
"commentId": 123,
|
|
255
|
+
"classification": "OBJECTION" | "NEW_EVIDENCE" | "CLARIFICATION" | "ACKNOWLEDGEMENT" | "UNRELATED",
|
|
256
|
+
"reason": "Short rationale."
|
|
257
|
+
}
|
|
258
|
+
]
|
|
259
|
+
}
|
|
260
|
+
</output_contract>`.trim();
|
|
261
|
+
export const triageActionOutputContract = `
|
|
262
|
+
<output_contract>
|
|
263
|
+
Return exactly one JSON object and nothing else. Do not wrap it in markdown.
|
|
264
|
+
|
|
265
|
+
The object must match this shape:
|
|
266
|
+
{
|
|
267
|
+
"action": "ASK" | "COMMENT" | "CLOSE" | "PR" | "CLEAR_ONLY",
|
|
268
|
+
"reason": "Short rationale."
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
Rules:
|
|
272
|
+
- Choose only an action listed as allowed in the task context.
|
|
273
|
+
- ASK means post an author-mentioned question and do not close, create a PR, or clear labels.
|
|
274
|
+
- COMMENT means post a decision comment only.
|
|
275
|
+
- CLOSE means post a decision comment and close the issue.
|
|
276
|
+
- PR means post a decision comment and create an implementation PR.
|
|
277
|
+
- CLEAR_ONLY means clear labels without posting a comment.
|
|
278
|
+
</output_contract>`.trim();
|
|
174
279
|
const outputContractsBySchemaName = {
|
|
175
280
|
"CI classification": ciClassificationOutputContract,
|
|
176
281
|
"close reconsideration": closeReconsiderationOutputContract,
|
|
@@ -179,6 +284,14 @@ const outputContractsBySchemaName = {
|
|
|
179
284
|
rereview: rereviewOutputContract,
|
|
180
285
|
"rereview close reconsideration": rereviewCloseReconsiderationOutputContract,
|
|
181
286
|
review: reviewOutputContract,
|
|
287
|
+
"triage action": triageActionOutputContract,
|
|
288
|
+
"triage acceptance": triageVoteOutputContract('"YES" | "NO" | "ASK"'),
|
|
289
|
+
"triage category": triageVoteOutputContract('"ASK" or one of the configured category IDs'),
|
|
290
|
+
"triage create PR": triageCreatePrOutputContract,
|
|
291
|
+
"triage comment classification": triageCommentClassificationOutputContract,
|
|
292
|
+
"triage duplicate": triageDuplicateOutputContract,
|
|
293
|
+
"triage existing PR": triageVoteOutputContract('"RELATED_PR_HANDLES_ISSUE" | "RELATED_PR_DOES_NOT_HANDLE_ISSUE"'),
|
|
294
|
+
"triage reconsider": triageVoteOutputContract('"YES" | "NO" | "ASK"'),
|
|
182
295
|
};
|
|
183
296
|
export function repairPrompt(schemaName) {
|
|
184
297
|
const outputContract = outputContractsBySchemaName[schemaName];
|