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