edsger 0.19.9 → 0.19.11
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/feature-coordinator.js +1 -0
- package/dist/phases/code-refine/index.d.ts +8 -1
- package/dist/phases/code-refine/index.js +250 -146
- package/dist/phases/code-review/index.js +22 -1
- package/dist/services/branches.d.ts +6 -1
- package/dist/services/branches.js +28 -9
- package/package.json +1 -1
- package/.claude/settings.local.json +0 -28
- package/dist/api/features/__tests__/regression-prevention.test.d.ts +0 -5
- package/dist/api/features/__tests__/regression-prevention.test.js +0 -338
- package/dist/api/features/__tests__/status-updater.integration.test.d.ts +0 -5
- package/dist/api/features/__tests__/status-updater.integration.test.js +0 -497
- package/dist/commands/workflow/pipeline-runner.d.ts +0 -17
- package/dist/commands/workflow/pipeline-runner.js +0 -393
- package/dist/commands/workflow/runner.d.ts +0 -26
- package/dist/commands/workflow/runner.js +0 -119
- package/dist/commands/workflow/workflow-runner.d.ts +0 -26
- package/dist/commands/workflow/workflow-runner.js +0 -119
- package/dist/phases/code-implementation/analyzer-helpers.d.ts +0 -28
- package/dist/phases/code-implementation/analyzer-helpers.js +0 -177
- package/dist/phases/code-implementation/analyzer.d.ts +0 -32
- package/dist/phases/code-implementation/analyzer.js +0 -629
- package/dist/phases/code-implementation/context-fetcher.d.ts +0 -17
- package/dist/phases/code-implementation/context-fetcher.js +0 -86
- package/dist/phases/code-implementation/mcp-server.d.ts +0 -1
- package/dist/phases/code-implementation/mcp-server.js +0 -93
- package/dist/phases/code-implementation/prompts-improvement.d.ts +0 -5
- package/dist/phases/code-implementation/prompts-improvement.js +0 -108
- package/dist/phases/code-implementation-verification/verifier.d.ts +0 -31
- package/dist/phases/code-implementation-verification/verifier.js +0 -196
- package/dist/phases/code-refine/analyzer.d.ts +0 -41
- package/dist/phases/code-refine/analyzer.js +0 -561
- package/dist/phases/code-refine/context-fetcher.d.ts +0 -94
- package/dist/phases/code-refine/context-fetcher.js +0 -423
- package/dist/phases/code-refine-verification/analysis/llm-analyzer.d.ts +0 -22
- package/dist/phases/code-refine-verification/analysis/llm-analyzer.js +0 -134
- package/dist/phases/code-refine-verification/verifier.d.ts +0 -47
- package/dist/phases/code-refine-verification/verifier.js +0 -597
- package/dist/phases/code-review/analyzer.d.ts +0 -29
- package/dist/phases/code-review/analyzer.js +0 -363
- package/dist/phases/code-review/context-fetcher.d.ts +0 -92
- package/dist/phases/code-review/context-fetcher.js +0 -296
- package/dist/phases/feature-analysis/analyzer-helpers.d.ts +0 -10
- package/dist/phases/feature-analysis/analyzer-helpers.js +0 -47
- package/dist/phases/feature-analysis/analyzer.d.ts +0 -11
- package/dist/phases/feature-analysis/analyzer.js +0 -208
- package/dist/phases/feature-analysis/context-fetcher.d.ts +0 -26
- package/dist/phases/feature-analysis/context-fetcher.js +0 -134
- package/dist/phases/feature-analysis/http-fallback.d.ts +0 -20
- package/dist/phases/feature-analysis/http-fallback.js +0 -95
- package/dist/phases/feature-analysis/mcp-server.d.ts +0 -1
- package/dist/phases/feature-analysis/mcp-server.js +0 -144
- package/dist/phases/feature-analysis/prompts-improvement.d.ts +0 -8
- package/dist/phases/feature-analysis/prompts-improvement.js +0 -109
- package/dist/phases/feature-analysis-verification/verifier.d.ts +0 -37
- package/dist/phases/feature-analysis-verification/verifier.js +0 -147
- package/dist/phases/technical-design/analyzer-helpers.d.ts +0 -25
- package/dist/phases/technical-design/analyzer-helpers.js +0 -39
- package/dist/phases/technical-design/analyzer.d.ts +0 -21
- package/dist/phases/technical-design/analyzer.js +0 -461
- package/dist/phases/technical-design/context-fetcher.d.ts +0 -12
- package/dist/phases/technical-design/context-fetcher.js +0 -39
- package/dist/phases/technical-design/http-fallback.d.ts +0 -17
- package/dist/phases/technical-design/http-fallback.js +0 -151
- package/dist/phases/technical-design/mcp-server.d.ts +0 -1
- package/dist/phases/technical-design/mcp-server.js +0 -157
- package/dist/phases/technical-design/prompts-improvement.d.ts +0 -5
- package/dist/phases/technical-design/prompts-improvement.js +0 -93
- package/dist/phases/technical-design-verification/verifier.d.ts +0 -53
- package/dist/phases/technical-design-verification/verifier.js +0 -170
- package/dist/services/feature-branches.d.ts +0 -77
- package/dist/services/feature-branches.js +0 -205
- package/dist/workflow-runner/config/phase-configs.d.ts +0 -5
- package/dist/workflow-runner/config/phase-configs.js +0 -120
- package/dist/workflow-runner/core/feature-filter.d.ts +0 -16
- package/dist/workflow-runner/core/feature-filter.js +0 -46
- package/dist/workflow-runner/core/index.d.ts +0 -8
- package/dist/workflow-runner/core/index.js +0 -12
- package/dist/workflow-runner/core/pipeline-evaluator.d.ts +0 -24
- package/dist/workflow-runner/core/pipeline-evaluator.js +0 -32
- package/dist/workflow-runner/core/state-manager.d.ts +0 -24
- package/dist/workflow-runner/core/state-manager.js +0 -42
- package/dist/workflow-runner/core/workflow-logger.d.ts +0 -20
- package/dist/workflow-runner/core/workflow-logger.js +0 -65
- package/dist/workflow-runner/executors/phase-executor.d.ts +0 -8
- package/dist/workflow-runner/executors/phase-executor.js +0 -248
- package/dist/workflow-runner/feature-workflow-runner.d.ts +0 -26
- package/dist/workflow-runner/feature-workflow-runner.js +0 -119
- package/dist/workflow-runner/index.d.ts +0 -2
- package/dist/workflow-runner/index.js +0 -2
- package/dist/workflow-runner/pipeline-runner.d.ts +0 -17
- package/dist/workflow-runner/pipeline-runner.js +0 -393
- package/dist/workflow-runner/workflow-processor.d.ts +0 -54
- package/dist/workflow-runner/workflow-processor.js +0 -170
|
@@ -16,6 +16,7 @@ import { logPhaseResult } from '../../utils/pipeline-logger.js';
|
|
|
16
16
|
import { runFeatureAnalysisPhase, runTechnicalDesignPhase, runBranchPlanningPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeRefinePhase, runCodeReviewPhase, } from './executors/phase-executor.js';
|
|
17
17
|
/**
|
|
18
18
|
* Map workflow phase names (underscore format) to phase runner functions
|
|
19
|
+
* Note: code_refine includes built-in verification loop (similar to technical_design)
|
|
19
20
|
*/
|
|
20
21
|
const PHASE_RUNNERS = {
|
|
21
22
|
feature_analysis: runFeatureAnalysisPhase,
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Code Refine Analyzer
|
|
3
3
|
* Processes GitHub PR review feedback and refines code accordingly
|
|
4
|
+
* Includes built-in verification loop that resolves PR comments and dismisses reviews
|
|
4
5
|
*/
|
|
5
6
|
import { EdsgerConfig } from '../../types/index.js';
|
|
7
|
+
import { type CodeRefineVerificationResult } from '../code-refine-verification/index.js';
|
|
8
|
+
export declare const MAX_REFINE_ITERATIONS = 10;
|
|
6
9
|
export interface CodeRefineOptions {
|
|
7
10
|
featureId: string;
|
|
8
11
|
githubToken: string;
|
|
@@ -34,8 +37,12 @@ export interface CodeRefineResult {
|
|
|
34
37
|
summary?: string;
|
|
35
38
|
filesModified?: string[];
|
|
36
39
|
commitsCreated?: number;
|
|
40
|
+
iterations?: number;
|
|
41
|
+
verificationResult?: CodeRefineVerificationResult;
|
|
37
42
|
}
|
|
38
43
|
/**
|
|
39
|
-
* Main code refine function
|
|
44
|
+
* Main code refine function with built-in verification loop
|
|
45
|
+
* Similar to technical-design, this includes an iterative improvement cycle:
|
|
46
|
+
* refine → verification → improve → re-refine (if needed)
|
|
40
47
|
*/
|
|
41
48
|
export declare const refineCodeFromPRFeedback: (options: CodeRefineOptions, config: EdsgerConfig) => Promise<CodeRefineResult>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Code Refine Analyzer
|
|
3
3
|
* Processes GitHub PR review feedback and refines code accordingly
|
|
4
|
+
* Includes built-in verification loop that resolves PR comments and dismisses reviews
|
|
4
5
|
*/
|
|
5
6
|
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
6
7
|
import { logInfo, logError } from '../../utils/logger.js';
|
|
@@ -10,8 +11,11 @@ import { getFeedbacksForPhase, formatFeedbacksForContext, } from '../../services
|
|
|
10
11
|
import { createSystemPrompt, createCodeRefinePrompt } from './prompts.js';
|
|
11
12
|
import { preparePhaseGitEnvironment, prepareCustomBranchGitEnvironment, hasUncommittedChanges, getUncommittedFiles, syncFeatBranchWithMain, } from '../../utils/git-branch-manager.js';
|
|
12
13
|
import { getFeature } from '../../api/features/get-feature.js';
|
|
13
|
-
import {
|
|
14
|
+
import { getReviewedBranch, updateBranch, } from '../../services/branches.js';
|
|
14
15
|
import { parsePullRequestUrl } from './context.js';
|
|
16
|
+
import { verifyAndResolveComments, } from '../code-refine-verification/index.js';
|
|
17
|
+
// Maximum number of refine + verification iterations
|
|
18
|
+
export const MAX_REFINE_ITERATIONS = 10;
|
|
15
19
|
function userMessage(content) {
|
|
16
20
|
return {
|
|
17
21
|
type: 'user',
|
|
@@ -58,29 +62,31 @@ const pushChanges = (verbose) => {
|
|
|
58
62
|
}
|
|
59
63
|
};
|
|
60
64
|
/**
|
|
61
|
-
* Main code refine function
|
|
65
|
+
* Main code refine function with built-in verification loop
|
|
66
|
+
* Similar to technical-design, this includes an iterative improvement cycle:
|
|
67
|
+
* refine → verification → improve → re-refine (if needed)
|
|
62
68
|
*/
|
|
63
69
|
export const refineCodeFromPRFeedback = async (options, config) => {
|
|
64
70
|
const { featureId, githubToken, verbose } = options;
|
|
65
71
|
if (verbose) {
|
|
66
72
|
logInfo(`Starting code refine for feature ID: ${featureId}`);
|
|
67
73
|
}
|
|
68
|
-
// For multi-branch features, find the branch that
|
|
69
|
-
// and use its branch_name for refine
|
|
74
|
+
// For multi-branch features, find the branch that has been reviewed
|
|
75
|
+
// (status = 'reviewed' after code-review phase) and use its branch_name for refine
|
|
70
76
|
let branchName = `dev/${featureId}`; // Default for single-branch features
|
|
71
77
|
let currentBranch = null;
|
|
72
78
|
try {
|
|
73
|
-
currentBranch = await
|
|
79
|
+
currentBranch = await getReviewedBranch({ featureId, verbose });
|
|
74
80
|
if (currentBranch && currentBranch.branch_name) {
|
|
75
81
|
// Use branch_name directly (already stored as dev/...)
|
|
76
82
|
branchName = currentBranch.branch_name;
|
|
77
83
|
if (verbose) {
|
|
78
|
-
logInfo(`📋 Found
|
|
84
|
+
logInfo(`📋 Found reviewed branch: ${currentBranch.name}`);
|
|
79
85
|
logInfo(` Using dev branch: ${branchName}`);
|
|
80
86
|
}
|
|
81
87
|
}
|
|
82
88
|
else if (verbose) {
|
|
83
|
-
logInfo(`ℹ️ No
|
|
89
|
+
logInfo(`ℹ️ No reviewed branch found, using default: ${branchName}`);
|
|
84
90
|
}
|
|
85
91
|
}
|
|
86
92
|
catch (error) {
|
|
@@ -129,13 +135,14 @@ export const refineCodeFromPRFeedback = async (options, config) => {
|
|
|
129
135
|
? prepareCustomBranchGitEnvironment(branchName, 'main', verbose)
|
|
130
136
|
: preparePhaseGitEnvironment(featureId, 'main', verbose);
|
|
131
137
|
try {
|
|
132
|
-
// Fetch code refine context (PR reviews and comments)
|
|
138
|
+
// Fetch initial code refine context (PR reviews and comments)
|
|
133
139
|
if (verbose) {
|
|
134
140
|
logInfo('Fetching code refine context from GitHub PR...');
|
|
135
141
|
}
|
|
136
|
-
const
|
|
142
|
+
const initialContext = await fetchCodeRefineContext(featureId, githubToken, verbose, pullRequestUrl || undefined);
|
|
137
143
|
// Check if there are any reviews or comments to address
|
|
138
|
-
if (
|
|
144
|
+
if (initialContext.reviews.length === 0 &&
|
|
145
|
+
initialContext.reviewComments.length === 0) {
|
|
139
146
|
if (verbose) {
|
|
140
147
|
logInfo('✅ No review comments or change requests found. Nothing to refine.');
|
|
141
148
|
}
|
|
@@ -144,129 +151,43 @@ export const refineCodeFromPRFeedback = async (options, config) => {
|
|
|
144
151
|
status: 'success',
|
|
145
152
|
message: 'No review feedback to address',
|
|
146
153
|
summary: 'No change requests or review comments found on the PR',
|
|
154
|
+
iterations: 0,
|
|
147
155
|
};
|
|
148
156
|
}
|
|
149
157
|
if (verbose) {
|
|
150
|
-
logInfo(`📋 Found ${
|
|
158
|
+
logInfo(`📋 Found ${initialContext.reviews.length} reviews and ${initialContext.reviewComments.length} comments to address`);
|
|
151
159
|
}
|
|
152
|
-
//
|
|
153
|
-
|
|
154
|
-
let
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
feedbacksInfo = await formatFeedbacksForContext(feedbacksContext);
|
|
160
|
-
if (verbose) {
|
|
161
|
-
logInfo(`Added ${feedbacksContext.feedbacks.length} human feedbacks to code refine context${currentBranch ? ` (including branch-specific for "${currentBranch.name}")` : ''}`);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
catch (error) {
|
|
160
|
+
// Iterative refine → verification loop
|
|
161
|
+
let currentIteration = 0;
|
|
162
|
+
let verificationFailureContext;
|
|
163
|
+
let lastRefineResult = null;
|
|
164
|
+
let lastVerificationResult = null;
|
|
165
|
+
while (currentIteration < MAX_REFINE_ITERATIONS) {
|
|
166
|
+
currentIteration++;
|
|
166
167
|
if (verbose) {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
// Feature branch switching is handled by preparePhaseGitEnvironment above
|
|
171
|
-
// Create prompt for code refine
|
|
172
|
-
const systemPrompt = createSystemPrompt();
|
|
173
|
-
const refinePrompt = createCodeRefinePrompt(featureId, context, feedbacksInfo, options.verificationFailureContext);
|
|
174
|
-
let lastAssistantResponse = '';
|
|
175
|
-
let structuredRefineResult = null;
|
|
176
|
-
if (verbose) {
|
|
177
|
-
logInfo('Starting Claude Code query for code refine...');
|
|
178
|
-
}
|
|
179
|
-
// Use Claude Code SDK to refine the code
|
|
180
|
-
for await (const message of query({
|
|
181
|
-
prompt: prompt(refinePrompt),
|
|
182
|
-
options: {
|
|
183
|
-
systemPrompt: {
|
|
184
|
-
type: 'preset',
|
|
185
|
-
preset: 'claude_code',
|
|
186
|
-
append: systemPrompt,
|
|
187
|
-
},
|
|
188
|
-
model: config.claude.model || 'sonnet',
|
|
189
|
-
maxTurns: 2000,
|
|
190
|
-
permissionMode: 'bypassPermissions',
|
|
191
|
-
},
|
|
192
|
-
})) {
|
|
193
|
-
if (verbose) {
|
|
194
|
-
logInfo(`Received message type: ${message.type}`);
|
|
195
|
-
}
|
|
196
|
-
// Stream the code refine process
|
|
197
|
-
if (message.type === 'assistant' && message.message?.content) {
|
|
198
|
-
for (const content of message.message.content) {
|
|
199
|
-
if (content.type === 'text') {
|
|
200
|
-
lastAssistantResponse += content.text + '\n';
|
|
201
|
-
if (verbose) {
|
|
202
|
-
console.log(`\n🔧 ${content.text}`);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
else if (content.type === 'tool_use') {
|
|
206
|
-
if (verbose) {
|
|
207
|
-
console.log(`\n🛠️ ${content.name}: ${content.input.description || 'Running...'}`);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
else if (message.type === 'result') {
|
|
213
|
-
if (message.subtype === 'success') {
|
|
214
|
-
logInfo('\n🛠️ Code refine completed, parsing results...');
|
|
215
|
-
try {
|
|
216
|
-
const responseText = message.result || lastAssistantResponse;
|
|
217
|
-
let jsonResult = null;
|
|
218
|
-
const jsonBlockMatch = responseText.match(/```json\s*\n([\s\S]*?)\n\s*```/);
|
|
219
|
-
if (jsonBlockMatch) {
|
|
220
|
-
jsonResult = JSON.parse(jsonBlockMatch[1]);
|
|
221
|
-
}
|
|
222
|
-
else {
|
|
223
|
-
jsonResult = JSON.parse(responseText);
|
|
224
|
-
}
|
|
225
|
-
if (jsonResult && jsonResult.refine_result) {
|
|
226
|
-
structuredRefineResult = jsonResult.refine_result;
|
|
227
|
-
}
|
|
228
|
-
else {
|
|
229
|
-
throw new Error('Invalid JSON structure');
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
catch (error) {
|
|
233
|
-
logError(`Failed to parse structured refine result: ${error}`);
|
|
234
|
-
structuredRefineResult = parseCodeRefineResponse(message.result || lastAssistantResponse);
|
|
235
|
-
}
|
|
168
|
+
if (currentIteration === 1) {
|
|
169
|
+
logInfo(`\n🔄 Starting refine iteration ${currentIteration}/${MAX_REFINE_ITERATIONS}`);
|
|
236
170
|
}
|
|
237
171
|
else {
|
|
238
|
-
|
|
239
|
-
if (message.subtype === 'error_max_turns') {
|
|
240
|
-
logError('💡 Try simplifying the changes or reducing scope');
|
|
241
|
-
}
|
|
242
|
-
if (lastAssistantResponse) {
|
|
243
|
-
try {
|
|
244
|
-
const responseText = lastAssistantResponse;
|
|
245
|
-
let jsonResult = null;
|
|
246
|
-
const jsonBlockMatch = responseText.match(/```json\s*\n([\s\S]*?)\n\s*```/);
|
|
247
|
-
if (jsonBlockMatch) {
|
|
248
|
-
jsonResult = JSON.parse(jsonBlockMatch[1]);
|
|
249
|
-
if (jsonResult && jsonResult.refine_result) {
|
|
250
|
-
structuredRefineResult = jsonResult.refine_result;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
else {
|
|
254
|
-
structuredRefineResult = parseCodeRefineResponse(lastAssistantResponse);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
catch (error) {
|
|
258
|
-
logError(`Failed to parse assistant response: ${error}`);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
172
|
+
logInfo(`\n🔄 Retry iteration ${currentIteration}/${MAX_REFINE_ITERATIONS}: Improving based on verification feedback...`);
|
|
261
173
|
}
|
|
262
174
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
175
|
+
// Execute refine for this iteration
|
|
176
|
+
const refineResult = await executeRefineIteration(featureId, githubToken, config, pullRequestUrl || undefined, currentBranch, verificationFailureContext, verbose);
|
|
177
|
+
if (refineResult.status === 'error') {
|
|
178
|
+
// Refine failed - return error
|
|
179
|
+
return {
|
|
180
|
+
featureId,
|
|
181
|
+
status: 'error',
|
|
182
|
+
message: refineResult.message || 'Code refine failed',
|
|
183
|
+
iterations: currentIteration,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
lastRefineResult = refineResult;
|
|
187
|
+
// Push changes before verification
|
|
267
188
|
if (hasUncommittedChanges()) {
|
|
268
189
|
const uncommittedFiles = getUncommittedFiles();
|
|
269
|
-
const errorMsg = `Code refine completed but there are uncommitted changes.
|
|
190
|
+
const errorMsg = `Code refine completed but there are uncommitted changes.
|
|
270
191
|
|
|
271
192
|
Uncommitted files:
|
|
272
193
|
${uncommittedFiles.join('\n')}
|
|
@@ -277,6 +198,7 @@ Please ensure Claude Code commits all changes before completing the refine phase
|
|
|
277
198
|
featureId,
|
|
278
199
|
status: 'error',
|
|
279
200
|
message: errorMsg,
|
|
201
|
+
iterations: currentIteration,
|
|
280
202
|
};
|
|
281
203
|
}
|
|
282
204
|
if (verbose) {
|
|
@@ -284,24 +206,6 @@ Please ensure Claude Code commits all changes before completing the refine phase
|
|
|
284
206
|
}
|
|
285
207
|
try {
|
|
286
208
|
pushChanges(verbose);
|
|
287
|
-
const { summary, files_modified, commits_created } = structuredRefineResult;
|
|
288
|
-
if (verbose) {
|
|
289
|
-
logInfo(`Code refine completed for feature: ${featureId}`);
|
|
290
|
-
if (files_modified?.length > 0) {
|
|
291
|
-
logInfo(`Files modified: ${files_modified.join(', ')}`);
|
|
292
|
-
}
|
|
293
|
-
if (commits_created) {
|
|
294
|
-
logInfo(`Commits created: ${commits_created}`);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
return {
|
|
298
|
-
featureId,
|
|
299
|
-
status: 'success',
|
|
300
|
-
message: 'Code successfully refined based on PR feedback',
|
|
301
|
-
summary: summary || 'Code refined based on PR review feedback',
|
|
302
|
-
filesModified: files_modified || [],
|
|
303
|
-
commitsCreated: commits_created || 1,
|
|
304
|
-
};
|
|
305
209
|
}
|
|
306
210
|
catch (pushError) {
|
|
307
211
|
logError(`Failed to push changes: ${pushError}`);
|
|
@@ -309,16 +213,86 @@ Please ensure Claude Code commits all changes before completing the refine phase
|
|
|
309
213
|
featureId,
|
|
310
214
|
status: 'error',
|
|
311
215
|
message: `Code refined but failed to push: ${pushError}`,
|
|
216
|
+
iterations: currentIteration,
|
|
312
217
|
};
|
|
313
218
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
219
|
+
// Run verification
|
|
220
|
+
if (verbose) {
|
|
221
|
+
logInfo('\n🔍 Running verification to check if all comments are addressed...');
|
|
222
|
+
}
|
|
223
|
+
const verificationResult = await verifyAndResolveComments({
|
|
317
224
|
featureId,
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
225
|
+
githubToken,
|
|
226
|
+
config,
|
|
227
|
+
verbose,
|
|
228
|
+
});
|
|
229
|
+
lastVerificationResult = verificationResult;
|
|
230
|
+
// If verification passed, we're done!
|
|
231
|
+
if (verificationResult.status === 'success') {
|
|
232
|
+
if (verbose) {
|
|
233
|
+
logInfo('✅ Verification passed! All PR comments have been addressed and resolved.');
|
|
234
|
+
}
|
|
235
|
+
// Update branch status to 'refined' for multi-branch features
|
|
236
|
+
if (currentBranch) {
|
|
237
|
+
await updateBranch(currentBranch.id, { status: 'refined' }, verbose);
|
|
238
|
+
if (verbose) {
|
|
239
|
+
logInfo(`✅ Branch "${currentBranch.name}" status updated to 'refined'`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
featureId,
|
|
244
|
+
status: 'success',
|
|
245
|
+
message: verificationResult.message,
|
|
246
|
+
summary: lastRefineResult.summary ||
|
|
247
|
+
'Code refined based on PR review feedback',
|
|
248
|
+
filesModified: lastRefineResult.files_modified || [],
|
|
249
|
+
commitsCreated: lastRefineResult.commits_created || 1,
|
|
250
|
+
iterations: currentIteration,
|
|
251
|
+
verificationResult,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
// Verification failed - prepare context for retry
|
|
255
|
+
if (currentIteration < MAX_REFINE_ITERATIONS) {
|
|
256
|
+
const verificationData = verificationResult.data;
|
|
257
|
+
const suggestions = verificationData.suggestions || [];
|
|
258
|
+
const unresolvedCommentDetails = verificationData.unresolvedCommentDetails || [];
|
|
259
|
+
const unresolvedReviewDetails = verificationData.unresolvedReviewDetails;
|
|
260
|
+
verificationFailureContext = {
|
|
261
|
+
attempt: currentIteration + 1,
|
|
262
|
+
suggestions,
|
|
263
|
+
unresolvedCommentDetails,
|
|
264
|
+
unresolvedReviewDetails,
|
|
265
|
+
};
|
|
266
|
+
if (verbose) {
|
|
267
|
+
logInfo(`\n⚠️ Verification failed: ${verificationResult.message}`);
|
|
268
|
+
if (suggestions.length > 0) {
|
|
269
|
+
logInfo(`💡 Suggestions for next iteration:`);
|
|
270
|
+
suggestions.forEach((suggestion) => {
|
|
271
|
+
logInfo(` ${suggestion}`);
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
logInfo(`\n🔄 Will retry... (${MAX_REFINE_ITERATIONS - currentIteration} attempts remaining)`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
// Max iterations reached
|
|
279
|
+
if (verbose) {
|
|
280
|
+
logInfo(`\n⚠️ Maximum iterations (${MAX_REFINE_ITERATIONS}) reached. Verification still failing.`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
321
283
|
}
|
|
284
|
+
// Return last verification result (which failed)
|
|
285
|
+
return {
|
|
286
|
+
featureId,
|
|
287
|
+
status: 'error',
|
|
288
|
+
message: lastVerificationResult?.message ||
|
|
289
|
+
'Verification failed after max iterations',
|
|
290
|
+
summary: lastRefineResult?.summary,
|
|
291
|
+
filesModified: lastRefineResult?.files_modified || [],
|
|
292
|
+
commitsCreated: lastRefineResult?.commits_created || 1,
|
|
293
|
+
iterations: currentIteration,
|
|
294
|
+
verificationResult: lastVerificationResult || undefined,
|
|
295
|
+
};
|
|
322
296
|
}
|
|
323
297
|
catch (error) {
|
|
324
298
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -334,6 +308,136 @@ Please ensure Claude Code commits all changes before completing the refine phase
|
|
|
334
308
|
cleanupGit();
|
|
335
309
|
}
|
|
336
310
|
};
|
|
311
|
+
/**
|
|
312
|
+
* Execute a single refine iteration
|
|
313
|
+
*/
|
|
314
|
+
async function executeRefineIteration(featureId, githubToken, config, pullRequestUrl, currentBranch, verificationFailureContext, verbose) {
|
|
315
|
+
// Fetch code refine context (PR reviews and comments)
|
|
316
|
+
const context = await fetchCodeRefineContext(featureId, githubToken, verbose, pullRequestUrl);
|
|
317
|
+
// Fetch additional feedbacks for code-refine phase
|
|
318
|
+
let feedbacksInfo;
|
|
319
|
+
try {
|
|
320
|
+
const feedbacksContext = await getFeedbacksForPhase({ featureId, verbose }, 'code_refine', currentBranch?.id);
|
|
321
|
+
if (feedbacksContext.feedbacks.length > 0) {
|
|
322
|
+
feedbacksInfo = await formatFeedbacksForContext(feedbacksContext);
|
|
323
|
+
if (verbose) {
|
|
324
|
+
logInfo(`Added ${feedbacksContext.feedbacks.length} human feedbacks to code refine context${currentBranch ? ` (including branch-specific for "${currentBranch.name}")` : ''}`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
if (verbose) {
|
|
330
|
+
logInfo(`Note: Could not fetch feedbacks (${error instanceof Error ? error.message : String(error)})`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
// Create prompt for code refine
|
|
334
|
+
const systemPrompt = createSystemPrompt();
|
|
335
|
+
const refinePrompt = createCodeRefinePrompt(featureId, context, feedbacksInfo, verificationFailureContext);
|
|
336
|
+
let lastAssistantResponse = '';
|
|
337
|
+
let structuredRefineResult = null;
|
|
338
|
+
if (verbose) {
|
|
339
|
+
logInfo('Starting Claude Code query for code refine...');
|
|
340
|
+
}
|
|
341
|
+
// Use Claude Code SDK to refine the code
|
|
342
|
+
for await (const message of query({
|
|
343
|
+
prompt: prompt(refinePrompt),
|
|
344
|
+
options: {
|
|
345
|
+
systemPrompt: {
|
|
346
|
+
type: 'preset',
|
|
347
|
+
preset: 'claude_code',
|
|
348
|
+
append: systemPrompt,
|
|
349
|
+
},
|
|
350
|
+
model: config.claude.model || 'sonnet',
|
|
351
|
+
maxTurns: 2000,
|
|
352
|
+
permissionMode: 'bypassPermissions',
|
|
353
|
+
},
|
|
354
|
+
})) {
|
|
355
|
+
if (verbose) {
|
|
356
|
+
logInfo(`Received message type: ${message.type}`);
|
|
357
|
+
}
|
|
358
|
+
// Stream the code refine process
|
|
359
|
+
if (message.type === 'assistant' && message.message?.content) {
|
|
360
|
+
for (const content of message.message.content) {
|
|
361
|
+
if (content.type === 'text') {
|
|
362
|
+
lastAssistantResponse += content.text + '\n';
|
|
363
|
+
if (verbose) {
|
|
364
|
+
console.log(`\n🔧 ${content.text}`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
else if (content.type === 'tool_use') {
|
|
368
|
+
if (verbose) {
|
|
369
|
+
console.log(`\n🛠️ ${content.name}: ${content.input.description || 'Running...'}`);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
else if (message.type === 'result') {
|
|
375
|
+
if (message.subtype === 'success') {
|
|
376
|
+
logInfo('\n🛠️ Code refine completed, parsing results...');
|
|
377
|
+
try {
|
|
378
|
+
const responseText = message.result || lastAssistantResponse;
|
|
379
|
+
let jsonResult = null;
|
|
380
|
+
const jsonBlockMatch = responseText.match(/```json\s*\n([\s\S]*?)\n\s*```/);
|
|
381
|
+
if (jsonBlockMatch) {
|
|
382
|
+
jsonResult = JSON.parse(jsonBlockMatch[1]);
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
jsonResult = JSON.parse(responseText);
|
|
386
|
+
}
|
|
387
|
+
if (jsonResult && jsonResult.refine_result) {
|
|
388
|
+
structuredRefineResult = jsonResult.refine_result;
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
throw new Error('Invalid JSON structure');
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
catch (error) {
|
|
395
|
+
logError(`Failed to parse structured refine result: ${error}`);
|
|
396
|
+
structuredRefineResult = parseCodeRefineResponse(message.result || lastAssistantResponse);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
logError(`\n⚠️ Code refine incomplete: ${message.subtype}`);
|
|
401
|
+
if (message.subtype === 'error_max_turns') {
|
|
402
|
+
logError('💡 Try simplifying the changes or reducing scope');
|
|
403
|
+
}
|
|
404
|
+
if (lastAssistantResponse) {
|
|
405
|
+
try {
|
|
406
|
+
const responseText = lastAssistantResponse;
|
|
407
|
+
let jsonResult = null;
|
|
408
|
+
const jsonBlockMatch = responseText.match(/```json\s*\n([\s\S]*?)\n\s*```/);
|
|
409
|
+
if (jsonBlockMatch) {
|
|
410
|
+
jsonResult = JSON.parse(jsonBlockMatch[1]);
|
|
411
|
+
if (jsonResult && jsonResult.refine_result) {
|
|
412
|
+
structuredRefineResult = jsonResult.refine_result;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
structuredRefineResult = parseCodeRefineResponse(lastAssistantResponse);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
catch (error) {
|
|
420
|
+
logError(`Failed to parse assistant response: ${error}`);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
if (structuredRefineResult) {
|
|
427
|
+
return {
|
|
428
|
+
status: 'success',
|
|
429
|
+
summary: structuredRefineResult.summary,
|
|
430
|
+
files_modified: structuredRefineResult.files_modified,
|
|
431
|
+
commits_created: structuredRefineResult.commits_created,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
else {
|
|
435
|
+
return {
|
|
436
|
+
status: 'error',
|
|
437
|
+
message: 'Code refine failed or incomplete',
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
}
|
|
337
441
|
function parseCodeRefineResponse(response) {
|
|
338
442
|
const summaryMatch = response.match(/## Refine Summary\n([\s\S]*?)(?=\n##|\n\n|$)/);
|
|
339
443
|
const summary = summaryMatch
|
|
@@ -8,7 +8,7 @@ import { Octokit } from '@octokit/rest';
|
|
|
8
8
|
import { fetchCodeReviewContext, formatContextForPrompt, } from './context.js';
|
|
9
9
|
import { getFeedbacksForPhase, formatFeedbacksForContext, } from '../../services/feedbacks.js';
|
|
10
10
|
import { preparePhaseGitEnvironment, prepareCustomBranchGitEnvironment, } from '../../utils/git-branch-manager.js';
|
|
11
|
-
import { getReadyForReviewBranch, } from '../../services/branches.js';
|
|
11
|
+
import { getReadyForReviewBranch, updateBranch, } from '../../services/branches.js';
|
|
12
12
|
import { getFeature } from '../../api/features/get-feature.js';
|
|
13
13
|
function userMessage(content) {
|
|
14
14
|
return {
|
|
@@ -319,6 +319,13 @@ export const reviewPullRequest = async (options, config) => {
|
|
|
319
319
|
overall_assessment ||
|
|
320
320
|
'Code review completed. No issues found.',
|
|
321
321
|
});
|
|
322
|
+
// Update branch status to 'reviewed' for multi-branch features
|
|
323
|
+
if (currentBranch) {
|
|
324
|
+
await updateBranch(currentBranch.id, { status: 'reviewed' }, verbose);
|
|
325
|
+
if (verbose) {
|
|
326
|
+
logInfo(`✅ Branch "${currentBranch.name}" status updated to 'reviewed'`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
322
329
|
return {
|
|
323
330
|
featureId,
|
|
324
331
|
status: 'success',
|
|
@@ -389,6 +396,13 @@ export const reviewPullRequest = async (options, config) => {
|
|
|
389
396
|
body: (summary || overall_assessment || 'Code review completed.') +
|
|
390
397
|
'\n\n**Note**: Some review comments could not be posted because they referenced lines not present in the diff.',
|
|
391
398
|
});
|
|
399
|
+
// Update branch status to 'reviewed' for multi-branch features
|
|
400
|
+
if (currentBranch) {
|
|
401
|
+
await updateBranch(currentBranch.id, { status: 'reviewed' }, verbose);
|
|
402
|
+
if (verbose) {
|
|
403
|
+
logInfo(`✅ Branch "${currentBranch.name}" status updated to 'reviewed'`);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
392
406
|
return {
|
|
393
407
|
featureId,
|
|
394
408
|
status: 'success',
|
|
@@ -418,6 +432,13 @@ export const reviewPullRequest = async (options, config) => {
|
|
|
418
432
|
logInfo(`Review ID: ${review.data.id}`);
|
|
419
433
|
logInfo(`Comments posted: ${comments.length}`);
|
|
420
434
|
}
|
|
435
|
+
// Update branch status to 'reviewed' for multi-branch features
|
|
436
|
+
if (currentBranch) {
|
|
437
|
+
await updateBranch(currentBranch.id, { status: 'reviewed' }, verbose);
|
|
438
|
+
if (verbose) {
|
|
439
|
+
logInfo(`✅ Branch "${currentBranch.name}" status updated to 'reviewed'`);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
421
442
|
return {
|
|
422
443
|
featureId,
|
|
423
444
|
status: 'success',
|
|
@@ -12,7 +12,7 @@ export interface Branch {
|
|
|
12
12
|
base_branch_id: string | null;
|
|
13
13
|
pull_request_url: string | null;
|
|
14
14
|
pull_request_number: number | null;
|
|
15
|
-
status: 'pending' | 'in_progress' | 'ready_for_review' | 'merged' | 'closed';
|
|
15
|
+
status: 'pending' | 'in_progress' | 'ready_for_review' | 'reviewed' | 'refined' | 'merged' | 'closed';
|
|
16
16
|
created_at: string;
|
|
17
17
|
updated_at: string;
|
|
18
18
|
}
|
|
@@ -80,6 +80,11 @@ export declare function getBranchById(branchId: string, verbose?: boolean): Prom
|
|
|
80
80
|
* Returns the first branch with status 'ready_for_review'
|
|
81
81
|
*/
|
|
82
82
|
export declare function getReadyForReviewBranch(options: PipelinePhaseOptions): Promise<Branch | null>;
|
|
83
|
+
/**
|
|
84
|
+
* Get the branch that has been reviewed (for code-refine phase)
|
|
85
|
+
* Returns the first branch with status 'reviewed'
|
|
86
|
+
*/
|
|
87
|
+
export declare function getReviewedBranch(options: PipelinePhaseOptions): Promise<Branch | null>;
|
|
83
88
|
/**
|
|
84
89
|
* Get the branch that is in progress (in_progress or ready_for_review)
|
|
85
90
|
* Useful for phases that need to work on the current active branch
|
|
@@ -95,17 +95,28 @@ export function formatBranchesForContext(branches) {
|
|
|
95
95
|
if (!branches || branches.length === 0) {
|
|
96
96
|
return 'No branches defined yet.';
|
|
97
97
|
}
|
|
98
|
+
const getStatusEmoji = (status) => {
|
|
99
|
+
switch (status) {
|
|
100
|
+
case 'merged':
|
|
101
|
+
return '✅';
|
|
102
|
+
case 'refined':
|
|
103
|
+
return '🎯';
|
|
104
|
+
case 'reviewed':
|
|
105
|
+
return '📝';
|
|
106
|
+
case 'ready_for_review':
|
|
107
|
+
return '👀';
|
|
108
|
+
case 'in_progress':
|
|
109
|
+
return '🔄';
|
|
110
|
+
case 'closed':
|
|
111
|
+
return '❌';
|
|
112
|
+
case 'pending':
|
|
113
|
+
default:
|
|
114
|
+
return '⏳';
|
|
115
|
+
}
|
|
116
|
+
};
|
|
98
117
|
const branchList = branches
|
|
99
118
|
.map((b, idx) => {
|
|
100
|
-
const statusEmoji = b.status
|
|
101
|
-
? '✅'
|
|
102
|
-
: b.status === 'in_progress'
|
|
103
|
-
? '🔄'
|
|
104
|
-
: b.status === 'ready_for_review'
|
|
105
|
-
? '👀'
|
|
106
|
-
: b.status === 'closed'
|
|
107
|
-
? '❌'
|
|
108
|
-
: '⏳';
|
|
119
|
+
const statusEmoji = getStatusEmoji(b.status);
|
|
109
120
|
return `${idx + 1}. **${b.name}** ${statusEmoji}
|
|
110
121
|
- Status: ${b.status}
|
|
111
122
|
- Branch: ${b.branch_name || 'Not created'}
|
|
@@ -211,6 +222,14 @@ export async function getReadyForReviewBranch(options) {
|
|
|
211
222
|
const branches = await getBranches(options);
|
|
212
223
|
return branches.find((b) => b.status === 'ready_for_review') || null;
|
|
213
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* Get the branch that has been reviewed (for code-refine phase)
|
|
227
|
+
* Returns the first branch with status 'reviewed'
|
|
228
|
+
*/
|
|
229
|
+
export async function getReviewedBranch(options) {
|
|
230
|
+
const branches = await getBranches(options);
|
|
231
|
+
return branches.find((b) => b.status === 'reviewed') || null;
|
|
232
|
+
}
|
|
214
233
|
/**
|
|
215
234
|
* Get the branch that is in progress (in_progress or ready_for_review)
|
|
216
235
|
* Useful for phases that need to work on the current active branch
|