edsger 0.21.10 ā 0.22.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/dist/commands/workflow/config/phase-configs.js +4 -4
- package/dist/phases/code-implementation/index.js +3 -0
- package/dist/phases/code-refine/index.d.ts +12 -1
- package/dist/phases/code-refine/index.js +12 -5
- package/dist/phases/code-refine/prompts.d.ts +3 -2
- package/dist/phases/code-refine/prompts.js +49 -4
- package/dist/phases/code-review/index.d.ts +12 -1
- package/dist/phases/code-review/index.js +59 -8
- package/dist/services/branches.d.ts +1 -0
- package/dist/services/branches.js +11 -4
- package/dist/utils/conflict-resolver.d.ts +6 -0
- package/dist/utils/conflict-resolver.js +21 -8
- package/dist/utils/git-branch-manager.d.ts +6 -0
- package/dist/utils/git-branch-manager.js +2 -1
- package/package.json +1 -1
|
@@ -14,7 +14,7 @@ import { getGitHubConfig } from '../../../api/github.js';
|
|
|
14
14
|
/**
|
|
15
15
|
* Wrapper for code-refine phase to inject GitHub token
|
|
16
16
|
*/
|
|
17
|
-
const executeCodeRefine = async (options, config) => {
|
|
17
|
+
const executeCodeRefine = async (options, config, checklistContext) => {
|
|
18
18
|
const githubConfig = await getGitHubConfig(options.featureId, options.verbose);
|
|
19
19
|
if (!githubConfig.configured || !githubConfig.token) {
|
|
20
20
|
return {
|
|
@@ -27,7 +27,7 @@ const executeCodeRefine = async (options, config) => {
|
|
|
27
27
|
featureId: options.featureId,
|
|
28
28
|
githubToken: githubConfig.token,
|
|
29
29
|
verbose: options.verbose,
|
|
30
|
-
}, config);
|
|
30
|
+
}, config, checklistContext);
|
|
31
31
|
};
|
|
32
32
|
/**
|
|
33
33
|
* Wrapper for code-refine-verification phase to inject GitHub token
|
|
@@ -51,7 +51,7 @@ const executeCodeRefineVerification = async (options, config) => {
|
|
|
51
51
|
/**
|
|
52
52
|
* Wrapper for code-review phase to inject GitHub token
|
|
53
53
|
*/
|
|
54
|
-
const executeCodeReview = async (options, config) => {
|
|
54
|
+
const executeCodeReview = async (options, config, checklistContext) => {
|
|
55
55
|
const githubConfig = await getGitHubConfig(options.featureId, options.verbose);
|
|
56
56
|
if (!githubConfig.configured || !githubConfig.token) {
|
|
57
57
|
return {
|
|
@@ -64,7 +64,7 @@ const executeCodeReview = async (options, config) => {
|
|
|
64
64
|
featureId: options.featureId,
|
|
65
65
|
githubToken: githubConfig.token,
|
|
66
66
|
verbose: options.verbose,
|
|
67
|
-
}, config);
|
|
67
|
+
}, config, checklistContext);
|
|
68
68
|
};
|
|
69
69
|
// Pipeline phase configurations
|
|
70
70
|
export const phaseConfigs = [
|
|
@@ -117,6 +117,7 @@ export const implementFeatureCode = async (options, config, checklistContext) =>
|
|
|
117
117
|
let actualBaseBranch = baseBranch;
|
|
118
118
|
let rebaseTargetBranch;
|
|
119
119
|
let originalBaseBranch;
|
|
120
|
+
let baseBranchCompleted = false;
|
|
120
121
|
if (currentBranch && currentBranch.base_branch_id) {
|
|
121
122
|
try {
|
|
122
123
|
const allBranches = await getBranches({
|
|
@@ -127,6 +128,7 @@ export const implementFeatureCode = async (options, config, checklistContext) =>
|
|
|
127
128
|
actualBaseBranch = baseBranchInfo.baseBranch;
|
|
128
129
|
rebaseTargetBranch = baseBranchInfo.rebaseTargetBranch;
|
|
129
130
|
originalBaseBranch = baseBranchInfo.originalBaseBranch;
|
|
131
|
+
baseBranchCompleted = baseBranchInfo.baseBranchCompleted || false;
|
|
130
132
|
if (verbose) {
|
|
131
133
|
logInfo(`š Branch chaining detected:`);
|
|
132
134
|
logInfo(` Current branch: ${currentBranch.name}`);
|
|
@@ -197,6 +199,7 @@ export const implementFeatureCode = async (options, config, checklistContext) =>
|
|
|
197
199
|
model: DEFAULT_MODEL,
|
|
198
200
|
},
|
|
199
201
|
forcePushAfterRebase: featSyncedToMain, // Trigger GitHub to recalculate PR diff
|
|
202
|
+
baseBranchCompleted, // Tell conflict resolver to use --theirs for all conflicts
|
|
200
203
|
});
|
|
201
204
|
try {
|
|
202
205
|
// Fetch all required context information via MCP endpoints
|
|
@@ -4,12 +4,14 @@
|
|
|
4
4
|
* Includes built-in verification loop that resolves PR comments and dismisses reviews
|
|
5
5
|
*/
|
|
6
6
|
import { EdsgerConfig } from '../../types/index.js';
|
|
7
|
+
import { ChecklistPhaseContext } from '../../services/checklist.js';
|
|
7
8
|
import { type CodeRefineVerificationResult } from '../code-refine-verification/index.js';
|
|
8
9
|
export declare const MAX_REFINE_ITERATIONS = 10;
|
|
9
10
|
export interface CodeRefineOptions {
|
|
10
11
|
featureId: string;
|
|
11
12
|
githubToken: string;
|
|
12
13
|
verbose?: boolean;
|
|
14
|
+
checklistContext?: ChecklistPhaseContext | null;
|
|
13
15
|
verificationFailureContext?: {
|
|
14
16
|
attempt: number;
|
|
15
17
|
suggestions: string[];
|
|
@@ -39,10 +41,19 @@ export interface CodeRefineResult {
|
|
|
39
41
|
commitsCreated?: number;
|
|
40
42
|
iterations?: number;
|
|
41
43
|
verificationResult?: CodeRefineVerificationResult;
|
|
44
|
+
data?: {
|
|
45
|
+
checklist_item_results?: Array<{
|
|
46
|
+
checklist_item_id: string;
|
|
47
|
+
is_passed: boolean;
|
|
48
|
+
value?: any;
|
|
49
|
+
notes?: string;
|
|
50
|
+
}>;
|
|
51
|
+
[key: string]: any;
|
|
52
|
+
};
|
|
42
53
|
}
|
|
43
54
|
/**
|
|
44
55
|
* Main code refine function with built-in verification loop
|
|
45
56
|
* Similar to technical-design, this includes an iterative improvement cycle:
|
|
46
57
|
* refine ā verification ā improve ā re-refine (if needed)
|
|
47
58
|
*/
|
|
48
|
-
export declare const refineCodeFromPRFeedback: (options: CodeRefineOptions, config: EdsgerConfig) => Promise<CodeRefineResult>;
|
|
59
|
+
export declare const refineCodeFromPRFeedback: (options: CodeRefineOptions, config: EdsgerConfig, checklistContext?: ChecklistPhaseContext | null) => Promise<CodeRefineResult>;
|
|
@@ -66,7 +66,7 @@ const pushChanges = (verbose) => {
|
|
|
66
66
|
* Similar to technical-design, this includes an iterative improvement cycle:
|
|
67
67
|
* refine ā verification ā improve ā re-refine (if needed)
|
|
68
68
|
*/
|
|
69
|
-
export const refineCodeFromPRFeedback = async (options, config) => {
|
|
69
|
+
export const refineCodeFromPRFeedback = async (options, config, checklistContext) => {
|
|
70
70
|
const { featureId, githubToken, verbose } = options;
|
|
71
71
|
if (verbose) {
|
|
72
72
|
logInfo(`Starting code refine for feature ID: ${featureId}`);
|
|
@@ -79,6 +79,7 @@ export const refineCodeFromPRFeedback = async (options, config) => {
|
|
|
79
79
|
let baseBranchForRebase = 'main'; // Default base branch for rebase
|
|
80
80
|
let rebaseTargetBranchForRebase; // Target for --onto rebase (e.g., main)
|
|
81
81
|
let originalBaseBranchForRebase; // For --onto when base was squash-merged
|
|
82
|
+
let baseBranchCompletedForRebase = false;
|
|
82
83
|
try {
|
|
83
84
|
// Get all branches to determine base branch info
|
|
84
85
|
allBranches = await getBranches({ featureId, verbose });
|
|
@@ -102,6 +103,7 @@ export const refineCodeFromPRFeedback = async (options, config) => {
|
|
|
102
103
|
rebaseTargetBranchForRebase = baseBranchInfo.rebaseTargetBranch;
|
|
103
104
|
// When base branch is merged (squash merge), we need originalBaseBranch for --onto
|
|
104
105
|
originalBaseBranchForRebase = baseBranchInfo.originalBaseBranch;
|
|
106
|
+
baseBranchCompletedForRebase = baseBranchInfo.baseBranchCompleted || false;
|
|
105
107
|
if (verbose) {
|
|
106
108
|
if (!currentBranch.base_branch_id) {
|
|
107
109
|
// No base branch - first branch in chain
|
|
@@ -175,6 +177,7 @@ export const refineCodeFromPRFeedback = async (options, config) => {
|
|
|
175
177
|
model: DEFAULT_MODEL,
|
|
176
178
|
},
|
|
177
179
|
forcePushAfterRebase: featSyncedToMain,
|
|
180
|
+
baseBranchCompleted: baseBranchCompletedForRebase,
|
|
178
181
|
})
|
|
179
182
|
: await preparePhaseGitEnvironmentAsync(featureId, 'main', verbose, true, {
|
|
180
183
|
model: DEFAULT_MODEL,
|
|
@@ -223,7 +226,7 @@ export const refineCodeFromPRFeedback = async (options, config) => {
|
|
|
223
226
|
}
|
|
224
227
|
}
|
|
225
228
|
// Execute refine for this iteration
|
|
226
|
-
const refineResult = await executeRefineIteration(featureId, githubToken, config, pullRequestUrl || undefined, currentBranch, verificationFailureContext, verbose);
|
|
229
|
+
const refineResult = await executeRefineIteration(featureId, githubToken, config, pullRequestUrl || undefined, currentBranch, verificationFailureContext, checklistContext, verbose);
|
|
227
230
|
if (refineResult.status === 'error') {
|
|
228
231
|
// Refine failed - return error
|
|
229
232
|
return {
|
|
@@ -299,6 +302,9 @@ Please ensure Claude Code commits all changes before completing the refine phase
|
|
|
299
302
|
commitsCreated: lastRefineResult.commits_created || 1,
|
|
300
303
|
iterations: currentIteration,
|
|
301
304
|
verificationResult,
|
|
305
|
+
data: {
|
|
306
|
+
checklist_item_results: lastRefineResult.checklist_item_results,
|
|
307
|
+
},
|
|
302
308
|
};
|
|
303
309
|
}
|
|
304
310
|
// Verification failed - prepare context for retry
|
|
@@ -361,7 +367,7 @@ Please ensure Claude Code commits all changes before completing the refine phase
|
|
|
361
367
|
/**
|
|
362
368
|
* Execute a single refine iteration
|
|
363
369
|
*/
|
|
364
|
-
async function executeRefineIteration(featureId, githubToken, config, pullRequestUrl, currentBranch, verificationFailureContext, verbose) {
|
|
370
|
+
async function executeRefineIteration(featureId, githubToken, config, pullRequestUrl, currentBranch, verificationFailureContext, checklistContext, verbose) {
|
|
365
371
|
// Fetch code refine context (PR reviews and comments)
|
|
366
372
|
const context = await fetchCodeRefineContext(featureId, githubToken, verbose, pullRequestUrl);
|
|
367
373
|
// Fetch additional feedbacks for code-refine phase
|
|
@@ -381,8 +387,8 @@ async function executeRefineIteration(featureId, githubToken, config, pullReques
|
|
|
381
387
|
}
|
|
382
388
|
}
|
|
383
389
|
// Create prompt for code refine
|
|
384
|
-
const systemPrompt = createSystemPrompt();
|
|
385
|
-
const refinePrompt = createCodeRefinePrompt(featureId, context, feedbacksInfo, verificationFailureContext);
|
|
390
|
+
const systemPrompt = createSystemPrompt(checklistContext);
|
|
391
|
+
const refinePrompt = createCodeRefinePrompt(featureId, context, feedbacksInfo, verificationFailureContext, checklistContext);
|
|
386
392
|
let lastAssistantResponse = '';
|
|
387
393
|
let structuredRefineResult = null;
|
|
388
394
|
if (verbose) {
|
|
@@ -479,6 +485,7 @@ async function executeRefineIteration(featureId, githubToken, config, pullReques
|
|
|
479
485
|
summary: structuredRefineResult.summary,
|
|
480
486
|
files_modified: structuredRefineResult.files_modified,
|
|
481
487
|
commits_created: structuredRefineResult.commits_created,
|
|
488
|
+
checklist_item_results: structuredRefineResult.checklist_item_results,
|
|
482
489
|
};
|
|
483
490
|
}
|
|
484
491
|
else {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type CodeRefineContext } from './context.js';
|
|
2
2
|
import { CodeRefineOptions } from './index.js';
|
|
3
|
-
|
|
4
|
-
export declare function
|
|
3
|
+
import { ChecklistPhaseContext } from '../../services/checklist.js';
|
|
4
|
+
export declare function createSystemPrompt(checklistContext?: ChecklistPhaseContext | null): string;
|
|
5
|
+
export declare function createCodeRefinePrompt(featureId: string, context: CodeRefineContext, feedbacksInfo?: string, verificationFailureContext?: CodeRefineOptions['verificationFailureContext'], checklistContext?: ChecklistPhaseContext | null): string;
|
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
import { formatContextForPrompt } from './context.js';
|
|
2
|
-
|
|
2
|
+
import { formatChecklistsForContext, } from '../../services/checklist.js';
|
|
3
|
+
export function createSystemPrompt(checklistContext) {
|
|
4
|
+
const checklistInstructions = checklistContext &&
|
|
5
|
+
checklistContext.checklists &&
|
|
6
|
+
checklistContext.checklists.length > 0
|
|
7
|
+
? `
|
|
8
|
+
|
|
9
|
+
**MANDATORY Checklist Compliance**:
|
|
10
|
+
If you are provided with checklists in the context, you MUST satisfy ALL of them during your code refinement work. Checklists are mandatory requirements that define quality standards and cannot be ignored or skipped.
|
|
11
|
+
|
|
12
|
+
- Review each checklist carefully and ensure your refinements address every requirement
|
|
13
|
+
- You MUST include ALL provided checklist item IDs in the "checklist_item_results" field of your JSON response
|
|
14
|
+
- CRITICAL: Use the exact UUID from the "ID:" field in each checklist ITEM, NOT the item title or description
|
|
15
|
+
- Set "is_passed": true for each checklist item you have completed successfully
|
|
16
|
+
- Provide appropriate "value" based on the item type (boolean: true/false, text: descriptive text, number: numeric value)
|
|
17
|
+
- If you cannot satisfy a checklist item requirement, set "is_passed": false and explain why in the "notes" field
|
|
18
|
+
- The system will validate that all checklists have been addressed - missing checklists will cause the pipeline to fail`
|
|
19
|
+
: '';
|
|
20
|
+
const checklistJsonField = checklistContext &&
|
|
21
|
+
checklistContext.checklists &&
|
|
22
|
+
checklistContext.checklists.length > 0
|
|
23
|
+
? `,
|
|
24
|
+
"checklist_item_results": [
|
|
25
|
+
{
|
|
26
|
+
"checklist_item_id": "EXACT_CHECKLIST_ITEM_UUID_FROM_ID_FIELD",
|
|
27
|
+
"is_passed": true,
|
|
28
|
+
"value": "Result value based on item type (boolean, text, number, etc.)",
|
|
29
|
+
"notes": "Optional notes about this specific checklist item"
|
|
30
|
+
}
|
|
31
|
+
]`
|
|
32
|
+
: '';
|
|
3
33
|
return `You are an expert software engineer specializing in addressing code review feedback. Your goal is to carefully analyze review comments and refine the code to address all concerns raised by human reviewers.
|
|
4
34
|
|
|
5
35
|
**Your Role**: Refine code based on PR review feedback while maintaining code quality and functionality.
|
|
@@ -35,6 +65,7 @@ export function createSystemPrompt() {
|
|
|
35
65
|
- DO NOT leave any uncommitted changes in the working directory
|
|
36
66
|
- Verification will fail if there are uncommitted changes
|
|
37
67
|
- Use clear, descriptive commit messages that explain what feedback was addressed
|
|
68
|
+
${checklistInstructions}
|
|
38
69
|
|
|
39
70
|
**CRITICAL - Result Format**:
|
|
40
71
|
You MUST end your response with a JSON object containing the code refine results in this EXACT format:
|
|
@@ -53,18 +84,22 @@ You MUST end your response with a JSON object containing the code refine results
|
|
|
53
84
|
"resolution": "Description of how it was addressed",
|
|
54
85
|
"files": ["path/to/file.ts"]
|
|
55
86
|
}
|
|
56
|
-
]
|
|
87
|
+
]${checklistJsonField}
|
|
57
88
|
}
|
|
58
89
|
}
|
|
59
90
|
\`\`\`
|
|
60
91
|
|
|
61
92
|
Focus on systematic code refinement based on the provided PR review feedback.`;
|
|
62
93
|
}
|
|
63
|
-
export function createCodeRefinePrompt(featureId, context, feedbacksInfo, verificationFailureContext) {
|
|
94
|
+
export function createCodeRefinePrompt(featureId, context, feedbacksInfo, verificationFailureContext, checklistContext) {
|
|
64
95
|
let contextInfo = formatContextForPrompt(context);
|
|
65
96
|
if (feedbacksInfo) {
|
|
66
97
|
contextInfo = contextInfo + '\n\n' + feedbacksInfo;
|
|
67
98
|
}
|
|
99
|
+
if (checklistContext && checklistContext.checklists.length > 0) {
|
|
100
|
+
const checklistInfo = formatChecklistsForContext(checklistContext);
|
|
101
|
+
contextInfo = contextInfo + '\n\n' + checklistInfo;
|
|
102
|
+
}
|
|
68
103
|
// Add verification failure context if this is a retry
|
|
69
104
|
let verificationSection = '';
|
|
70
105
|
if (verificationFailureContext) {
|
|
@@ -183,7 +218,17 @@ BEFORE you generate the final JSON result, you MUST complete this checklist:
|
|
|
183
218
|
- All review feedback has been addressed
|
|
184
219
|
|
|
185
220
|
**FAILURE TO COMMIT ALL CHANGES WILL CAUSE THE PIPELINE TO FAIL**
|
|
186
|
-
|
|
221
|
+
${checklistContext && checklistContext.checklists.length > 0
|
|
222
|
+
? `
|
|
223
|
+
## MANDATORY - Checklist Compliance
|
|
224
|
+
You MUST satisfy ALL checklist items provided in the context and include results in your JSON response.
|
|
225
|
+
- Use the exact UUID from each checklist item's "ID:" field as the "checklist_item_id"
|
|
226
|
+
- Ensure your code refinements satisfy each checklist requirement
|
|
227
|
+
- Set "is_passed" to true if the requirement is met, false otherwise
|
|
228
|
+
- Provide detailed "notes" for any items that are not passed
|
|
229
|
+
- ALL checklist items must be included - missing items will cause pipeline failure
|
|
230
|
+
`
|
|
231
|
+
: ''}
|
|
187
232
|
You are currently on the \`dev/${featureId}\` branch. Make your changes, commit them, and they will be pushed to the remote repository.
|
|
188
233
|
|
|
189
234
|
Begin by analyzing all the review feedback and creating a plan to address each comment.`;
|
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
* Reviews GitHub PR code and creates review comments with REQUEST_CHANGES
|
|
4
4
|
*/
|
|
5
5
|
import { EdsgerConfig } from '../../types/index.js';
|
|
6
|
+
import { ChecklistPhaseContext } from '../../services/checklist.js';
|
|
6
7
|
export interface CodeReviewOptions {
|
|
7
8
|
featureId: string;
|
|
8
9
|
githubToken: string;
|
|
9
10
|
verbose?: boolean;
|
|
11
|
+
checklistContext?: ChecklistPhaseContext | null;
|
|
10
12
|
}
|
|
11
13
|
export interface ReviewComment {
|
|
12
14
|
path: string;
|
|
@@ -22,8 +24,17 @@ export interface CodeReviewResult {
|
|
|
22
24
|
reviewUrl?: string;
|
|
23
25
|
commentsCount?: number;
|
|
24
26
|
summary?: string;
|
|
27
|
+
data?: {
|
|
28
|
+
checklist_item_results?: Array<{
|
|
29
|
+
checklist_item_id: string;
|
|
30
|
+
is_passed: boolean;
|
|
31
|
+
value?: any;
|
|
32
|
+
notes?: string;
|
|
33
|
+
}>;
|
|
34
|
+
[key: string]: any;
|
|
35
|
+
};
|
|
25
36
|
}
|
|
26
37
|
/**
|
|
27
38
|
* Main code review function
|
|
28
39
|
*/
|
|
29
|
-
export declare const reviewPullRequest: (options: CodeReviewOptions, config: EdsgerConfig) => Promise<CodeReviewResult>;
|
|
40
|
+
export declare const reviewPullRequest: (options: CodeReviewOptions, config: EdsgerConfig, checklistContext?: ChecklistPhaseContext | null) => Promise<CodeReviewResult>;
|
|
@@ -8,6 +8,7 @@ import { logInfo, logError } from '../../utils/logger.js';
|
|
|
8
8
|
import { Octokit } from '@octokit/rest';
|
|
9
9
|
import { fetchCodeReviewContext, formatContextForPrompt, } from './context.js';
|
|
10
10
|
import { getFeedbacksForPhase, formatFeedbacksForContext, } from '../../services/feedbacks.js';
|
|
11
|
+
import { formatChecklistsForContext, } from '../../services/checklist.js';
|
|
11
12
|
import { preparePhaseGitEnvironmentAsync, prepareCustomBranchGitEnvironmentAsync, syncFeatBranchWithMain, } from '../../utils/git-branch-manager.js';
|
|
12
13
|
import { getBranches, getBaseBranchInfo, updateBranch, } from '../../services/branches.js';
|
|
13
14
|
import { getGitHubConfig } from '../../api/github.js';
|
|
@@ -118,7 +119,7 @@ function findClosestPosition(targetLine, lineToPosition) {
|
|
|
118
119
|
/**
|
|
119
120
|
* Main code review function
|
|
120
121
|
*/
|
|
121
|
-
export const reviewPullRequest = async (options, config) => {
|
|
122
|
+
export const reviewPullRequest = async (options, config, checklistContext) => {
|
|
122
123
|
const { featureId, githubToken, verbose } = options;
|
|
123
124
|
if (verbose) {
|
|
124
125
|
logInfo(`Starting code review for feature ID: ${featureId}`);
|
|
@@ -131,6 +132,7 @@ export const reviewPullRequest = async (options, config) => {
|
|
|
131
132
|
let baseBranchForRebase = 'main'; // Default base branch for rebase
|
|
132
133
|
let rebaseTargetBranchForRebase; // Target for --onto rebase (e.g., main)
|
|
133
134
|
let originalBaseBranchForRebase; // For --onto when base was squash-merged
|
|
135
|
+
let baseBranchCompletedForRebase = false;
|
|
134
136
|
try {
|
|
135
137
|
// Get all branches to determine base branch info
|
|
136
138
|
allBranches = await getBranches({ featureId, verbose });
|
|
@@ -155,6 +157,7 @@ export const reviewPullRequest = async (options, config) => {
|
|
|
155
157
|
rebaseTargetBranchForRebase = baseBranchInfo.rebaseTargetBranch;
|
|
156
158
|
// When base branch is merged (squash merge), we need originalBaseBranch for --onto
|
|
157
159
|
originalBaseBranchForRebase = baseBranchInfo.originalBaseBranch;
|
|
160
|
+
baseBranchCompletedForRebase = baseBranchInfo.baseBranchCompleted || false;
|
|
158
161
|
if (verbose) {
|
|
159
162
|
if (!currentBranch.base_branch_id) {
|
|
160
163
|
// No base branch - first branch in chain
|
|
@@ -228,6 +231,7 @@ export const reviewPullRequest = async (options, config) => {
|
|
|
228
231
|
model: DEFAULT_MODEL,
|
|
229
232
|
},
|
|
230
233
|
forcePushAfterRebase: featSyncedToMain,
|
|
234
|
+
baseBranchCompleted: baseBranchCompletedForRebase,
|
|
231
235
|
})
|
|
232
236
|
: await preparePhaseGitEnvironmentAsync(featureId, 'main', verbose, true, {
|
|
233
237
|
model: DEFAULT_MODEL,
|
|
@@ -277,8 +281,8 @@ export const reviewPullRequest = async (options, config) => {
|
|
|
277
281
|
}
|
|
278
282
|
}
|
|
279
283
|
// Create prompt for code review
|
|
280
|
-
const systemPrompt = createSystemPrompt(config);
|
|
281
|
-
const reviewPrompt = createCodeReviewPrompt(featureId, context, feedbacksInfo);
|
|
284
|
+
const systemPrompt = createSystemPrompt(config, checklistContext);
|
|
285
|
+
const reviewPrompt = createCodeReviewPrompt(featureId, context, feedbacksInfo, checklistContext);
|
|
282
286
|
let lastAssistantResponse = '';
|
|
283
287
|
let structuredReviewResult = null;
|
|
284
288
|
if (verbose) {
|
|
@@ -367,7 +371,7 @@ export const reviewPullRequest = async (options, config) => {
|
|
|
367
371
|
}
|
|
368
372
|
// Create GitHub review with comments
|
|
369
373
|
if (structuredReviewResult) {
|
|
370
|
-
const { summary, comments, overall_assessment } = structuredReviewResult;
|
|
374
|
+
const { summary, comments, overall_assessment, checklist_item_results } = structuredReviewResult;
|
|
371
375
|
// Initialize Octokit
|
|
372
376
|
const octokit = new Octokit({ auth: githubToken });
|
|
373
377
|
if (!comments || comments.length === 0) {
|
|
@@ -402,6 +406,7 @@ export const reviewPullRequest = async (options, config) => {
|
|
|
402
406
|
reviewUrl: review.data.html_url,
|
|
403
407
|
commentsCount: 0,
|
|
404
408
|
summary: summary || 'No issues found',
|
|
409
|
+
data: { checklist_item_results },
|
|
405
410
|
};
|
|
406
411
|
}
|
|
407
412
|
if (verbose) {
|
|
@@ -479,6 +484,7 @@ export const reviewPullRequest = async (options, config) => {
|
|
|
479
484
|
reviewUrl: review.data.html_url,
|
|
480
485
|
commentsCount: 0,
|
|
481
486
|
summary: summary || 'Code review completed (comments filtered)',
|
|
487
|
+
data: { checklist_item_results },
|
|
482
488
|
};
|
|
483
489
|
}
|
|
484
490
|
if (verbose) {
|
|
@@ -515,6 +521,7 @@ export const reviewPullRequest = async (options, config) => {
|
|
|
515
521
|
reviewUrl: review.data.html_url,
|
|
516
522
|
commentsCount: comments.length,
|
|
517
523
|
summary: summary || 'Code review completed',
|
|
524
|
+
data: { checklist_item_results },
|
|
518
525
|
};
|
|
519
526
|
}
|
|
520
527
|
else {
|
|
@@ -539,7 +546,36 @@ export const reviewPullRequest = async (options, config) => {
|
|
|
539
546
|
cleanupGit();
|
|
540
547
|
}
|
|
541
548
|
};
|
|
542
|
-
function createSystemPrompt(_config) {
|
|
549
|
+
function createSystemPrompt(_config, checklistContext) {
|
|
550
|
+
const checklistInstructions = checklistContext &&
|
|
551
|
+
checklistContext.checklists &&
|
|
552
|
+
checklistContext.checklists.length > 0
|
|
553
|
+
? `
|
|
554
|
+
|
|
555
|
+
**MANDATORY Checklist Compliance**:
|
|
556
|
+
If you are provided with checklists in the context, you MUST evaluate ALL of them during your code review. Checklists are mandatory requirements that define quality standards and cannot be ignored or skipped.
|
|
557
|
+
|
|
558
|
+
- Review each checklist carefully and evaluate whether the code satisfies every requirement
|
|
559
|
+
- You MUST include ALL provided checklist item IDs in the "checklist_item_results" field of your JSON response
|
|
560
|
+
- CRITICAL: Use the exact UUID from the "ID:" field in each checklist ITEM, NOT the item title or description
|
|
561
|
+
- Set "is_passed": true for each checklist item the code satisfies
|
|
562
|
+
- Provide appropriate "value" based on the item type (boolean: true/false, text: descriptive text, number: numeric value)
|
|
563
|
+
- If the code does not satisfy a checklist item requirement, set "is_passed": false and explain why in the "notes" field
|
|
564
|
+
- The system will validate that all checklists have been addressed - missing checklists will cause the pipeline to fail`
|
|
565
|
+
: '';
|
|
566
|
+
const checklistJsonField = checklistContext &&
|
|
567
|
+
checklistContext.checklists &&
|
|
568
|
+
checklistContext.checklists.length > 0
|
|
569
|
+
? `,
|
|
570
|
+
"checklist_item_results": [
|
|
571
|
+
{
|
|
572
|
+
"checklist_item_id": "EXACT_CHECKLIST_ITEM_UUID_FROM_ID_FIELD",
|
|
573
|
+
"is_passed": true,
|
|
574
|
+
"value": "Result value based on item type (boolean, text, number, etc.)",
|
|
575
|
+
"notes": "Optional notes about this specific checklist item"
|
|
576
|
+
}
|
|
577
|
+
]`
|
|
578
|
+
: '';
|
|
543
579
|
return `You are an expert code reviewer specializing in thorough, constructive code reviews. Your goal is to analyze pull request code and identify issues, potential bugs, code quality concerns, and areas for improvement.
|
|
544
580
|
|
|
545
581
|
**Your Role**: Review pull request code and provide detailed, actionable feedback.
|
|
@@ -562,6 +598,7 @@ function createSystemPrompt(_config) {
|
|
|
562
598
|
- Be respectful and professional
|
|
563
599
|
- Focus on issues that truly matter, not nitpicks
|
|
564
600
|
- If code looks good overall, provide positive feedback
|
|
601
|
+
${checklistInstructions}
|
|
565
602
|
|
|
566
603
|
**CRITICAL - Result Format**:
|
|
567
604
|
You MUST end your response with a JSON object containing the review results in this EXACT format:
|
|
@@ -583,7 +620,7 @@ You MUST end your response with a JSON object containing the review results in t
|
|
|
583
620
|
"critical": 0,
|
|
584
621
|
"major": 0,
|
|
585
622
|
"minor": 0
|
|
586
|
-
}
|
|
623
|
+
}${checklistJsonField}
|
|
587
624
|
}
|
|
588
625
|
}
|
|
589
626
|
\`\`\`
|
|
@@ -601,11 +638,15 @@ You MUST end your response with a JSON object containing the review results in t
|
|
|
601
638
|
|
|
602
639
|
Focus on providing valuable, actionable code review feedback.`;
|
|
603
640
|
}
|
|
604
|
-
function createCodeReviewPrompt(featureId, context, feedbacksInfo) {
|
|
641
|
+
function createCodeReviewPrompt(featureId, context, feedbacksInfo, checklistContext) {
|
|
605
642
|
let contextInfo = formatContextForPrompt(context);
|
|
606
643
|
if (feedbacksInfo) {
|
|
607
644
|
contextInfo = contextInfo + '\n\n' + feedbacksInfo;
|
|
608
645
|
}
|
|
646
|
+
if (checklistContext && checklistContext.checklists.length > 0) {
|
|
647
|
+
const checklistInfo = formatChecklistsForContext(checklistContext);
|
|
648
|
+
contextInfo = contextInfo + '\n\n' + checklistInfo;
|
|
649
|
+
}
|
|
609
650
|
return `Review the pull request code for feature: ${featureId}
|
|
610
651
|
|
|
611
652
|
${contextInfo}
|
|
@@ -653,7 +694,17 @@ Follow this systematic approach:
|
|
|
653
694
|
- Consider the context and requirements
|
|
654
695
|
- Be professional and constructive
|
|
655
696
|
- If code is good, say so!
|
|
656
|
-
|
|
697
|
+
${checklistContext && checklistContext.checklists.length > 0
|
|
698
|
+
? `
|
|
699
|
+
## MANDATORY - Checklist Compliance
|
|
700
|
+
You MUST evaluate ALL checklist items provided in the context and include results in your JSON response.
|
|
701
|
+
- Use the exact UUID from each checklist item's "ID:" field as the "checklist_item_id"
|
|
702
|
+
- Evaluate whether the code satisfies each checklist requirement
|
|
703
|
+
- Set "is_passed" to true if the requirement is met, false otherwise
|
|
704
|
+
- Provide detailed "notes" for any items that are not passed
|
|
705
|
+
- ALL checklist items must be included - missing items will cause pipeline failure
|
|
706
|
+
`
|
|
707
|
+
: ''}
|
|
657
708
|
Begin by analyzing the changed files and identifying any issues or improvements.`;
|
|
658
709
|
}
|
|
659
710
|
function parseCodeReviewResponse(response) {
|
|
@@ -192,14 +192,21 @@ export async function getBaseBranchInfo(branch, allBranches, mainBranch = 'main'
|
|
|
192
192
|
}
|
|
193
193
|
// Check if base branch is completed (feat merged to main)
|
|
194
194
|
if (baseBranch.status === 'completed') {
|
|
195
|
-
// Base branch's feat
|
|
196
|
-
//
|
|
197
|
-
//
|
|
195
|
+
// Base branch's feat was squash-merged to main, and feat was likely synced
|
|
196
|
+
// to main during the base branch's code-implementation phase.
|
|
197
|
+
// This means origin/feat/base = origin/main, so --onto would not help
|
|
198
|
+
// (it would replay all commits including the old squash commit).
|
|
199
|
+
//
|
|
200
|
+
// Use a normal rebase to main instead. The dev branch may carry an old
|
|
201
|
+
// squash commit (S1) from feat that conflicts with main's squash (S2).
|
|
202
|
+
// This is expected and handled by the conflict resolver (resolveConflicts: true).
|
|
203
|
+
// baseBranchCompleted tells the resolver to use "theirs" (main) for all conflicts,
|
|
204
|
+
// so the old commit becomes empty and gets dropped by git rebase.
|
|
198
205
|
return {
|
|
199
206
|
baseBranch: mainBranch,
|
|
200
|
-
// Don't set originalBaseBranch - we want a normal rebase, not --onto
|
|
201
207
|
needsRebase: false,
|
|
202
208
|
baseBranchMerged: true,
|
|
209
|
+
baseBranchCompleted: true,
|
|
203
210
|
};
|
|
204
211
|
}
|
|
205
212
|
// Check if base branch is merged (dev merged to feat, but feat not yet merged to main)
|
|
@@ -17,6 +17,12 @@ export interface ConflictResolverConfig {
|
|
|
17
17
|
* to only replay commits after this branch.
|
|
18
18
|
*/
|
|
19
19
|
originalBaseBranch?: string;
|
|
20
|
+
/**
|
|
21
|
+
* When true, the base branch's feat was already squash-merged to main.
|
|
22
|
+
* All conflicts should be resolved by accepting "theirs" (main's version)
|
|
23
|
+
* so duplicate commits from the base branch become empty and get dropped.
|
|
24
|
+
*/
|
|
25
|
+
baseBranchCompleted?: boolean;
|
|
20
26
|
}
|
|
21
27
|
/**
|
|
22
28
|
* Result of conflict resolution
|
|
@@ -17,8 +17,8 @@ function* createPromptGenerator(prompt) {
|
|
|
17
17
|
/**
|
|
18
18
|
* Create a system prompt for conflict resolution
|
|
19
19
|
*/
|
|
20
|
-
function createConflictResolutionSystemPrompt() {
|
|
21
|
-
|
|
20
|
+
function createConflictResolutionSystemPrompt(baseBranchCompleted) {
|
|
21
|
+
const basePrompt = `You are a skilled software engineer completing a git rebase operation that has conflicts.
|
|
22
22
|
|
|
23
23
|
Your task is to resolve ALL merge conflicts and complete the rebase successfully.
|
|
24
24
|
|
|
@@ -31,15 +31,28 @@ WORKFLOW:
|
|
|
31
31
|
d. Run "git add <file>" to stage the resolved file
|
|
32
32
|
3. After ALL conflicts are resolved, run "git rebase --continue"
|
|
33
33
|
4. If more conflicts appear from subsequent commits, repeat steps 1-3
|
|
34
|
-
5. Continue until the rebase is complete (no more rebase in progress)
|
|
35
|
-
|
|
34
|
+
5. Continue until the rebase is complete (no more rebase in progress)`;
|
|
35
|
+
const resolutionRules = baseBranchCompleted
|
|
36
|
+
? `
|
|
37
|
+
CONFLICT RESOLUTION RULES:
|
|
38
|
+
- The base branch has already been squash-merged to main. The conflicts are caused by
|
|
39
|
+
old commits from the base branch being replayed on top of main which already has those changes.
|
|
40
|
+
- For EVERY conflict, accept "theirs" (main's version) by running: git checkout --theirs <file> && git add <file>
|
|
41
|
+
- This will make the duplicate commits empty, and git rebase will automatically drop them.
|
|
42
|
+
- Do NOT try to merge changes intelligently - main already has the final correct version.
|
|
43
|
+
- After resolving all conflicts with --theirs, run "git rebase --continue"
|
|
44
|
+
- If git says "No changes - did you forget to use 'git add'?", run "git rebase --skip" to skip the empty commit`
|
|
45
|
+
: `
|
|
36
46
|
CONFLICT RESOLUTION RULES:
|
|
37
47
|
- Remove ALL conflict markers (<<<<<<, =======, >>>>>>>)
|
|
38
48
|
- Merge changes intelligently - don't just pick one side
|
|
39
49
|
- Preserve functionality from both sides when possible
|
|
40
50
|
- If changes are truly incompatible, prefer theirs (incoming) as it's usually newer
|
|
41
51
|
- Ensure the resulting code is syntactically correct
|
|
42
|
-
- DO NOT add comments explaining the merge
|
|
52
|
+
- DO NOT add comments explaining the merge`;
|
|
53
|
+
return (basePrompt +
|
|
54
|
+
resolutionRules +
|
|
55
|
+
`
|
|
43
56
|
|
|
44
57
|
IMPORTANT:
|
|
45
58
|
- Use "git status" to check progress
|
|
@@ -47,7 +60,7 @@ IMPORTANT:
|
|
|
47
60
|
- Use the Edit tool to fix conflicts (remove markers, merge content)
|
|
48
61
|
- Use Bash for git commands (git add, git rebase --continue)
|
|
49
62
|
- Keep working until "git status" shows no rebase in progress
|
|
50
|
-
- If you encounter an error you cannot resolve, explain what went wrong
|
|
63
|
+
- If you encounter an error you cannot resolve, explain what went wrong`);
|
|
51
64
|
}
|
|
52
65
|
/**
|
|
53
66
|
* Create the initial prompt describing the rebase task
|
|
@@ -88,7 +101,7 @@ Start by running "git status" to see the current state.`;
|
|
|
88
101
|
* @returns Result of the conflict resolution
|
|
89
102
|
*/
|
|
90
103
|
export async function resolveConflictsWithAgent(config = {}) {
|
|
91
|
-
const { model = 'sonnet', verbose, maxTurns = 100, featureBranch, baseBranch, originalBaseBranch, } = config;
|
|
104
|
+
const { model = 'sonnet', verbose, maxTurns = 100, featureBranch, baseBranch, originalBaseBranch, baseBranchCompleted, } = config;
|
|
92
105
|
const resolvedFiles = [];
|
|
93
106
|
if (verbose) {
|
|
94
107
|
logInfo(`\nš§ Starting automatic conflict resolution with Claude agent...`);
|
|
@@ -105,7 +118,7 @@ export async function resolveConflictsWithAgent(config = {}) {
|
|
|
105
118
|
}
|
|
106
119
|
logInfo(` Initial conflicts: ${initialConflictFiles.length} file(s)`);
|
|
107
120
|
}
|
|
108
|
-
const systemPrompt = createConflictResolutionSystemPrompt();
|
|
121
|
+
const systemPrompt = createConflictResolutionSystemPrompt(baseBranchCompleted);
|
|
109
122
|
const userPrompt = createRebaseTaskPrompt(currentBranch, targetBranch, initialConflictFiles, originalBaseBranch);
|
|
110
123
|
try {
|
|
111
124
|
let lastMessage = '';
|
|
@@ -200,6 +200,12 @@ export interface RebaseWithConflictResolutionOptions {
|
|
|
200
200
|
model?: string;
|
|
201
201
|
maxTurns?: number;
|
|
202
202
|
};
|
|
203
|
+
/**
|
|
204
|
+
* When true, indicates the base branch's feat was already squash-merged to main.
|
|
205
|
+
* The conflict resolver should use "theirs" (main) for all conflicts to drop
|
|
206
|
+
* duplicate commits from the base branch.
|
|
207
|
+
*/
|
|
208
|
+
baseBranchCompleted?: boolean;
|
|
203
209
|
/**
|
|
204
210
|
* Force push the branch after rebase.
|
|
205
211
|
* This is needed when feat branch was synced to main, to trigger
|
|
@@ -689,7 +689,7 @@ export async function syncFeatBranchWithMain(featBranch, githubToken, owner, rep
|
|
|
689
689
|
* @returns Object containing the previous branch name and conflict resolution result
|
|
690
690
|
*/
|
|
691
691
|
export async function switchToFeatureBranchAndRebaseAsync(options) {
|
|
692
|
-
const { featureBranch, baseBranch = 'main', rebaseTargetBranch, originalBaseBranch, verbose, resolveConflicts = false, conflictResolverConfig, forcePushAfterRebase = false, } = options;
|
|
692
|
+
const { featureBranch, baseBranch = 'main', rebaseTargetBranch, originalBaseBranch, verbose, resolveConflicts = false, conflictResolverConfig, forcePushAfterRebase = false, baseBranchCompleted = false, } = options;
|
|
693
693
|
// Determine the actual target branch for rebase
|
|
694
694
|
// If rebaseTargetBranch is set (e.g., main), use it; otherwise use baseBranch
|
|
695
695
|
const actualRebaseTarget = rebaseTargetBranch || baseBranch;
|
|
@@ -871,6 +871,7 @@ export async function switchToFeatureBranchAndRebaseAsync(options) {
|
|
|
871
871
|
featureBranch,
|
|
872
872
|
baseBranch,
|
|
873
873
|
originalBaseBranch,
|
|
874
|
+
baseBranchCompleted,
|
|
874
875
|
});
|
|
875
876
|
if (result.success) {
|
|
876
877
|
if (verbose) {
|