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.
@@ -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
- export declare function createSystemPrompt(): string;
4
- export declare function createCodeRefinePrompt(featureId: string, context: CodeRefineContext, feedbacksInfo?: string, verificationFailureContext?: CodeRefineOptions['verificationFailureContext']): string;
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
- export function createSystemPrompt() {
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) {
@@ -78,6 +78,7 @@ export declare function getBaseBranchInfo(branch: Branch, allBranches: Branch[],
78
78
  originalBaseBranch?: string;
79
79
  needsRebase: boolean;
80
80
  baseBranchMerged: boolean;
81
+ baseBranchCompleted?: boolean;
81
82
  }>;
82
83
  /**
83
84
  * Get branch by ID
@@ -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 is already squash-merged to main
196
- // Just rebase directly to main - no need for --onto
197
- // Main already contains all the changes, so a normal rebase will work correctly
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
- return `You are a skilled software engineer completing a git rebase operation that has conflicts.
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "edsger",
3
- "version": "0.21.10",
3
+ "version": "0.22.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "edsger": "dist/index.js"