edsger 0.9.0 → 0.9.2
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/api/features/approval-checker.d.ts +20 -0
- package/dist/api/features/approval-checker.js +163 -0
- package/dist/api/features/status-updater.js +1 -0
- package/dist/commands/workflow/executors/phase-executor.js +29 -0
- package/dist/commands/workflow/phase-orchestrator.js +36 -21
- package/dist/config/feature-status.js +1 -0
- package/dist/phases/code-refine/index.js +16 -1
- package/dist/services/feedbacks.d.ts +2 -0
- package/dist/services/feedbacks.js +9 -0
- package/dist/types/pipeline.d.ts +1 -1
- package/dist/utils/git-branch-manager.js +5 -1
- package/package.json +1 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Approval workflow integration for feature phases
|
|
3
|
+
* Checks if current feature status has been approved before executing next phase
|
|
4
|
+
*/
|
|
5
|
+
interface ApprovalCheckResult {
|
|
6
|
+
canProceed: boolean;
|
|
7
|
+
requiresApproval: boolean;
|
|
8
|
+
approvalId?: string;
|
|
9
|
+
message?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Check if current feature status has been approved before executing next phase
|
|
13
|
+
* This should be called BEFORE executing a phase, not after
|
|
14
|
+
*
|
|
15
|
+
* @param featureId - Feature ID
|
|
16
|
+
* @param verbose - Verbose logging
|
|
17
|
+
* @returns Promise<ApprovalCheckResult> - Whether the phase can proceed
|
|
18
|
+
*/
|
|
19
|
+
export declare function checkApprovalBeforePhaseExecution(featureId: string, verbose?: boolean): Promise<ApprovalCheckResult>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Approval workflow integration for feature phases
|
|
3
|
+
* Checks if current feature status has been approved before executing next phase
|
|
4
|
+
*/
|
|
5
|
+
import { logInfo, logError, logWarning } from '../../utils/logger.js';
|
|
6
|
+
import { callMcpEndpoint } from '../mcp-client.js';
|
|
7
|
+
import { getFeature } from './get-feature.js';
|
|
8
|
+
/**
|
|
9
|
+
* Check if current feature status has been approved before executing next phase
|
|
10
|
+
* This should be called BEFORE executing a phase, not after
|
|
11
|
+
*
|
|
12
|
+
* @param featureId - Feature ID
|
|
13
|
+
* @param verbose - Verbose logging
|
|
14
|
+
* @returns Promise<ApprovalCheckResult> - Whether the phase can proceed
|
|
15
|
+
*/
|
|
16
|
+
export async function checkApprovalBeforePhaseExecution(featureId, verbose = false) {
|
|
17
|
+
try {
|
|
18
|
+
// 1. Get current feature to check its status and product_id
|
|
19
|
+
const feature = await getFeature(featureId, verbose);
|
|
20
|
+
const currentStatus = feature.status;
|
|
21
|
+
const productId = feature.product_id;
|
|
22
|
+
if (verbose) {
|
|
23
|
+
logInfo(`🔍 Checking approval for current status: ${currentStatus}`);
|
|
24
|
+
}
|
|
25
|
+
// 2. Check if current status requires approval
|
|
26
|
+
const requiresApprovalResult = (await callMcpEndpoint('approvals/requires_approval', {
|
|
27
|
+
product_id: productId,
|
|
28
|
+
feature_status: currentStatus,
|
|
29
|
+
}));
|
|
30
|
+
const requiresApproval = requiresApprovalResult?.requires_approval === true;
|
|
31
|
+
if (!requiresApproval) {
|
|
32
|
+
if (verbose) {
|
|
33
|
+
logInfo(`✅ Current status ${currentStatus} does not require approval. Proceeding.`);
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
canProceed: true,
|
|
37
|
+
requiresApproval: false,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
// 3. Status requires approval - check if already approved
|
|
41
|
+
if (verbose) {
|
|
42
|
+
logInfo(`🔒 Current status ${currentStatus} requires approval`);
|
|
43
|
+
}
|
|
44
|
+
const existingApprovals = (await callMcpEndpoint('approvals/feature_approvals', {
|
|
45
|
+
feature_id: featureId,
|
|
46
|
+
}));
|
|
47
|
+
// Check if there's an approved approval for the current status
|
|
48
|
+
const approvedApproval = existingApprovals?.approvals?.find((approval) => approval.requested_status === currentStatus &&
|
|
49
|
+
approval.approval_status === 'approved');
|
|
50
|
+
if (approvedApproval) {
|
|
51
|
+
if (verbose) {
|
|
52
|
+
logInfo(`✅ Found approved approval for ${currentStatus}. Proceeding.`);
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
canProceed: true,
|
|
56
|
+
requiresApproval: true,
|
|
57
|
+
approvalId: approvedApproval.id,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// Check if there's already a pending approval for current status
|
|
61
|
+
const pendingApproval = existingApprovals?.approvals?.find((approval) => approval.requested_status === currentStatus &&
|
|
62
|
+
approval.approval_status === 'pending');
|
|
63
|
+
if (pendingApproval) {
|
|
64
|
+
if (verbose) {
|
|
65
|
+
logWarning(`⏳ Approval already pending for ${currentStatus}. Waiting for review.`);
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
canProceed: false,
|
|
69
|
+
requiresApproval: true,
|
|
70
|
+
approvalId: pendingApproval.id,
|
|
71
|
+
message: `Waiting for approval of current status: ${currentStatus}`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// No pending or approved approval - need to create one
|
|
75
|
+
if (verbose) {
|
|
76
|
+
logInfo(`🔔 No approval exists for ${currentStatus}. Creating approval request...`);
|
|
77
|
+
}
|
|
78
|
+
// Create approval request
|
|
79
|
+
const approvalId = await createApprovalRequestWithEmail(featureId, productId, currentStatus, verbose);
|
|
80
|
+
if (approvalId) {
|
|
81
|
+
return {
|
|
82
|
+
canProceed: false,
|
|
83
|
+
requiresApproval: true,
|
|
84
|
+
approvalId,
|
|
85
|
+
message: `Approval request created for status: ${currentStatus}`,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
logError('Failed to create approval request');
|
|
90
|
+
return {
|
|
91
|
+
canProceed: false,
|
|
92
|
+
requiresApproval: true,
|
|
93
|
+
message: `Failed to create approval request for status: ${currentStatus}`,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
99
|
+
logError(`Error checking approval before phase execution: ${errorMessage}`);
|
|
100
|
+
// On error, block execution to be safe
|
|
101
|
+
return {
|
|
102
|
+
canProceed: false,
|
|
103
|
+
requiresApproval: true,
|
|
104
|
+
message: `Error checking approval: ${errorMessage}`,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Create an approval request and send notification emails
|
|
110
|
+
* This is for the CURRENT status, not the next status
|
|
111
|
+
*/
|
|
112
|
+
async function createApprovalRequestWithEmail(featureId, productId, currentStatus, verbose = false) {
|
|
113
|
+
try {
|
|
114
|
+
if (verbose) {
|
|
115
|
+
logInfo(`Creating approval request for current status: ${currentStatus}`);
|
|
116
|
+
}
|
|
117
|
+
// Create approval request via MCP endpoint
|
|
118
|
+
// Note: We're requesting approval for the current status
|
|
119
|
+
// The approval is asking: "Can we proceed from this status?"
|
|
120
|
+
const result = (await callMcpEndpoint('approvals/create', {
|
|
121
|
+
feature_id: featureId,
|
|
122
|
+
requested_status: currentStatus,
|
|
123
|
+
previous_status: null, // We're approving current status, not transitioning
|
|
124
|
+
}));
|
|
125
|
+
const approvalId = result?.approval_id;
|
|
126
|
+
if (!approvalId) {
|
|
127
|
+
logError('Failed to create approval request: No approval_id returned');
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
if (verbose) {
|
|
131
|
+
logInfo(`✅ Approval request created: ${approvalId}`);
|
|
132
|
+
}
|
|
133
|
+
// Send approval emails via edge function
|
|
134
|
+
try {
|
|
135
|
+
if (verbose) {
|
|
136
|
+
logInfo('Sending approval notification emails...');
|
|
137
|
+
}
|
|
138
|
+
const emailResult = (await callMcpEndpoint('approvals/send_email', {
|
|
139
|
+
approval_id: approvalId,
|
|
140
|
+
}));
|
|
141
|
+
if (emailResult?.emails_sent) {
|
|
142
|
+
const sentCount = emailResult.emails_sent.filter((e) => e.status === 'sent').length;
|
|
143
|
+
if (verbose) {
|
|
144
|
+
logInfo(`✅ Sent approval emails to ${sentCount} assignee(s)`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (emailError) {
|
|
149
|
+
// Don't fail the whole process if email fails
|
|
150
|
+
const errorMessage = emailError instanceof Error ? emailError.message : String(emailError);
|
|
151
|
+
logWarning(`Failed to send approval emails: ${errorMessage}`);
|
|
152
|
+
if (verbose) {
|
|
153
|
+
logInfo('⚠️ Approval created but email notification failed');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return approvalId;
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
160
|
+
logError(`Failed to create approval request: ${errorMessage}`);
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -5,6 +5,7 @@ import { updateFeatureStatusForPhase } from '../../../api/features/index.js';
|
|
|
5
5
|
import { phaseConfigs } from '../config/phase-configs.js';
|
|
6
6
|
import { getChecklistsForPhase, validateChecklistsForPhase, validateRequiredChecklistResults, processChecklistResultsFromResponse, processChecklistItemResultsFromResponse, } from '../../../services/checklist.js';
|
|
7
7
|
import { logFeaturePhaseEvent } from '../../../services/audit-logs.js';
|
|
8
|
+
import { checkApprovalBeforePhaseExecution } from '../../../api/features/approval-checker.js';
|
|
8
9
|
// Higher-order function for phase execution
|
|
9
10
|
export const createPhaseRunner = (phaseConfig) => async (options, config) => {
|
|
10
11
|
const { featureId, verbose } = options;
|
|
@@ -12,6 +13,34 @@ export const createPhaseRunner = (phaseConfig) => async (options, config) => {
|
|
|
12
13
|
// Track phase duration for logging
|
|
13
14
|
const phaseStartTime = Date.now();
|
|
14
15
|
try {
|
|
16
|
+
// CHECK APPROVAL BEFORE EXECUTING PHASE
|
|
17
|
+
// This checks if the current feature status has been approved
|
|
18
|
+
if (verbose) {
|
|
19
|
+
console.log(`🔍 Checking approval before executing ${name} phase...`);
|
|
20
|
+
}
|
|
21
|
+
const approvalCheck = await checkApprovalBeforePhaseExecution(featureId, verbose);
|
|
22
|
+
if (!approvalCheck.canProceed) {
|
|
23
|
+
// Current status requires approval but hasn't been approved yet
|
|
24
|
+
// Block phase execution and return blocked result
|
|
25
|
+
if (verbose) {
|
|
26
|
+
console.log(`⛔ Phase ${name} blocked: ${approvalCheck.message || 'Waiting for approval'}`);
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
featureId,
|
|
30
|
+
phase: name,
|
|
31
|
+
status: 'blocked',
|
|
32
|
+
message: approvalCheck.message ||
|
|
33
|
+
'Phase execution blocked - waiting for approval of current feature status',
|
|
34
|
+
data: {
|
|
35
|
+
approval_required: true,
|
|
36
|
+
approval_id: approvalCheck.approvalId,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
// Approval check passed, proceed with phase execution
|
|
41
|
+
if (verbose && approvalCheck.requiresApproval) {
|
|
42
|
+
console.log(`✅ Approval verified for current status. Proceeding with ${name} phase.`);
|
|
43
|
+
}
|
|
15
44
|
// Update feature status to reflect current phase
|
|
16
45
|
await updateFeatureStatusForPhase(featureId, name, verbose);
|
|
17
46
|
if (verbose) {
|
|
@@ -88,6 +88,21 @@ export const runCompletePipeline = (options, config) => {
|
|
|
88
88
|
};
|
|
89
89
|
};
|
|
90
90
|
// Helper functions for different execution patterns
|
|
91
|
+
/**
|
|
92
|
+
* Finalize pipeline execution by updating feature status to ready_for_review if all phases succeeded
|
|
93
|
+
*/
|
|
94
|
+
const finalizePipelineExecution = async (options, results, verbose) => {
|
|
95
|
+
// Check if all phases succeeded
|
|
96
|
+
const allSucceeded = results.every((result) => result.status === 'success');
|
|
97
|
+
if (allSucceeded && results.length > 0) {
|
|
98
|
+
// Update status to ready_for_review
|
|
99
|
+
await updateFeatureStatusForPhase(options.featureId, 'ready-for-review', verbose);
|
|
100
|
+
if (verbose) {
|
|
101
|
+
logInfo('✅ Pipeline execution completed - feature is ready for review');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return logPipelineComplete(results, verbose);
|
|
105
|
+
};
|
|
91
106
|
/**
|
|
92
107
|
* Run only feature analysis phase
|
|
93
108
|
*/
|
|
@@ -97,7 +112,7 @@ const runOnlyFeatureAnalysis = async (options, config) => {
|
|
|
97
112
|
const results = [];
|
|
98
113
|
const analysisResult = await runFeatureAnalysisPhase(options, config);
|
|
99
114
|
results.push(logPhaseResult(analysisResult, verbose));
|
|
100
|
-
return
|
|
115
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
101
116
|
};
|
|
102
117
|
/**
|
|
103
118
|
* Run only technical design phase
|
|
@@ -108,7 +123,7 @@ const runOnlyTechnicalDesign = async (options, config) => {
|
|
|
108
123
|
const results = [];
|
|
109
124
|
const designResult = await runTechnicalDesignPhase(options, config);
|
|
110
125
|
results.push(logPhaseResult(designResult, verbose));
|
|
111
|
-
return
|
|
126
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
112
127
|
};
|
|
113
128
|
/**
|
|
114
129
|
* Run only code implementation phase
|
|
@@ -119,7 +134,7 @@ const runOnlyCodeImplementation = async (options, config) => {
|
|
|
119
134
|
const results = [];
|
|
120
135
|
const implementationResult = await runCodeImplementationPhase(options, config);
|
|
121
136
|
results.push(logPhaseResult(implementationResult, verbose));
|
|
122
|
-
return
|
|
137
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
123
138
|
};
|
|
124
139
|
/**
|
|
125
140
|
* Run only functional testing phase
|
|
@@ -130,7 +145,7 @@ const runOnlyFunctionalTesting = async (options, config) => {
|
|
|
130
145
|
const results = [];
|
|
131
146
|
const testingResult = await runFunctionalTestingPhase(options, config);
|
|
132
147
|
results.push(logPhaseResult(testingResult, verbose));
|
|
133
|
-
return
|
|
148
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
134
149
|
};
|
|
135
150
|
/**
|
|
136
151
|
* Run from feature analysis to end
|
|
@@ -143,19 +158,19 @@ const runFromFeatureAnalysis = async (options, config) => {
|
|
|
143
158
|
const analysisResult = await runFeatureAnalysisPhase(options, config);
|
|
144
159
|
results.push(logPhaseResult(analysisResult, verbose));
|
|
145
160
|
if (!shouldContinuePipeline(results)) {
|
|
146
|
-
return
|
|
161
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
147
162
|
}
|
|
148
163
|
// 2. Technical Design
|
|
149
164
|
const designResult = await runTechnicalDesignPhase(options, config);
|
|
150
165
|
results.push(logPhaseResult(designResult, verbose));
|
|
151
166
|
if (!shouldContinuePipeline(results)) {
|
|
152
|
-
return
|
|
167
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
153
168
|
}
|
|
154
169
|
// 3. Code Implementation
|
|
155
170
|
const implementationResult = await runCodeImplementationPhase(options, config);
|
|
156
171
|
results.push(logPhaseResult(implementationResult, verbose));
|
|
157
172
|
if (!shouldContinuePipeline(results)) {
|
|
158
|
-
return
|
|
173
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
159
174
|
}
|
|
160
175
|
// 4. Functional Testing with retry loop for bug fixes
|
|
161
176
|
const testingResult = await handleTestFailuresWithRetry({
|
|
@@ -185,7 +200,7 @@ const runFromFeatureAnalysis = async (options, config) => {
|
|
|
185
200
|
}
|
|
186
201
|
}
|
|
187
202
|
}
|
|
188
|
-
return
|
|
203
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
189
204
|
};
|
|
190
205
|
/**
|
|
191
206
|
* Run from technical design to end
|
|
@@ -198,13 +213,13 @@ const runFromTechnicalDesign = async (options, config) => {
|
|
|
198
213
|
const designResult = await runTechnicalDesignPhase(options, config);
|
|
199
214
|
results.push(logPhaseResult(designResult, verbose));
|
|
200
215
|
if (!shouldContinuePipeline(results)) {
|
|
201
|
-
return
|
|
216
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
202
217
|
}
|
|
203
218
|
// 2. Code Implementation
|
|
204
219
|
const implementationResult = await runCodeImplementationPhase(options, config);
|
|
205
220
|
results.push(logPhaseResult(implementationResult, verbose));
|
|
206
221
|
if (!shouldContinuePipeline(results)) {
|
|
207
|
-
return
|
|
222
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
208
223
|
}
|
|
209
224
|
// 3. Functional Testing with retry loop for bug fixes
|
|
210
225
|
const testingResult = await handleTestFailuresWithRetry({
|
|
@@ -234,7 +249,7 @@ const runFromTechnicalDesign = async (options, config) => {
|
|
|
234
249
|
}
|
|
235
250
|
}
|
|
236
251
|
}
|
|
237
|
-
return
|
|
252
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
238
253
|
};
|
|
239
254
|
/**
|
|
240
255
|
* Run from code implementation to end
|
|
@@ -247,7 +262,7 @@ const runFromCodeImplementation = async (options, config) => {
|
|
|
247
262
|
const implementationResult = await runCodeImplementationPhase(options, config);
|
|
248
263
|
results.push(logPhaseResult(implementationResult, verbose));
|
|
249
264
|
if (!shouldContinuePipeline(results)) {
|
|
250
|
-
return
|
|
265
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
251
266
|
}
|
|
252
267
|
// 2. Functional Testing with retry loop for bug fixes
|
|
253
268
|
const testingResult = await handleTestFailuresWithRetry({
|
|
@@ -277,7 +292,7 @@ const runFromCodeImplementation = async (options, config) => {
|
|
|
277
292
|
}
|
|
278
293
|
}
|
|
279
294
|
}
|
|
280
|
-
return
|
|
295
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
281
296
|
};
|
|
282
297
|
/**
|
|
283
298
|
* Run from functional testing to end
|
|
@@ -314,7 +329,7 @@ const runFromFunctionalTesting = async (options, config) => {
|
|
|
314
329
|
}
|
|
315
330
|
}
|
|
316
331
|
}
|
|
317
|
-
return
|
|
332
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
318
333
|
};
|
|
319
334
|
/**
|
|
320
335
|
* Run only pull request creation phase
|
|
@@ -349,7 +364,7 @@ const runOnlyPullRequest = async (options, config) => {
|
|
|
349
364
|
data: {},
|
|
350
365
|
};
|
|
351
366
|
results.push(logPhaseResult(prResult, verbose));
|
|
352
|
-
return
|
|
367
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
353
368
|
};
|
|
354
369
|
/**
|
|
355
370
|
* Run only code refine phase (refine code based on PR feedback)
|
|
@@ -366,7 +381,7 @@ const runOnlyCodeRefine = async (options, config) => {
|
|
|
366
381
|
results,
|
|
367
382
|
verbose,
|
|
368
383
|
});
|
|
369
|
-
return
|
|
384
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
370
385
|
};
|
|
371
386
|
/**
|
|
372
387
|
* Run only code review phase (review PR and create review comments)
|
|
@@ -378,7 +393,7 @@ const runOnlyCodeReview = async (options, config) => {
|
|
|
378
393
|
// Code Review - analyze PR and create review comments
|
|
379
394
|
const reviewResult = await runCodeReviewPhase(options, config);
|
|
380
395
|
results.push(logPhaseResult(reviewResult, verbose));
|
|
381
|
-
return
|
|
396
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
382
397
|
};
|
|
383
398
|
/**
|
|
384
399
|
* Run from code review to end (code-review → code-refine → code-refine-verification)
|
|
@@ -391,7 +406,7 @@ const runFromCodeReview = async (options, config) => {
|
|
|
391
406
|
const reviewResult = await runCodeReviewPhase(options, config);
|
|
392
407
|
results.push(logPhaseResult(reviewResult, verbose));
|
|
393
408
|
if (!shouldContinuePipeline(results)) {
|
|
394
|
-
return
|
|
409
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
395
410
|
}
|
|
396
411
|
// 2. Code Refine with automatic retry for verification failures
|
|
397
412
|
await handleCodeRefineWithRetry({
|
|
@@ -400,7 +415,7 @@ const runFromCodeReview = async (options, config) => {
|
|
|
400
415
|
results,
|
|
401
416
|
verbose,
|
|
402
417
|
});
|
|
403
|
-
return
|
|
418
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
404
419
|
};
|
|
405
420
|
/**
|
|
406
421
|
* Run from pull request creation to end (pull-request → code-review → code-refine → code-refine-verification)
|
|
@@ -429,11 +444,11 @@ const runFromPullRequest = async (options, config) => {
|
|
|
429
444
|
if (verbose) {
|
|
430
445
|
logInfo('⚠️ Pull request creation failed, stopping workflow');
|
|
431
446
|
}
|
|
432
|
-
return
|
|
447
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
433
448
|
}
|
|
434
449
|
// 2. Continue with code review and refine workflow
|
|
435
450
|
await continueWithCodeReviewAndRefine(options, config, results, verbose);
|
|
436
|
-
return
|
|
451
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
437
452
|
};
|
|
438
453
|
/**
|
|
439
454
|
* Continue with code review and refine after PR creation
|
|
@@ -36,7 +36,22 @@ const pushChanges = (verbose) => {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
catch (error) {
|
|
39
|
-
|
|
39
|
+
// If push fails due to non-fast-forward, use force-with-lease for safer force push
|
|
40
|
+
// force-with-lease ensures we don't overwrite others' work by checking remote state
|
|
41
|
+
if (verbose) {
|
|
42
|
+
logInfo(`⚠️ Push rejected, attempting force push with lease...`);
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
execSync('git push --force-with-lease origin $(git branch --show-current)', {
|
|
46
|
+
encoding: 'utf-8',
|
|
47
|
+
});
|
|
48
|
+
if (verbose) {
|
|
49
|
+
logInfo(`✅ Successfully force pushed changes`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (forceError) {
|
|
53
|
+
throw new Error(`Failed to push changes: ${forceError instanceof Error ? forceError.message : String(forceError)}`);
|
|
54
|
+
}
|
|
40
55
|
}
|
|
41
56
|
};
|
|
42
57
|
/**
|
|
@@ -141,6 +141,15 @@ function formatFeedbacksList(feedbacks) {
|
|
|
141
141
|
const priorityBadge = getPriorityBadge(feedback.priority);
|
|
142
142
|
// Build context metadata section
|
|
143
143
|
let contextInfo = '';
|
|
144
|
+
// Item references (User Story or Test Case)
|
|
145
|
+
if (feedback.user_story_id) {
|
|
146
|
+
contextInfo += `\n**User Story ID**: ${feedback.user_story_id}`;
|
|
147
|
+
contextInfo += `\n*Note: This feedback applies to the user story referenced above in the context. Look for the ID in the "Existing User Stories" section.*`;
|
|
148
|
+
}
|
|
149
|
+
if (feedback.test_case_id) {
|
|
150
|
+
contextInfo += `\n**Test Case ID**: ${feedback.test_case_id}`;
|
|
151
|
+
contextInfo += `\n*Note: This feedback applies to the test case referenced above in the context. Look for the ID in the "Existing Test Cases" section.*`;
|
|
152
|
+
}
|
|
144
153
|
// Document type
|
|
145
154
|
if (feedback.document_type) {
|
|
146
155
|
const docTypeDisplay = feedback.document_type
|
package/dist/types/pipeline.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export interface PipelinePhaseOptions {
|
|
|
11
11
|
export interface PipelineResult {
|
|
12
12
|
readonly featureId: string;
|
|
13
13
|
readonly phase: string;
|
|
14
|
-
readonly status: 'success' | 'error';
|
|
14
|
+
readonly status: 'success' | 'error' | 'blocked';
|
|
15
15
|
readonly message: string;
|
|
16
16
|
readonly data?: unknown;
|
|
17
17
|
}
|
|
@@ -238,8 +238,12 @@ export function returnToMainBranch(baseBranch = 'main', verbose) {
|
|
|
238
238
|
if (verbose) {
|
|
239
239
|
logError(`⚠️ Warning: Uncommitted changes detected before returning to ${baseBranch}:`);
|
|
240
240
|
uncommittedFiles.forEach((file) => logError(` ${file}`));
|
|
241
|
-
logError(`
|
|
241
|
+
logError(` Resetting uncommitted changes to allow branch switch.`);
|
|
242
242
|
}
|
|
243
|
+
// Reset uncommitted changes to allow clean branch switch
|
|
244
|
+
// The code should already be committed on the feature branch,
|
|
245
|
+
// so these changes are likely from post-commit operations
|
|
246
|
+
resetUncommittedChanges(verbose);
|
|
243
247
|
}
|
|
244
248
|
switchToBranch(baseBranch, verbose);
|
|
245
249
|
if (verbose) {
|