edsger 0.4.11 → 0.5.1
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/phases/code-review/analyzer.d.ts +32 -0
- package/dist/phases/code-review/analyzer.js +369 -0
- package/dist/phases/code-review/context-fetcher.d.ts +92 -0
- package/dist/phases/code-review/context-fetcher.js +292 -0
- package/dist/phases/code-review/index.d.ts +7 -0
- package/dist/phases/code-review/index.js +7 -0
- package/dist/services/audit-logs.d.ts +2 -2
- package/dist/types/pipeline.d.ts +1 -1
- package/dist/workflow-runner/config/phase-configs.js +26 -0
- package/dist/workflow-runner/executors/phase-executor.d.ts +2 -2
- package/dist/workflow-runner/executors/phase-executor.js +2 -2
- package/dist/workflow-runner/feature-workflow-runner.js +4 -0
- package/dist/workflow-runner/pipeline-runner.js +40 -1
- package/package.json +1 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Review Analyzer
|
|
3
|
+
* Reviews GitHub PR code and creates review comments with REQUEST_CHANGES
|
|
4
|
+
*/
|
|
5
|
+
import { EdsgerConfig } from '../../types/index.js';
|
|
6
|
+
export interface CodeReviewOptions {
|
|
7
|
+
featureId: string;
|
|
8
|
+
mcpServerUrl: string;
|
|
9
|
+
mcpToken: string;
|
|
10
|
+
githubToken: string;
|
|
11
|
+
verbose?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface ReviewComment {
|
|
14
|
+
path: string;
|
|
15
|
+
line: number;
|
|
16
|
+
side: 'LEFT' | 'RIGHT';
|
|
17
|
+
body: string;
|
|
18
|
+
}
|
|
19
|
+
export interface CodeReviewResult {
|
|
20
|
+
featureId: string;
|
|
21
|
+
status: 'success' | 'error';
|
|
22
|
+
message: string;
|
|
23
|
+
reviewId?: number;
|
|
24
|
+
reviewUrl?: string;
|
|
25
|
+
commentsCount?: number;
|
|
26
|
+
summary?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Main code review function
|
|
30
|
+
*/
|
|
31
|
+
export declare const reviewPullRequest: (options: CodeReviewOptions, config: EdsgerConfig) => Promise<CodeReviewResult>;
|
|
32
|
+
export declare function checkCodeReviewRequirements(): Promise<boolean>;
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Review Analyzer
|
|
3
|
+
* Reviews GitHub PR code and creates review comments with REQUEST_CHANGES
|
|
4
|
+
*/
|
|
5
|
+
import { query } from '@anthropic-ai/claude-code';
|
|
6
|
+
import { logInfo, logError } from '../../utils/logger.js';
|
|
7
|
+
import { Octokit } from '@octokit/rest';
|
|
8
|
+
import { fetchCodeReviewContext, formatContextForPrompt, } from './context-fetcher.js';
|
|
9
|
+
import { getFeedbacksForPhase, formatFeedbacksForContext, } from '../../services/feedbacks.js';
|
|
10
|
+
function userMessage(content) {
|
|
11
|
+
return {
|
|
12
|
+
type: 'user',
|
|
13
|
+
message: { role: 'user', content: content },
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
async function* prompt(reviewPrompt) {
|
|
17
|
+
yield userMessage(reviewPrompt);
|
|
18
|
+
await new Promise((res) => setTimeout(res, 10000));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Main code review function
|
|
22
|
+
*/
|
|
23
|
+
export const reviewPullRequest = async (options, config) => {
|
|
24
|
+
const { featureId, mcpServerUrl, mcpToken, githubToken, verbose } = options;
|
|
25
|
+
if (verbose) {
|
|
26
|
+
logInfo(`Starting code review for feature ID: ${featureId}`);
|
|
27
|
+
logInfo(`Using MCP server: ${mcpServerUrl}`);
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
// Fetch code review context (PR data, files, commits)
|
|
31
|
+
if (verbose) {
|
|
32
|
+
logInfo('Fetching code review context from GitHub PR...');
|
|
33
|
+
}
|
|
34
|
+
const context = await fetchCodeReviewContext(mcpServerUrl, mcpToken, featureId, githubToken, verbose);
|
|
35
|
+
// Check if there are any files to review
|
|
36
|
+
if (context.files.length === 0) {
|
|
37
|
+
if (verbose) {
|
|
38
|
+
logInfo('✅ No files to review in the PR.');
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
featureId,
|
|
42
|
+
status: 'success',
|
|
43
|
+
message: 'No files to review',
|
|
44
|
+
summary: 'No changed files found in the PR',
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
if (verbose) {
|
|
48
|
+
logInfo(`📋 Found ${context.files.length} files to review across ${context.commits.length} commits`);
|
|
49
|
+
}
|
|
50
|
+
// Fetch additional feedbacks for code-review phase
|
|
51
|
+
let feedbacksInfo;
|
|
52
|
+
try {
|
|
53
|
+
const feedbacksContext = await getFeedbacksForPhase({ featureId, mcpServerUrl, mcpToken, verbose }, 'code_review');
|
|
54
|
+
if (feedbacksContext.feedbacks.length > 0) {
|
|
55
|
+
feedbacksInfo = formatFeedbacksForContext(feedbacksContext);
|
|
56
|
+
if (verbose) {
|
|
57
|
+
logInfo(`Added ${feedbacksContext.feedbacks.length} human feedbacks to code review context`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (verbose) {
|
|
63
|
+
logInfo(`Note: Could not fetch feedbacks (${error instanceof Error ? error.message : String(error)})`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Create prompt for code review
|
|
67
|
+
const systemPrompt = createSystemPrompt(config);
|
|
68
|
+
const reviewPrompt = createCodeReviewPrompt(featureId, context, feedbacksInfo);
|
|
69
|
+
let lastAssistantResponse = '';
|
|
70
|
+
let structuredReviewResult = null;
|
|
71
|
+
if (verbose) {
|
|
72
|
+
logInfo('Starting Claude analysis for code review...');
|
|
73
|
+
}
|
|
74
|
+
// Use Claude to analyze the code (not using Claude Code SDK for actual implementation)
|
|
75
|
+
// This is a simplified approach - just analyze the code
|
|
76
|
+
for await (const message of query({
|
|
77
|
+
prompt: prompt(reviewPrompt),
|
|
78
|
+
options: {
|
|
79
|
+
appendSystemPrompt: systemPrompt,
|
|
80
|
+
model: config.claude.model || 'sonnet',
|
|
81
|
+
maxTurns: 50, // Limited turns since this is just analysis
|
|
82
|
+
permissionMode: 'bypassPermissions',
|
|
83
|
+
},
|
|
84
|
+
})) {
|
|
85
|
+
if (verbose) {
|
|
86
|
+
logInfo(`Received message type: ${message.type}`);
|
|
87
|
+
}
|
|
88
|
+
// Stream the code review process
|
|
89
|
+
if (message.type === 'assistant' && message.message?.content) {
|
|
90
|
+
for (const content of message.message.content) {
|
|
91
|
+
if (content.type === 'text') {
|
|
92
|
+
lastAssistantResponse += content.text + '\n';
|
|
93
|
+
if (verbose) {
|
|
94
|
+
console.log(`\n🔍 ${content.text}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else if (message.type === 'result') {
|
|
100
|
+
if (message.subtype === 'success') {
|
|
101
|
+
logInfo('\n🛠️ Code review analysis completed, parsing results...');
|
|
102
|
+
try {
|
|
103
|
+
const responseText = message.result || lastAssistantResponse;
|
|
104
|
+
let jsonResult = null;
|
|
105
|
+
const jsonBlockMatch = responseText.match(/```json\s*\n([\s\S]*?)\n\s*```/);
|
|
106
|
+
if (jsonBlockMatch) {
|
|
107
|
+
jsonResult = JSON.parse(jsonBlockMatch[1]);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
jsonResult = JSON.parse(responseText);
|
|
111
|
+
}
|
|
112
|
+
if (jsonResult && jsonResult.review_result) {
|
|
113
|
+
structuredReviewResult = jsonResult.review_result;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
throw new Error('Invalid JSON structure');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
logError(`Failed to parse structured review result: ${error}`);
|
|
121
|
+
structuredReviewResult = parseCodeReviewResponse(message.result || lastAssistantResponse);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
logError(`\n⚠️ Code review incomplete: ${message.subtype}`);
|
|
126
|
+
if (message.subtype === 'error_max_turns') {
|
|
127
|
+
logError('💡 Try simplifying the review scope');
|
|
128
|
+
}
|
|
129
|
+
if (lastAssistantResponse) {
|
|
130
|
+
try {
|
|
131
|
+
const responseText = lastAssistantResponse;
|
|
132
|
+
let jsonResult = null;
|
|
133
|
+
const jsonBlockMatch = responseText.match(/```json\s*\n([\s\S]*?)\n\s*```/);
|
|
134
|
+
if (jsonBlockMatch) {
|
|
135
|
+
jsonResult = JSON.parse(jsonBlockMatch[1]);
|
|
136
|
+
if (jsonResult && jsonResult.review_result) {
|
|
137
|
+
structuredReviewResult = jsonResult.review_result;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
structuredReviewResult = parseCodeReviewResponse(lastAssistantResponse);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
logError(`Failed to parse assistant response: ${error}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Create GitHub review with comments
|
|
152
|
+
if (structuredReviewResult) {
|
|
153
|
+
const { summary, comments, overall_assessment } = structuredReviewResult;
|
|
154
|
+
// Initialize Octokit
|
|
155
|
+
const octokit = new Octokit({ auth: githubToken });
|
|
156
|
+
if (!comments || comments.length === 0) {
|
|
157
|
+
if (verbose) {
|
|
158
|
+
logInfo('✅ No issues found. PR looks good!');
|
|
159
|
+
}
|
|
160
|
+
// Create an approval review if no issues found
|
|
161
|
+
const review = await octokit.pulls.createReview({
|
|
162
|
+
owner: context.owner,
|
|
163
|
+
repo: context.repo,
|
|
164
|
+
pull_number: context.pullRequestNumber,
|
|
165
|
+
event: 'APPROVE',
|
|
166
|
+
body: summary ||
|
|
167
|
+
overall_assessment ||
|
|
168
|
+
'Code review completed. No issues found.',
|
|
169
|
+
});
|
|
170
|
+
return {
|
|
171
|
+
featureId,
|
|
172
|
+
status: 'success',
|
|
173
|
+
message: 'Code review completed - no issues found',
|
|
174
|
+
reviewId: review.data.id,
|
|
175
|
+
reviewUrl: review.data.html_url,
|
|
176
|
+
commentsCount: 0,
|
|
177
|
+
summary: summary || 'No issues found',
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
if (verbose) {
|
|
181
|
+
logInfo(`Creating GitHub review with ${comments.length} comments...`);
|
|
182
|
+
}
|
|
183
|
+
// Create review with inline comments
|
|
184
|
+
const reviewComments = comments.map((comment) => ({
|
|
185
|
+
path: comment.file || comment.path,
|
|
186
|
+
line: comment.line,
|
|
187
|
+
body: comment.comment || comment.body,
|
|
188
|
+
}));
|
|
189
|
+
const review = await octokit.pulls.createReview({
|
|
190
|
+
owner: context.owner,
|
|
191
|
+
repo: context.repo,
|
|
192
|
+
pull_number: context.pullRequestNumber,
|
|
193
|
+
event: 'COMMENT',
|
|
194
|
+
body: summary ||
|
|
195
|
+
overall_assessment ||
|
|
196
|
+
'Please address the following review comments.',
|
|
197
|
+
comments: reviewComments,
|
|
198
|
+
});
|
|
199
|
+
if (verbose) {
|
|
200
|
+
logInfo(`✅ GitHub review created: ${review.data.html_url}`);
|
|
201
|
+
logInfo(`Review ID: ${review.data.id}`);
|
|
202
|
+
logInfo(`Comments posted: ${comments.length}`);
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
featureId,
|
|
206
|
+
status: 'success',
|
|
207
|
+
message: 'Code review completed and posted to GitHub',
|
|
208
|
+
reviewId: review.data.id,
|
|
209
|
+
reviewUrl: review.data.html_url,
|
|
210
|
+
commentsCount: comments.length,
|
|
211
|
+
summary: summary || 'Code review completed',
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
return {
|
|
216
|
+
featureId,
|
|
217
|
+
status: 'error',
|
|
218
|
+
message: 'Code review analysis failed or incomplete',
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
224
|
+
logError(`Code review failed: ${errorMessage}`);
|
|
225
|
+
return {
|
|
226
|
+
featureId,
|
|
227
|
+
status: 'error',
|
|
228
|
+
message: `Code review failed: ${errorMessage}`,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
function createSystemPrompt(_config) {
|
|
233
|
+
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.
|
|
234
|
+
|
|
235
|
+
**Your Role**: Review pull request code and provide detailed, actionable feedback.
|
|
236
|
+
|
|
237
|
+
**Review Focus Areas**:
|
|
238
|
+
1. **Code Quality**: Clean code principles, readability, maintainability
|
|
239
|
+
2. **Best Practices**: Language-specific conventions, design patterns
|
|
240
|
+
3. **Potential Bugs**: Logic errors, edge cases, error handling
|
|
241
|
+
4. **Security**: Security vulnerabilities, input validation
|
|
242
|
+
5. **Performance**: Performance issues, optimization opportunities
|
|
243
|
+
6. **Testing**: Test coverage, test quality
|
|
244
|
+
7. **Documentation**: Code comments, documentation completeness
|
|
245
|
+
8. **Architecture**: Design decisions, coupling, cohesion
|
|
246
|
+
|
|
247
|
+
**Important Guidelines**:
|
|
248
|
+
- Be thorough but focus on significant issues
|
|
249
|
+
- Provide constructive, actionable feedback
|
|
250
|
+
- Suggest specific improvements
|
|
251
|
+
- Consider the technical design and requirements
|
|
252
|
+
- Be respectful and professional
|
|
253
|
+
- Focus on issues that truly matter, not nitpicks
|
|
254
|
+
- If code looks good overall, provide positive feedback
|
|
255
|
+
|
|
256
|
+
**CRITICAL - Result Format**:
|
|
257
|
+
You MUST end your response with a JSON object containing the review results in this EXACT format:
|
|
258
|
+
|
|
259
|
+
\`\`\`json
|
|
260
|
+
{
|
|
261
|
+
"review_result": {
|
|
262
|
+
"feature_id": "FEATURE_ID_PLACEHOLDER",
|
|
263
|
+
"summary": "Overall review summary and assessment",
|
|
264
|
+
"overall_assessment": "APPROVE | REQUEST_CHANGES | COMMENT",
|
|
265
|
+
"comments": [
|
|
266
|
+
{
|
|
267
|
+
"file": "path/to/file.ts",
|
|
268
|
+
"line": 42,
|
|
269
|
+
"comment": "Detailed comment about the issue and suggested fix"
|
|
270
|
+
}
|
|
271
|
+
],
|
|
272
|
+
"issues_found": {
|
|
273
|
+
"critical": 0,
|
|
274
|
+
"major": 0,
|
|
275
|
+
"minor": 0
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
\`\`\`
|
|
280
|
+
|
|
281
|
+
**Comment Guidelines**:
|
|
282
|
+
- Each comment should reference a specific file and line number
|
|
283
|
+
- Be specific about what the issue is
|
|
284
|
+
- Provide a suggested fix or improvement
|
|
285
|
+
- Use a constructive, helpful tone
|
|
286
|
+
|
|
287
|
+
**Assessment Options**:
|
|
288
|
+
- **APPROVE**: Code looks good, no significant issues
|
|
289
|
+
- **REQUEST_CHANGES**: Issues found that should be addressed
|
|
290
|
+
- **COMMENT**: Minor suggestions or questions, not blocking
|
|
291
|
+
|
|
292
|
+
Focus on providing valuable, actionable code review feedback.`;
|
|
293
|
+
}
|
|
294
|
+
function createCodeReviewPrompt(featureId, context, feedbacksInfo) {
|
|
295
|
+
let contextInfo = formatContextForPrompt(context);
|
|
296
|
+
if (feedbacksInfo) {
|
|
297
|
+
contextInfo = contextInfo + '\n\n' + feedbacksInfo;
|
|
298
|
+
}
|
|
299
|
+
return `Review the pull request code for feature: ${featureId}
|
|
300
|
+
|
|
301
|
+
${contextInfo}
|
|
302
|
+
|
|
303
|
+
## Code Review Instructions
|
|
304
|
+
|
|
305
|
+
Follow this systematic approach:
|
|
306
|
+
|
|
307
|
+
1. **Understand the Context**: Review the feature description, technical design, user stories, and test cases to understand what the code should accomplish.
|
|
308
|
+
|
|
309
|
+
2. **Analyze Each File**: For each changed file:
|
|
310
|
+
- Review the code changes in the diff
|
|
311
|
+
- Look for code quality issues
|
|
312
|
+
- Check for potential bugs or logic errors
|
|
313
|
+
- Verify error handling
|
|
314
|
+
- Consider security implications
|
|
315
|
+
- Evaluate performance considerations
|
|
316
|
+
- Check test coverage
|
|
317
|
+
|
|
318
|
+
3. **Check Against Requirements**:
|
|
319
|
+
- Does the code implement the technical design correctly?
|
|
320
|
+
- Are all user stories addressed?
|
|
321
|
+
- Are test cases covered?
|
|
322
|
+
|
|
323
|
+
4. **Identify Issues**: Categorize issues by severity:
|
|
324
|
+
- **Critical**: Security vulnerabilities, data loss risks, major bugs
|
|
325
|
+
- **Major**: Logic errors, incorrect implementations, missing error handling
|
|
326
|
+
- **Minor**: Code quality improvements, style issues, optimization suggestions
|
|
327
|
+
|
|
328
|
+
5. **Provide Actionable Feedback**: For each issue:
|
|
329
|
+
- Reference the specific file and line number
|
|
330
|
+
- Explain what the issue is
|
|
331
|
+
- Suggest how to fix it
|
|
332
|
+
- Be constructive and helpful
|
|
333
|
+
|
|
334
|
+
6. **Generate Review Result**: Create a structured JSON response with:
|
|
335
|
+
- Overall summary
|
|
336
|
+
- Assessment (APPROVE/REQUEST_CHANGES/COMMENT)
|
|
337
|
+
- List of comments with file, line, and comment text
|
|
338
|
+
- Count of issues by severity
|
|
339
|
+
|
|
340
|
+
## Important Notes
|
|
341
|
+
- Be thorough but focus on meaningful issues
|
|
342
|
+
- Provide specific, actionable suggestions
|
|
343
|
+
- Consider the context and requirements
|
|
344
|
+
- Be professional and constructive
|
|
345
|
+
- If code is good, say so!
|
|
346
|
+
|
|
347
|
+
Begin by analyzing the changed files and identifying any issues or improvements.`;
|
|
348
|
+
}
|
|
349
|
+
function parseCodeReviewResponse(response) {
|
|
350
|
+
const summaryMatch = response.match(/## Review Summary\n([\s\S]*?)(?=\n##|\n\n|$)/);
|
|
351
|
+
const summary = summaryMatch
|
|
352
|
+
? summaryMatch[1].trim()
|
|
353
|
+
: 'Code review completed';
|
|
354
|
+
return {
|
|
355
|
+
summary,
|
|
356
|
+
comments: [],
|
|
357
|
+
overall_assessment: 'COMMENT',
|
|
358
|
+
issues_found: {
|
|
359
|
+
critical: 0,
|
|
360
|
+
major: 0,
|
|
361
|
+
minor: 0,
|
|
362
|
+
},
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
export function checkCodeReviewRequirements() {
|
|
366
|
+
// Dependencies are declared in package.json and will be installed automatically
|
|
367
|
+
// No need to check for them here
|
|
368
|
+
return Promise.resolve(true);
|
|
369
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context fetcher for code review phase
|
|
3
|
+
* Fetches GitHub PR data including files, diffs, and commits for review
|
|
4
|
+
*/
|
|
5
|
+
import { Octokit } from '@octokit/rest';
|
|
6
|
+
export interface PRFile {
|
|
7
|
+
filename: string;
|
|
8
|
+
status: string;
|
|
9
|
+
additions: number;
|
|
10
|
+
deletions: number;
|
|
11
|
+
changes: number;
|
|
12
|
+
patch?: string;
|
|
13
|
+
blob_url: string;
|
|
14
|
+
}
|
|
15
|
+
export interface PRCommit {
|
|
16
|
+
sha: string;
|
|
17
|
+
commit: {
|
|
18
|
+
message: string;
|
|
19
|
+
author: {
|
|
20
|
+
name: string;
|
|
21
|
+
date: string;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export interface PRData {
|
|
26
|
+
number: number;
|
|
27
|
+
title: string;
|
|
28
|
+
body: string | null;
|
|
29
|
+
state: string;
|
|
30
|
+
head: {
|
|
31
|
+
ref: string;
|
|
32
|
+
sha: string;
|
|
33
|
+
};
|
|
34
|
+
base: {
|
|
35
|
+
ref: string;
|
|
36
|
+
sha: string;
|
|
37
|
+
};
|
|
38
|
+
user: {
|
|
39
|
+
login: string;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export interface CodeReviewContext {
|
|
43
|
+
featureId: string;
|
|
44
|
+
featureName: string;
|
|
45
|
+
featureDescription: string | null;
|
|
46
|
+
pullRequestUrl: string;
|
|
47
|
+
pullRequestNumber: number;
|
|
48
|
+
owner: string;
|
|
49
|
+
repo: string;
|
|
50
|
+
prData: PRData;
|
|
51
|
+
files: PRFile[];
|
|
52
|
+
commits: PRCommit[];
|
|
53
|
+
technicalDesign?: string;
|
|
54
|
+
userStories?: any[];
|
|
55
|
+
testCases?: any[];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Extract owner, repo, and PR number from GitHub PR URL
|
|
59
|
+
*/
|
|
60
|
+
export declare function parsePullRequestUrl(pullRequestUrl: string): {
|
|
61
|
+
owner: string;
|
|
62
|
+
repo: string;
|
|
63
|
+
prNumber: number;
|
|
64
|
+
} | null;
|
|
65
|
+
/**
|
|
66
|
+
* Fetch PR details
|
|
67
|
+
*/
|
|
68
|
+
export declare function fetchPRDetails(octokit: Octokit, owner: string, repo: string, prNumber: number, verbose?: boolean): Promise<PRData>;
|
|
69
|
+
/**
|
|
70
|
+
* Fetch PR files (changed files with diffs)
|
|
71
|
+
*/
|
|
72
|
+
export declare function fetchPRFiles(octokit: Octokit, owner: string, repo: string, prNumber: number, verbose?: boolean): Promise<PRFile[]>;
|
|
73
|
+
/**
|
|
74
|
+
* Fetch PR commits
|
|
75
|
+
*/
|
|
76
|
+
export declare function fetchPRCommits(octokit: Octokit, owner: string, repo: string, prNumber: number, verbose?: boolean): Promise<PRCommit[]>;
|
|
77
|
+
/**
|
|
78
|
+
* Fetch user stories via MCP
|
|
79
|
+
*/
|
|
80
|
+
export declare function fetchUserStories(mcpServerUrl: string, mcpToken: string, featureId: string, verbose?: boolean): Promise<any[]>;
|
|
81
|
+
/**
|
|
82
|
+
* Fetch test cases via MCP
|
|
83
|
+
*/
|
|
84
|
+
export declare function fetchTestCases(mcpServerUrl: string, mcpToken: string, featureId: string, verbose?: boolean): Promise<any[]>;
|
|
85
|
+
/**
|
|
86
|
+
* Fetch complete code review context
|
|
87
|
+
*/
|
|
88
|
+
export declare function fetchCodeReviewContext(mcpServerUrl: string, mcpToken: string, featureId: string, githubToken: string, verbose?: boolean): Promise<CodeReviewContext>;
|
|
89
|
+
/**
|
|
90
|
+
* Format code review context for prompt
|
|
91
|
+
*/
|
|
92
|
+
export declare function formatContextForPrompt(context: CodeReviewContext): string;
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context fetcher for code review phase
|
|
3
|
+
* Fetches GitHub PR data including files, diffs, and commits for review
|
|
4
|
+
*/
|
|
5
|
+
import { Octokit } from '@octokit/rest';
|
|
6
|
+
import { getFeature } from '../../api/features/get-feature.js';
|
|
7
|
+
/**
|
|
8
|
+
* Extract owner, repo, and PR number from GitHub PR URL
|
|
9
|
+
*/
|
|
10
|
+
export function parsePullRequestUrl(pullRequestUrl) {
|
|
11
|
+
const match = pullRequestUrl.match(/github\.com\/([^\/]+)\/([^\/]+)\/pull\/(\d+)/);
|
|
12
|
+
if (!match) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
owner: match[1],
|
|
17
|
+
repo: match[2],
|
|
18
|
+
prNumber: parseInt(match[3], 10),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Fetch PR details
|
|
23
|
+
*/
|
|
24
|
+
export async function fetchPRDetails(octokit, owner, repo, prNumber, verbose) {
|
|
25
|
+
if (verbose) {
|
|
26
|
+
console.log(`📋 Fetching PR details for ${owner}/${repo}#${prNumber}...`);
|
|
27
|
+
}
|
|
28
|
+
const { data } = await octokit.pulls.get({
|
|
29
|
+
owner,
|
|
30
|
+
repo,
|
|
31
|
+
pull_number: prNumber,
|
|
32
|
+
});
|
|
33
|
+
if (verbose) {
|
|
34
|
+
console.log(`✅ Fetched PR: ${data.title}`);
|
|
35
|
+
}
|
|
36
|
+
return data;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Fetch PR files (changed files with diffs)
|
|
40
|
+
*/
|
|
41
|
+
export async function fetchPRFiles(octokit, owner, repo, prNumber, verbose) {
|
|
42
|
+
if (verbose) {
|
|
43
|
+
console.log(`📂 Fetching PR files for ${owner}/${repo}#${prNumber}...`);
|
|
44
|
+
}
|
|
45
|
+
const { data: files } = await octokit.pulls.listFiles({
|
|
46
|
+
owner,
|
|
47
|
+
repo,
|
|
48
|
+
pull_number: prNumber,
|
|
49
|
+
per_page: 100,
|
|
50
|
+
});
|
|
51
|
+
if (verbose) {
|
|
52
|
+
console.log(`✅ Found ${files.length} changed files`);
|
|
53
|
+
}
|
|
54
|
+
return files;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Fetch PR commits
|
|
58
|
+
*/
|
|
59
|
+
export async function fetchPRCommits(octokit, owner, repo, prNumber, verbose) {
|
|
60
|
+
if (verbose) {
|
|
61
|
+
console.log(`💾 Fetching PR commits for ${owner}/${repo}#${prNumber}...`);
|
|
62
|
+
}
|
|
63
|
+
const { data: commits } = await octokit.pulls.listCommits({
|
|
64
|
+
owner,
|
|
65
|
+
repo,
|
|
66
|
+
pull_number: prNumber,
|
|
67
|
+
per_page: 100,
|
|
68
|
+
});
|
|
69
|
+
if (verbose) {
|
|
70
|
+
console.log(`✅ Found ${commits.length} commits`);
|
|
71
|
+
}
|
|
72
|
+
return commits;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Fetch user stories via MCP
|
|
76
|
+
*/
|
|
77
|
+
export async function fetchUserStories(mcpServerUrl, mcpToken, featureId, verbose) {
|
|
78
|
+
try {
|
|
79
|
+
if (verbose) {
|
|
80
|
+
console.log(`📖 Fetching user stories for ${featureId}...`);
|
|
81
|
+
}
|
|
82
|
+
const response = await fetch(`${mcpServerUrl}/mcp`, {
|
|
83
|
+
method: 'POST',
|
|
84
|
+
headers: {
|
|
85
|
+
'Content-Type': 'application/json',
|
|
86
|
+
Authorization: `Bearer ${mcpToken}`,
|
|
87
|
+
},
|
|
88
|
+
body: JSON.stringify({
|
|
89
|
+
jsonrpc: '2.0',
|
|
90
|
+
id: 1,
|
|
91
|
+
method: 'user_stories/list',
|
|
92
|
+
params: {
|
|
93
|
+
feature_id: featureId,
|
|
94
|
+
},
|
|
95
|
+
}),
|
|
96
|
+
});
|
|
97
|
+
if (!response.ok) {
|
|
98
|
+
if (verbose) {
|
|
99
|
+
console.log(`⚠️ Could not fetch user stories: ${response.status}`);
|
|
100
|
+
}
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
const data = await response.json();
|
|
104
|
+
if (data.error || !data.result) {
|
|
105
|
+
if (verbose) {
|
|
106
|
+
console.log(`⚠️ User stories not available`);
|
|
107
|
+
}
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
return data.result.user_stories || [];
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
if (verbose) {
|
|
114
|
+
console.log(`⚠️ Error fetching user stories: ${error}`);
|
|
115
|
+
}
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Fetch test cases via MCP
|
|
121
|
+
*/
|
|
122
|
+
export async function fetchTestCases(mcpServerUrl, mcpToken, featureId, verbose) {
|
|
123
|
+
try {
|
|
124
|
+
if (verbose) {
|
|
125
|
+
console.log(`🧪 Fetching test cases for ${featureId}...`);
|
|
126
|
+
}
|
|
127
|
+
const response = await fetch(`${mcpServerUrl}/mcp`, {
|
|
128
|
+
method: 'POST',
|
|
129
|
+
headers: {
|
|
130
|
+
'Content-Type': 'application/json',
|
|
131
|
+
Authorization: `Bearer ${mcpToken}`,
|
|
132
|
+
},
|
|
133
|
+
body: JSON.stringify({
|
|
134
|
+
jsonrpc: '2.0',
|
|
135
|
+
id: 1,
|
|
136
|
+
method: 'test_cases/list',
|
|
137
|
+
params: {
|
|
138
|
+
feature_id: featureId,
|
|
139
|
+
},
|
|
140
|
+
}),
|
|
141
|
+
});
|
|
142
|
+
if (!response.ok) {
|
|
143
|
+
if (verbose) {
|
|
144
|
+
console.log(`⚠️ Could not fetch test cases: ${response.status}`);
|
|
145
|
+
}
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
const data = await response.json();
|
|
149
|
+
if (data.error || !data.result) {
|
|
150
|
+
if (verbose) {
|
|
151
|
+
console.log(`⚠️ Test cases not available`);
|
|
152
|
+
}
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
return data.result.test_cases || [];
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
if (verbose) {
|
|
159
|
+
console.log(`⚠️ Error fetching test cases: ${error}`);
|
|
160
|
+
}
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Fetch complete code review context
|
|
166
|
+
*/
|
|
167
|
+
export async function fetchCodeReviewContext(mcpServerUrl, mcpToken, featureId, githubToken, verbose) {
|
|
168
|
+
// Fetch feature info using shared API
|
|
169
|
+
const feature = await getFeature(mcpServerUrl, mcpToken, featureId, verbose);
|
|
170
|
+
if (!feature.pull_request_url) {
|
|
171
|
+
throw new Error(`Feature ${featureId} does not have a pull request URL. Cannot perform code review.`);
|
|
172
|
+
}
|
|
173
|
+
// Parse PR URL
|
|
174
|
+
const prInfo = parsePullRequestUrl(feature.pull_request_url);
|
|
175
|
+
if (!prInfo) {
|
|
176
|
+
throw new Error(`Invalid pull request URL: ${feature.pull_request_url}. Expected format: https://github.com/owner/repo/pull/123`);
|
|
177
|
+
}
|
|
178
|
+
const { owner, repo, prNumber } = prInfo;
|
|
179
|
+
// Initialize Octokit with GitHub token
|
|
180
|
+
const octokit = new Octokit({
|
|
181
|
+
auth: githubToken,
|
|
182
|
+
});
|
|
183
|
+
// Fetch PR data, files, commits, and additional context in parallel
|
|
184
|
+
const [prData, files, commits, userStories, testCases] = await Promise.all([
|
|
185
|
+
fetchPRDetails(octokit, owner, repo, prNumber, verbose),
|
|
186
|
+
fetchPRFiles(octokit, owner, repo, prNumber, verbose),
|
|
187
|
+
fetchPRCommits(octokit, owner, repo, prNumber, verbose),
|
|
188
|
+
fetchUserStories(mcpServerUrl, mcpToken, featureId, verbose),
|
|
189
|
+
fetchTestCases(mcpServerUrl, mcpToken, featureId, verbose),
|
|
190
|
+
]);
|
|
191
|
+
if (verbose) {
|
|
192
|
+
console.log(`📊 Summary: ${files.length} files changed across ${commits.length} commits`);
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
featureId,
|
|
196
|
+
featureName: feature.name,
|
|
197
|
+
featureDescription: feature.description ?? null,
|
|
198
|
+
pullRequestUrl: feature.pull_request_url,
|
|
199
|
+
pullRequestNumber: prNumber,
|
|
200
|
+
owner,
|
|
201
|
+
repo,
|
|
202
|
+
prData,
|
|
203
|
+
files,
|
|
204
|
+
commits,
|
|
205
|
+
technicalDesign: feature.technical_design,
|
|
206
|
+
userStories,
|
|
207
|
+
testCases,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Format code review context for prompt
|
|
212
|
+
*/
|
|
213
|
+
export function formatContextForPrompt(context) {
|
|
214
|
+
const sections = [];
|
|
215
|
+
// Feature information
|
|
216
|
+
sections.push(`# Feature Information`);
|
|
217
|
+
sections.push(`**Feature ID**: ${context.featureId}`);
|
|
218
|
+
sections.push(`**Feature Name**: ${context.featureName}`);
|
|
219
|
+
if (context.featureDescription) {
|
|
220
|
+
sections.push(`**Description**: ${context.featureDescription}`);
|
|
221
|
+
}
|
|
222
|
+
sections.push(`**Pull Request**: ${context.pullRequestUrl} (#${context.pullRequestNumber})`);
|
|
223
|
+
sections.push('');
|
|
224
|
+
// PR details
|
|
225
|
+
sections.push(`## Pull Request Details`);
|
|
226
|
+
sections.push(`**Title**: ${context.prData.title}`);
|
|
227
|
+
sections.push(`**Author**: @${context.prData.user.login}`);
|
|
228
|
+
sections.push(`**Base Branch**: ${context.prData.base.ref}`);
|
|
229
|
+
sections.push(`**Head Branch**: ${context.prData.head.ref}`);
|
|
230
|
+
if (context.prData.body) {
|
|
231
|
+
sections.push(`**Description**:`);
|
|
232
|
+
sections.push(context.prData.body);
|
|
233
|
+
}
|
|
234
|
+
sections.push('');
|
|
235
|
+
// Commits
|
|
236
|
+
if (context.commits.length > 0) {
|
|
237
|
+
sections.push(`## Commits (${context.commits.length})`);
|
|
238
|
+
sections.push('');
|
|
239
|
+
context.commits.forEach((commit) => {
|
|
240
|
+
sections.push(`- **${commit.sha.substring(0, 7)}**: ${commit.commit.message}`);
|
|
241
|
+
sections.push(` by ${commit.commit.author.name} on ${commit.commit.author.date}`);
|
|
242
|
+
});
|
|
243
|
+
sections.push('');
|
|
244
|
+
}
|
|
245
|
+
// Changed files
|
|
246
|
+
if (context.files.length > 0) {
|
|
247
|
+
sections.push(`## Changed Files (${context.files.length})`);
|
|
248
|
+
sections.push('');
|
|
249
|
+
context.files.forEach((file) => {
|
|
250
|
+
sections.push(`### ${file.filename}`);
|
|
251
|
+
sections.push(`**Status**: ${file.status}`);
|
|
252
|
+
sections.push(`**Changes**: +${file.additions} -${file.deletions}`);
|
|
253
|
+
if (file.patch) {
|
|
254
|
+
sections.push(`**Diff**:`);
|
|
255
|
+
sections.push('```diff');
|
|
256
|
+
sections.push(file.patch);
|
|
257
|
+
sections.push('```');
|
|
258
|
+
}
|
|
259
|
+
sections.push('');
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
// Technical design
|
|
263
|
+
if (context.technicalDesign) {
|
|
264
|
+
sections.push(`## Technical Design`);
|
|
265
|
+
sections.push('');
|
|
266
|
+
sections.push(context.technicalDesign);
|
|
267
|
+
sections.push('');
|
|
268
|
+
}
|
|
269
|
+
// User stories
|
|
270
|
+
if (context.userStories && context.userStories.length > 0) {
|
|
271
|
+
sections.push(`## User Stories`);
|
|
272
|
+
sections.push('');
|
|
273
|
+
context.userStories.forEach((story) => {
|
|
274
|
+
sections.push(`### ${story.title}`);
|
|
275
|
+
sections.push(story.description || '');
|
|
276
|
+
sections.push('');
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
// Test cases
|
|
280
|
+
if (context.testCases && context.testCases.length > 0) {
|
|
281
|
+
sections.push(`## Test Cases`);
|
|
282
|
+
sections.push('');
|
|
283
|
+
context.testCases.forEach((testCase) => {
|
|
284
|
+
sections.push(`### ${testCase.title}`);
|
|
285
|
+
if (testCase.description) {
|
|
286
|
+
sections.push(testCase.description);
|
|
287
|
+
}
|
|
288
|
+
sections.push('');
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
return sections.join('\n');
|
|
292
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Review Module
|
|
3
|
+
* Reviews pull request code and creates GitHub review comments
|
|
4
|
+
* Part of the automated development workflow
|
|
5
|
+
*/
|
|
6
|
+
export { reviewPullRequest, checkCodeReviewRequirements, type CodeReviewOptions, type CodeReviewResult, type ReviewComment, } from './analyzer.js';
|
|
7
|
+
export { fetchCodeReviewContext, formatContextForPrompt, parsePullRequestUrl, type CodeReviewContext, type PRFile, type PRCommit, type PRData, } from './context-fetcher.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Review Module
|
|
3
|
+
* Reviews pull request code and creates GitHub review comments
|
|
4
|
+
* Part of the automated development workflow
|
|
5
|
+
*/
|
|
6
|
+
export { reviewPullRequest, checkCodeReviewRequirements, } from './analyzer.js';
|
|
7
|
+
export { fetchCodeReviewContext, formatContextForPrompt, parsePullRequestUrl, } from './context-fetcher.js';
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
export interface LogPhaseEventParams {
|
|
6
6
|
featureId: string;
|
|
7
7
|
eventType: 'phase_started' | 'phase_completed' | 'phase_failed';
|
|
8
|
-
phase: 'feature_analysis' | 'technical_design' | 'code_implementation' | 'functional_testing' | 'code_refine' | 'code_refine_verification';
|
|
8
|
+
phase: 'feature_analysis' | 'technical_design' | 'code_implementation' | 'functional_testing' | 'code_refine' | 'code_refine_verification' | 'code_review';
|
|
9
9
|
result?: 'success' | 'error' | 'warning' | 'info';
|
|
10
10
|
metadata?: Record<string, any>;
|
|
11
11
|
errorMessage?: string;
|
|
@@ -19,7 +19,7 @@ export interface LogChecklistEventParams {
|
|
|
19
19
|
}
|
|
20
20
|
export interface LogVerificationEventParams {
|
|
21
21
|
featureId: string;
|
|
22
|
-
phase: 'feature_analysis' | 'technical_design' | 'code_implementation' | 'functional_testing' | 'code_refine' | 'code_refine_verification';
|
|
22
|
+
phase: 'feature_analysis' | 'technical_design' | 'code_implementation' | 'functional_testing' | 'code_refine' | 'code_refine_verification' | 'code_review';
|
|
23
23
|
iteration: number;
|
|
24
24
|
result: 'success' | 'error';
|
|
25
25
|
verificationData: {
|
package/dist/types/pipeline.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { EdsgerConfig } from './index.js';
|
|
5
5
|
import { ChecklistPhaseContext } from '../services/checklist.js';
|
|
6
|
-
export type ExecutionMode = 'full_pipeline' | 'only_feature_analysis' | 'only_technical_design' | 'only_code_implementation' | 'only_functional_testing' | 'only_code_refine' | 'from_feature_analysis' | 'from_technical_design' | 'from_code_implementation' | 'from_functional_testing';
|
|
6
|
+
export type ExecutionMode = 'full_pipeline' | 'only_feature_analysis' | 'only_technical_design' | 'only_code_implementation' | 'only_functional_testing' | 'only_code_refine' | 'only_code_review' | 'from_feature_analysis' | 'from_technical_design' | 'from_code_implementation' | 'from_functional_testing' | 'from_code_review';
|
|
7
7
|
export interface PipelinePhaseOptions {
|
|
8
8
|
readonly featureId: string;
|
|
9
9
|
readonly mcpServerUrl: string;
|
|
@@ -7,6 +7,7 @@ import { implementFeatureCode, checkCodeImplementationRequirements, } from '../.
|
|
|
7
7
|
import { runFunctionalTesting, checkFunctionalTestingRequirements, } from '../../phases/functional-testing/analyzer.js';
|
|
8
8
|
import { refineCodeFromPRFeedback, checkCodeRefineRequirements, } from '../../phases/code-refine/analyzer.js';
|
|
9
9
|
import { verifyAndResolveComments, checkCodeRefineVerificationRequirements, } from '../../phases/code-refine-verification/verifier.js';
|
|
10
|
+
import { reviewPullRequest, checkCodeReviewRequirements, } from '../../phases/code-review/analyzer.js';
|
|
10
11
|
/**
|
|
11
12
|
* Wrapper for code-refine phase to inject GitHub token
|
|
12
13
|
*/
|
|
@@ -46,6 +47,25 @@ const executeCodeRefineVerification = async (options, config) => {
|
|
|
46
47
|
verbose: options.verbose,
|
|
47
48
|
});
|
|
48
49
|
};
|
|
50
|
+
/**
|
|
51
|
+
* Wrapper for code-review phase to inject GitHub token
|
|
52
|
+
*/
|
|
53
|
+
const executeCodeReview = async (options, config) => {
|
|
54
|
+
const githubToken = process.env.GITHUB_TOKEN || process.env.GITHUB_ACCESS_TOKEN;
|
|
55
|
+
if (!githubToken) {
|
|
56
|
+
return {
|
|
57
|
+
status: 'error',
|
|
58
|
+
message: 'GitHub token not found. Set GITHUB_TOKEN or GITHUB_ACCESS_TOKEN environment variable.',
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return reviewPullRequest({
|
|
62
|
+
featureId: options.featureId,
|
|
63
|
+
mcpServerUrl: options.mcpServerUrl,
|
|
64
|
+
mcpToken: options.mcpToken,
|
|
65
|
+
githubToken,
|
|
66
|
+
verbose: options.verbose,
|
|
67
|
+
}, config);
|
|
68
|
+
};
|
|
49
69
|
// Pipeline phase configurations
|
|
50
70
|
export const phaseConfigs = [
|
|
51
71
|
{
|
|
@@ -84,4 +104,10 @@ export const phaseConfigs = [
|
|
|
84
104
|
execute: executeCodeRefineVerification,
|
|
85
105
|
requirementsError: 'Code refine verification requirements not met. Install with: npm install @octokit/rest',
|
|
86
106
|
},
|
|
107
|
+
{
|
|
108
|
+
name: 'code-review',
|
|
109
|
+
checkRequirements: checkCodeReviewRequirements,
|
|
110
|
+
execute: executeCodeReview,
|
|
111
|
+
requirementsError: 'Code review requirements not met. Install with: npm install @anthropic-ai/claude-code @octokit/rest',
|
|
112
|
+
},
|
|
87
113
|
];
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
import { EdsgerConfig } from '../../types/index.js';
|
|
5
5
|
import { PipelinePhaseOptions, PipelineResult, PhaseConfig } from '../../types/pipeline.js';
|
|
6
6
|
export declare const createPhaseRunner: (phaseConfig: PhaseConfig) => (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>;
|
|
7
|
-
declare const runFeatureAnalysisPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runTechnicalDesignPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeImplementationPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runFunctionalTestingPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeRefinePhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeRefineVerificationPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>;
|
|
8
|
-
export { runFeatureAnalysisPhase, runTechnicalDesignPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, };
|
|
7
|
+
declare const runFeatureAnalysisPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runTechnicalDesignPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeImplementationPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runFunctionalTestingPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeRefinePhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeRefineVerificationPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeReviewPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>;
|
|
8
|
+
export { runFeatureAnalysisPhase, runTechnicalDesignPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, runCodeReviewPhase, };
|
|
@@ -243,6 +243,6 @@ export const createPhaseRunner = (phaseConfig) => async (options, config) => {
|
|
|
243
243
|
}
|
|
244
244
|
};
|
|
245
245
|
// Create phase runners using the configuration
|
|
246
|
-
const [runFeatureAnalysisPhase, runTechnicalDesignPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase,] = phaseConfigs.map(createPhaseRunner);
|
|
246
|
+
const [runFeatureAnalysisPhase, runTechnicalDesignPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, runCodeReviewPhase,] = phaseConfigs.map(createPhaseRunner);
|
|
247
247
|
// Export individual phase runners for granular control
|
|
248
|
-
export { runFeatureAnalysisPhase, runTechnicalDesignPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, };
|
|
248
|
+
export { runFeatureAnalysisPhase, runTechnicalDesignPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, runCodeReviewPhase, };
|
|
@@ -83,10 +83,12 @@ export function getAvailableExecutionModes() {
|
|
|
83
83
|
'only_code_implementation',
|
|
84
84
|
'only_functional_testing',
|
|
85
85
|
'only_code_refine',
|
|
86
|
+
'only_code_review',
|
|
86
87
|
'from_feature_analysis',
|
|
87
88
|
'from_technical_design',
|
|
88
89
|
'from_code_implementation',
|
|
89
90
|
'from_functional_testing',
|
|
91
|
+
'from_code_review',
|
|
90
92
|
];
|
|
91
93
|
}
|
|
92
94
|
/**
|
|
@@ -106,10 +108,12 @@ export function getExecutionModeDescription(mode) {
|
|
|
106
108
|
only_code_implementation: 'Execute only: code implementation',
|
|
107
109
|
only_functional_testing: 'Execute only: functional testing',
|
|
108
110
|
only_code_refine: 'Execute only: code refine (address PR review feedback and verify resolution)',
|
|
111
|
+
only_code_review: 'Execute only: code review (review PR code and create review comments)',
|
|
109
112
|
from_feature_analysis: 'Execute from feature analysis to end: analysis → design → implementation → testing',
|
|
110
113
|
from_technical_design: 'Execute from technical design to end: design → implementation → testing',
|
|
111
114
|
from_code_implementation: 'Execute from code implementation to end: implementation → testing',
|
|
112
115
|
from_functional_testing: 'Execute from functional testing to end: testing',
|
|
116
|
+
from_code_review: 'Execute from code review to end: code-review → code-refine → code-refine-verification',
|
|
113
117
|
};
|
|
114
118
|
return descriptions[mode] || 'Unknown execution mode';
|
|
115
119
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Uses functional programming principles
|
|
5
5
|
*/
|
|
6
6
|
import { updateFeatureStatusForPhase } from '../api/features/index.js';
|
|
7
|
-
import { runFeatureAnalysisPhase, runTechnicalDesignPhase, runCodeImplementationPhase, runFunctionalTestingPhase, } from './executors/phase-executor.js';
|
|
7
|
+
import { runFeatureAnalysisPhase, runTechnicalDesignPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeReviewPhase, } from './executors/phase-executor.js';
|
|
8
8
|
import { logPipelineStart, logPhaseResult, logPipelineComplete, shouldContinuePipeline, } from '../utils/pipeline-logger.js';
|
|
9
9
|
import { handleTestFailuresWithRetry } from '../phases/functional-testing/test-retry-handler.js';
|
|
10
10
|
import { handleCodeRefineWithRetry } from '../phases/code-refine/retry-handler.js';
|
|
@@ -53,6 +53,11 @@ export const runPipelineByMode = async (options, config, executionMode) => {
|
|
|
53
53
|
// Code Refine
|
|
54
54
|
case 'only_code_refine':
|
|
55
55
|
return await runOnlyCodeRefine(options, config);
|
|
56
|
+
// Code Review
|
|
57
|
+
case 'only_code_review':
|
|
58
|
+
return await runOnlyCodeReview(options, config);
|
|
59
|
+
case 'from_code_review':
|
|
60
|
+
return await runFromCodeReview(options, config);
|
|
56
61
|
default:
|
|
57
62
|
throw new Error(`Unsupported execution mode: ${executionMode}`);
|
|
58
63
|
}
|
|
@@ -283,3 +288,37 @@ const runOnlyCodeRefine = async (options, config) => {
|
|
|
283
288
|
});
|
|
284
289
|
return logPipelineComplete(results, verbose);
|
|
285
290
|
};
|
|
291
|
+
/**
|
|
292
|
+
* Run only code review phase (review PR and create review comments)
|
|
293
|
+
*/
|
|
294
|
+
const runOnlyCodeReview = async (options, config) => {
|
|
295
|
+
const { featureId, verbose } = options;
|
|
296
|
+
logPipelineStart(featureId, verbose);
|
|
297
|
+
const results = [];
|
|
298
|
+
// Code Review - analyze PR and create review comments
|
|
299
|
+
const reviewResult = await runCodeReviewPhase(options, config);
|
|
300
|
+
results.push(logPhaseResult(reviewResult, verbose));
|
|
301
|
+
return logPipelineComplete(results, verbose);
|
|
302
|
+
};
|
|
303
|
+
/**
|
|
304
|
+
* Run from code review to end (code-review → code-refine → code-refine-verification)
|
|
305
|
+
*/
|
|
306
|
+
const runFromCodeReview = async (options, config) => {
|
|
307
|
+
const { featureId, verbose } = options;
|
|
308
|
+
logPipelineStart(featureId, verbose);
|
|
309
|
+
const results = [];
|
|
310
|
+
// 1. Code Review - analyze PR and create review comments
|
|
311
|
+
const reviewResult = await runCodeReviewPhase(options, config);
|
|
312
|
+
results.push(logPhaseResult(reviewResult, verbose));
|
|
313
|
+
if (!shouldContinuePipeline(results)) {
|
|
314
|
+
return logPipelineComplete(results, verbose);
|
|
315
|
+
}
|
|
316
|
+
// 2. Code Refine with automatic retry for verification failures
|
|
317
|
+
await handleCodeRefineWithRetry({
|
|
318
|
+
options,
|
|
319
|
+
config,
|
|
320
|
+
results,
|
|
321
|
+
verbose,
|
|
322
|
+
});
|
|
323
|
+
return logPipelineComplete(results, verbose);
|
|
324
|
+
};
|