edsger 0.2.3 ā 0.2.4
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/README.md +17 -0
- package/dist/api/features/batch-operations.d.ts +16 -0
- package/dist/api/features/batch-operations.js +100 -0
- package/dist/api/features/index.d.ts +1 -0
- package/dist/api/features/index.js +1 -0
- package/dist/api/features/test-cases.d.ts +8 -0
- package/dist/api/features/test-cases.js +45 -0
- package/dist/api/features/user-stories.d.ts +8 -0
- package/dist/api/features/user-stories.js +45 -0
- package/dist/cli/commands/refactor-command.d.ts +2 -0
- package/dist/cli/commands/refactor-command.js +107 -0
- package/dist/cli/index.js +7 -0
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +4 -99
- package/dist/phases/code-implementation/analyzer-helpers.d.ts +28 -0
- package/dist/phases/code-implementation/analyzer-helpers.js +177 -0
- package/dist/phases/code-implementation/analyzer.d.ts +2 -0
- package/dist/phases/code-implementation/analyzer.js +304 -175
- package/dist/phases/code-implementation-verification/index.d.ts +1 -0
- package/dist/phases/code-implementation-verification/index.js +1 -0
- package/dist/phases/code-implementation-verification/verifier.d.ts +31 -0
- package/dist/phases/code-implementation-verification/verifier.js +196 -0
- package/dist/phases/feature-analysis/analyzer-helpers.d.ts +62 -0
- package/dist/phases/feature-analysis/analyzer-helpers.js +450 -0
- package/dist/phases/feature-analysis/analyzer.d.ts +1 -0
- package/dist/phases/feature-analysis/analyzer.js +132 -219
- package/dist/phases/feature-analysis-verification/index.d.ts +1 -0
- package/dist/phases/feature-analysis-verification/index.js +1 -0
- package/dist/phases/feature-analysis-verification/verifier.d.ts +37 -0
- package/dist/phases/feature-analysis-verification/verifier.js +147 -0
- package/dist/phases/pull-request/creator.js +2 -1
- package/dist/phases/technical-design/analyzer-helpers.d.ts +37 -0
- package/dist/phases/technical-design/analyzer-helpers.js +144 -0
- package/dist/phases/technical-design/analyzer.d.ts +3 -0
- package/dist/phases/technical-design/analyzer.js +282 -318
- package/dist/phases/technical-design-verification/index.d.ts +1 -0
- package/dist/phases/technical-design-verification/index.js +1 -0
- package/dist/phases/technical-design-verification/verifier.d.ts +36 -0
- package/dist/phases/technical-design-verification/verifier.js +147 -0
- package/dist/prompts/checklist-verification.d.ts +11 -0
- package/dist/prompts/checklist-verification.js +153 -0
- package/dist/prompts/code-implementation-improvement.d.ts +5 -0
- package/dist/prompts/code-implementation-improvement.js +108 -0
- package/dist/prompts/code-implementation-verification.d.ts +3 -0
- package/dist/prompts/code-implementation-verification.js +176 -0
- package/dist/prompts/feature-analysis-improvement.d.ts +8 -0
- package/dist/prompts/feature-analysis-improvement.js +109 -0
- package/dist/prompts/feature-analysis.js +1 -1
- package/dist/prompts/technical-design-improvement.d.ts +5 -0
- package/dist/prompts/technical-design-improvement.js +93 -0
- package/dist/prompts/technical-design-verification.d.ts +11 -0
- package/dist/prompts/technical-design-verification.js +134 -0
- package/dist/prompts/technical-design.js +1 -1
- package/dist/services/audit-logs.d.ts +60 -0
- package/dist/services/audit-logs.js +115 -0
- package/dist/services/checklist.d.ts +1 -0
- package/dist/types/index.d.ts +19 -0
- package/dist/workflow-runner/executors/phase-executor.js +56 -12
- package/package.json +1 -1
- package/dist/api/features.d.ts +0 -100
- package/dist/api/features.js +0 -219
- package/dist/logger.d.ts +0 -19
- package/dist/logger.js +0 -52
- package/dist/types.d.ts +0 -99
- package/dist/types.js +0 -1
- package/dist/utils/image-processor.d.ts +0 -5
- package/dist/utils/image-processor.js +0 -55
- package/dist/workflow-runner/config/stage-configs.d.ts +0 -5
- package/dist/workflow-runner/config/stage-configs.js +0 -34
- package/dist/workflow-runner/core/feature-filter.test.d.ts +0 -4
- package/dist/workflow-runner/core/feature-filter.test.js +0 -127
- package/dist/workflow-runner/executors/stage-executor.d.ts +0 -8
- package/dist/workflow-runner/executors/stage-executor.js +0 -49
- package/dist/workflow-runner/feature-fetcher.d.ts +0 -41
- package/dist/workflow-runner/feature-fetcher.js +0 -121
- package/dist/workflow-runner/feature-service.d.ts +0 -17
- package/dist/workflow-runner/feature-service.js +0 -60
- package/dist/workflow-runner/pipeline.d.ts +0 -18
- package/dist/workflow-runner/pipeline.js +0 -197
- package/dist/workflow-runner/processor.d.ts +0 -40
- package/dist/workflow-runner/processor.js +0 -191
- package/dist/workflow-runner/status-updater.d.ts +0 -27
- package/dist/workflow-runner/status-updater.js +0 -80
- package/dist/workflow-runner/types.d.ts +0 -48
- package/dist/workflow-runner/types.js +0 -4
|
@@ -1,22 +1,10 @@
|
|
|
1
|
-
import { query } from '@anthropic-ai/claude-code';
|
|
2
1
|
import { logInfo, logError } from '../../utils/logger.js';
|
|
3
|
-
import { saveDataViaHttp } from './http-fallback.js';
|
|
4
2
|
import { fetchFeatureAnalysisContext, } from './context-fetcher.js';
|
|
5
|
-
import { createUserStories } from '../../api/features/index.js';
|
|
6
|
-
import { createTestCases } from '../../api/features/index.js';
|
|
7
3
|
import { formatFeatureAnalysisContext } from '../../prompts/formatters.js';
|
|
8
4
|
import { createFeatureAnalysisSystemPrompt, createFeatureAnalysisPromptWithContext, } from '../../prompts/feature-analysis.js';
|
|
9
5
|
import { formatChecklistsForContext, } from '../../services/checklist.js';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
type: 'user',
|
|
13
|
-
message: { role: 'user', content: content },
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
async function* prompt(analysisPrompt) {
|
|
17
|
-
yield userMessage(analysisPrompt);
|
|
18
|
-
await new Promise((res) => setTimeout(res, 10000));
|
|
19
|
-
}
|
|
6
|
+
import { executeAnalysisQuery, performVerificationCycle, saveAnalysisArtifactsAsDraft, updateArtifactsToReady, deleteArtifacts, buildAnalysisResult, buildVerificationFailureResult, buildNoResultsError, } from './analyzer-helpers.js';
|
|
7
|
+
import { logFeaturePhaseEvent } from '../../services/audit-logs.js';
|
|
20
8
|
export const analyzeFeatureWithMCP = async (options, config, checklistContext) => {
|
|
21
9
|
const { featureId, mcpServerUrl, mcpToken, verbose } = options;
|
|
22
10
|
if (verbose) {
|
|
@@ -24,230 +12,128 @@ export const analyzeFeatureWithMCP = async (options, config, checklistContext) =
|
|
|
24
12
|
logInfo(`Using MCP server: ${mcpServerUrl}`);
|
|
25
13
|
}
|
|
26
14
|
try {
|
|
27
|
-
// Fetch
|
|
28
|
-
|
|
29
|
-
logInfo('Fetching feature analysis context via MCP endpoints...');
|
|
30
|
-
}
|
|
31
|
-
const context = await fetchFeatureAnalysisContext(mcpServerUrl, mcpToken, featureId, verbose);
|
|
15
|
+
// Fetch and prepare context
|
|
16
|
+
const context = await prepareAnalysisContext(mcpServerUrl, mcpToken, featureId, checklistContext, verbose);
|
|
32
17
|
const systemPrompt = createFeatureAnalysisSystemPrompt(config, mcpServerUrl, mcpToken, featureId);
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
logInfo(` - ${img.url} -> ${img.localPath}`);
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
// Add checklist context to the analysis prompt
|
|
41
|
-
let finalContextInfo = contextInfo;
|
|
42
|
-
if (checklistContext && checklistContext.checklists.length > 0) {
|
|
43
|
-
const checklistInfo = formatChecklistsForContext(checklistContext);
|
|
44
|
-
finalContextInfo = contextInfo + '\n\n' + checklistInfo;
|
|
45
|
-
if (verbose) {
|
|
46
|
-
logInfo(`Added ${checklistContext.checklists.length} checklists to analysis context`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
const analysisPrompt = createFeatureAnalysisPromptWithContext(featureId, finalContextInfo);
|
|
50
|
-
let lastAssistantResponse = '';
|
|
18
|
+
const initialAnalysisPrompt = context.analysisPrompt;
|
|
19
|
+
const maxIterations = options.maxVerificationIterations || 10;
|
|
20
|
+
let currentIteration = 0;
|
|
21
|
+
let currentPrompt = initialAnalysisPrompt;
|
|
51
22
|
let structuredAnalysisResult = null;
|
|
23
|
+
let verificationResult = null;
|
|
52
24
|
if (verbose) {
|
|
53
25
|
logInfo('Starting Claude Code query with pre-fetched information...');
|
|
54
26
|
}
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
permissionMode: 'bypassPermissions',
|
|
63
|
-
},
|
|
64
|
-
})) {
|
|
65
|
-
if (verbose) {
|
|
66
|
-
logInfo(`Received message type: ${message.type}`);
|
|
27
|
+
// Iterative improvement loop: analysis ā save draft ā verification ā update ready or delete ā re-analysis
|
|
28
|
+
let currentDraftUserStoryIds = [];
|
|
29
|
+
let currentDraftTestCaseIds = [];
|
|
30
|
+
while (currentIteration < maxIterations) {
|
|
31
|
+
currentIteration++;
|
|
32
|
+
if (verbose && currentIteration > 1) {
|
|
33
|
+
logInfo(`\nš Iteration ${currentIteration}/${maxIterations}: Improving analysis based on verification feedback...`);
|
|
67
34
|
}
|
|
68
|
-
//
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
35
|
+
// Log iteration start (for iterations after the first)
|
|
36
|
+
if (currentIteration > 1) {
|
|
37
|
+
await logFeaturePhaseEvent(mcpServerUrl, mcpToken, {
|
|
38
|
+
featureId,
|
|
39
|
+
eventType: 'phase_started',
|
|
40
|
+
phase: 'feature_analysis',
|
|
41
|
+
result: 'info',
|
|
42
|
+
metadata: {
|
|
43
|
+
iteration: currentIteration,
|
|
44
|
+
max_iterations: maxIterations,
|
|
45
|
+
re_analysis: true,
|
|
46
|
+
timestamp: new Date().toISOString(),
|
|
47
|
+
},
|
|
48
|
+
}, verbose);
|
|
83
49
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const responseText = message.result || lastAssistantResponse;
|
|
90
|
-
let jsonResult = null;
|
|
91
|
-
// First try to extract JSON from markdown code block
|
|
92
|
-
const jsonBlockMatch = responseText.match(/```json\s*\n([\s\S]*?)\n\s*```/);
|
|
93
|
-
if (jsonBlockMatch) {
|
|
94
|
-
jsonResult = JSON.parse(jsonBlockMatch[1]);
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
// Try to parse the entire response as JSON
|
|
98
|
-
jsonResult = JSON.parse(responseText);
|
|
99
|
-
}
|
|
100
|
-
if (jsonResult && jsonResult.analysis) {
|
|
101
|
-
structuredAnalysisResult = jsonResult.analysis;
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
throw new Error('Invalid JSON structure');
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
catch (error) {
|
|
108
|
-
logError(`Failed to parse structured analysis result: ${error}`);
|
|
109
|
-
structuredAnalysisResult = {
|
|
110
|
-
status: 'error',
|
|
111
|
-
summary: 'Failed to parse analysis results from Claude Code response',
|
|
112
|
-
created_user_stories: [],
|
|
113
|
-
created_test_cases: [],
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
logError(`\nā ļø Analysis incomplete: ${message.subtype}`);
|
|
119
|
-
if (message.subtype === 'error_max_turns') {
|
|
120
|
-
logError('š” Try increasing timeout or reducing complexity');
|
|
121
|
-
}
|
|
122
|
-
// Try to parse results from the last assistant response
|
|
123
|
-
if (lastAssistantResponse) {
|
|
124
|
-
try {
|
|
125
|
-
const responseText = lastAssistantResponse;
|
|
126
|
-
let jsonResult = null;
|
|
127
|
-
const jsonBlockMatch = responseText.match(/```json\s*\n([\s\S]*?)\n\s*```/);
|
|
128
|
-
if (jsonBlockMatch) {
|
|
129
|
-
jsonResult = JSON.parse(jsonBlockMatch[1]);
|
|
130
|
-
if (jsonResult && jsonResult.analysis) {
|
|
131
|
-
structuredAnalysisResult = jsonResult.analysis;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
catch (error) {
|
|
136
|
-
logError(`Failed to parse assistant response: ${error}`);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
50
|
+
// Execute analysis query
|
|
51
|
+
structuredAnalysisResult = await executeAnalysisQuery(currentPrompt, systemPrompt, config, verbose);
|
|
52
|
+
// No result produced, break out
|
|
53
|
+
if (!structuredAnalysisResult) {
|
|
54
|
+
break;
|
|
140
55
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
56
|
+
// Log analysis completion for this iteration
|
|
57
|
+
await logFeaturePhaseEvent(mcpServerUrl, mcpToken, {
|
|
58
|
+
featureId,
|
|
59
|
+
eventType: 'phase_completed',
|
|
60
|
+
phase: 'feature_analysis',
|
|
61
|
+
result: 'success',
|
|
62
|
+
metadata: {
|
|
63
|
+
iteration: currentIteration,
|
|
64
|
+
max_iterations: maxIterations,
|
|
65
|
+
analysis_step: 'completed',
|
|
66
|
+
user_stories_count: structuredAnalysisResult.created_user_stories?.length || 0,
|
|
67
|
+
test_cases_count: structuredAnalysisResult.created_test_cases?.length || 0,
|
|
68
|
+
timestamp: new Date().toISOString(),
|
|
69
|
+
},
|
|
70
|
+
}, verbose);
|
|
71
|
+
// Save artifacts as draft and get their IDs
|
|
72
|
+
const { userStoryIds, testCaseIds } = await saveAnalysisArtifactsAsDraft(mcpServerUrl, mcpToken, featureId, structuredAnalysisResult.created_user_stories || [], structuredAnalysisResult.created_test_cases || [], verbose);
|
|
73
|
+
currentDraftUserStoryIds = userStoryIds;
|
|
74
|
+
currentDraftTestCaseIds = testCaseIds;
|
|
75
|
+
// Perform verification cycle
|
|
76
|
+
const verificationCycle = await performVerificationCycle(structuredAnalysisResult, checklistContext || null, context.featureContext, config, currentIteration, maxIterations, mcpServerUrl, mcpToken, featureId, verbose);
|
|
77
|
+
verificationResult = verificationCycle.verificationResult;
|
|
78
|
+
// If verification passed, update artifacts to ready and exit
|
|
79
|
+
if (verificationCycle.passed) {
|
|
146
80
|
if (verbose) {
|
|
147
|
-
logInfo('
|
|
148
|
-
}
|
|
149
|
-
const userStoriesCreated = await createUserStories(mcpServerUrl, mcpToken, featureId, created_user_stories, verbose);
|
|
150
|
-
if (!userStoriesCreated && verbose) {
|
|
151
|
-
logError('ā ļø Failed to save user stories');
|
|
81
|
+
logInfo('ā
Verification passed! Updating artifacts to ready status...');
|
|
152
82
|
}
|
|
83
|
+
await updateArtifactsToReady(mcpServerUrl, mcpToken, currentDraftUserStoryIds, currentDraftTestCaseIds, verbose);
|
|
84
|
+
break;
|
|
153
85
|
}
|
|
154
|
-
|
|
86
|
+
// Verification failed
|
|
87
|
+
if (currentIteration < maxIterations && verificationCycle.nextPrompt) {
|
|
88
|
+
// We have more iterations - delete draft artifacts and retry
|
|
155
89
|
if (verbose) {
|
|
156
|
-
logInfo('
|
|
157
|
-
}
|
|
158
|
-
const testCasesCreated = await createTestCases(mcpServerUrl, mcpToken, featureId, created_test_cases, verbose);
|
|
159
|
-
if (!testCasesCreated && verbose) {
|
|
160
|
-
logError('ā ļø Failed to save test cases');
|
|
90
|
+
logInfo('šļø Deleting draft artifacts for re-analysis...');
|
|
161
91
|
}
|
|
92
|
+
await deleteArtifacts(mcpServerUrl, mcpToken, currentDraftUserStoryIds, currentDraftTestCaseIds, verbose);
|
|
93
|
+
// Continue with improvement prompt
|
|
94
|
+
currentPrompt = verificationCycle.nextPrompt;
|
|
162
95
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
96
|
+
else {
|
|
97
|
+
// Max iterations reached or no next prompt - exit loop
|
|
98
|
+
// Draft artifacts remain in database for manual review
|
|
166
99
|
if (verbose) {
|
|
167
|
-
logInfo('
|
|
168
|
-
}
|
|
169
|
-
const httpSuccess = await saveDataViaHttp({
|
|
170
|
-
mcpServerUrl,
|
|
171
|
-
mcpToken,
|
|
172
|
-
featureId,
|
|
173
|
-
userStories: created_user_stories || [],
|
|
174
|
-
testCases: created_test_cases || [],
|
|
175
|
-
verbose,
|
|
176
|
-
});
|
|
177
|
-
if (httpSuccess) {
|
|
178
|
-
logInfo('ā
Successfully saved data via HTTP fallback');
|
|
179
|
-
structuredAnalysisResult.status = 'success';
|
|
180
|
-
structuredAnalysisResult.summary =
|
|
181
|
-
(structuredAnalysisResult.summary || '') +
|
|
182
|
-
' (saved via HTTP fallback)';
|
|
100
|
+
logInfo('ā ļø Max iterations reached. Draft artifacts kept for manual review.');
|
|
183
101
|
}
|
|
184
|
-
|
|
185
|
-
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Handle results
|
|
106
|
+
if (!structuredAnalysisResult) {
|
|
107
|
+
return buildNoResultsError(featureId, context.featureContext);
|
|
108
|
+
}
|
|
109
|
+
const { created_user_stories, created_test_cases, status, checklist_results, checklist_item_results, } = structuredAnalysisResult;
|
|
110
|
+
// If no checklist was used, update draft artifacts to ready now
|
|
111
|
+
if (!checklistContext ||
|
|
112
|
+
checklistContext.checklists.length === 0 ||
|
|
113
|
+
!verificationResult) {
|
|
114
|
+
if (currentDraftUserStoryIds.length > 0 ||
|
|
115
|
+
currentDraftTestCaseIds.length > 0) {
|
|
116
|
+
if (verbose) {
|
|
117
|
+
logInfo('ā
No checklist verification needed. Updating artifacts to ready status...');
|
|
186
118
|
}
|
|
119
|
+
await updateArtifactsToReady(mcpServerUrl, mcpToken, currentDraftUserStoryIds, currentDraftTestCaseIds, verbose);
|
|
187
120
|
}
|
|
188
|
-
return {
|
|
189
|
-
featureId,
|
|
190
|
-
productInfo: context.product,
|
|
191
|
-
featureInfo: context.feature,
|
|
192
|
-
existingUserStories: context.existing_user_stories.map((story) => ({
|
|
193
|
-
...story,
|
|
194
|
-
status: story.status,
|
|
195
|
-
created_at: story.created_at || new Date().toISOString(),
|
|
196
|
-
updated_at: story.updated_at || new Date().toISOString(),
|
|
197
|
-
})),
|
|
198
|
-
existingTestCases: context.existing_test_cases.map((testCase) => ({
|
|
199
|
-
...testCase,
|
|
200
|
-
created_at: testCase.created_at || new Date().toISOString(),
|
|
201
|
-
updated_at: testCase.updated_at || new Date().toISOString(),
|
|
202
|
-
})),
|
|
203
|
-
createdUserStories: (created_user_stories || []).map((story) => ({
|
|
204
|
-
id: '',
|
|
205
|
-
title: story.title,
|
|
206
|
-
description: story.description,
|
|
207
|
-
status: story.status || 'draft',
|
|
208
|
-
created_at: new Date().toISOString(),
|
|
209
|
-
updated_at: new Date().toISOString(),
|
|
210
|
-
})),
|
|
211
|
-
createdTestCases: (created_test_cases || []).map((testCase) => ({
|
|
212
|
-
id: '',
|
|
213
|
-
name: testCase.name,
|
|
214
|
-
description: testCase.description,
|
|
215
|
-
is_critical: testCase.is_critical || false,
|
|
216
|
-
created_at: new Date().toISOString(),
|
|
217
|
-
updated_at: new Date().toISOString(),
|
|
218
|
-
})),
|
|
219
|
-
summary: structuredAnalysisResult.summary || 'Analysis completed',
|
|
220
|
-
status: structuredAnalysisResult.status === 'success' ? 'success' : 'error',
|
|
221
|
-
data: {
|
|
222
|
-
checklist_results,
|
|
223
|
-
checklist_item_results,
|
|
224
|
-
},
|
|
225
|
-
};
|
|
226
121
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
createdUserStories: [],
|
|
243
|
-
createdTestCases: [],
|
|
244
|
-
summary: 'No analysis results received',
|
|
245
|
-
status: 'error',
|
|
246
|
-
data: {
|
|
247
|
-
checklist_results: undefined,
|
|
248
|
-
checklist_item_results: undefined,
|
|
249
|
-
},
|
|
250
|
-
};
|
|
122
|
+
// Check if verification failed after all iterations
|
|
123
|
+
// Note: Artifacts are already saved as draft in the database
|
|
124
|
+
// If verification failed, they remain as draft for manual review
|
|
125
|
+
if (verificationResult &&
|
|
126
|
+
verificationResult.rejected_count > 0 &&
|
|
127
|
+
checklistContext &&
|
|
128
|
+
checklistContext.checklists.length > 0) {
|
|
129
|
+
logError(`ā Final result: Checklist verification FAILED after ${currentIteration} iterations`);
|
|
130
|
+
logError(` Draft artifacts (${currentDraftUserStoryIds.length} user stories, ${currentDraftTestCaseIds.length} test cases) kept for manual review`);
|
|
131
|
+
return buildVerificationFailureResult(featureId, context.featureContext, verificationResult, checklist_results, checklist_item_results, currentIteration);
|
|
132
|
+
}
|
|
133
|
+
// Return success result
|
|
134
|
+
// Note: Artifacts have already been saved and updated to 'ready' status (if verification passed)
|
|
135
|
+
// or remain as draft (if verification failed)
|
|
136
|
+
return buildAnalysisResult(featureId, context.featureContext, structuredAnalysisResult, currentIteration);
|
|
251
137
|
}
|
|
252
138
|
catch (error) {
|
|
253
139
|
logError(`Feature analysis failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -264,6 +150,33 @@ export const analyzeFeatureWithMCP = async (options, config, checklistContext) =
|
|
|
264
150
|
};
|
|
265
151
|
}
|
|
266
152
|
};
|
|
153
|
+
/**
|
|
154
|
+
* Prepare all context information needed for analysis
|
|
155
|
+
*/
|
|
156
|
+
async function prepareAnalysisContext(mcpServerUrl, mcpToken, featureId, checklistContext, verbose) {
|
|
157
|
+
if (verbose) {
|
|
158
|
+
logInfo('Fetching feature analysis context via MCP endpoints...');
|
|
159
|
+
}
|
|
160
|
+
const featureContext = await fetchFeatureAnalysisContext(mcpServerUrl, mcpToken, featureId, verbose);
|
|
161
|
+
const { content: contextInfo, downloadedImages } = await formatFeatureAnalysisContext(featureContext);
|
|
162
|
+
if (verbose && downloadedImages.length > 0) {
|
|
163
|
+
logInfo(`Downloaded ${downloadedImages.length} images for Claude Code:`);
|
|
164
|
+
downloadedImages.forEach((img) => {
|
|
165
|
+
logInfo(` - ${img.url} -> ${img.localPath}`);
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
// Add checklist context to the analysis prompt
|
|
169
|
+
let finalContextInfo = contextInfo;
|
|
170
|
+
if (checklistContext && checklistContext.checklists.length > 0) {
|
|
171
|
+
const checklistInfo = formatChecklistsForContext(checklistContext);
|
|
172
|
+
finalContextInfo = contextInfo + '\n\n' + checklistInfo;
|
|
173
|
+
if (verbose) {
|
|
174
|
+
logInfo(`Added ${checklistContext.checklists.length} checklists to analysis context`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const analysisPrompt = createFeatureAnalysisPromptWithContext(featureId, finalContextInfo);
|
|
178
|
+
return { featureContext, analysisPrompt };
|
|
179
|
+
}
|
|
267
180
|
export const checkFeatureAnalysisRequirements = async () => {
|
|
268
181
|
try {
|
|
269
182
|
// Check if Claude Code SDK is available
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { verifyChecklistCompliance, type ChecklistItemVerificationResult, type ChecklistVerificationResult, type VerifyChecklistOptions, } from './verifier.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { verifyChecklistCompliance, } from './verifier.js';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checklist verification agent for feature analysis
|
|
3
|
+
* This agent independently reviews checklist compliance to ensure objectivity
|
|
4
|
+
*/
|
|
5
|
+
import { EdsgerConfig } from '../../types/index.js';
|
|
6
|
+
import { ChecklistPhaseContext } from '../../services/checklist.js';
|
|
7
|
+
import { FeatureAnalysisContext } from '../feature-analysis/context-fetcher.js';
|
|
8
|
+
export interface ChecklistItemVerificationResult {
|
|
9
|
+
checklist_item_id: string;
|
|
10
|
+
is_satisfied: boolean;
|
|
11
|
+
verification_status: 'confirmed' | 'rejected' | 'uncertain';
|
|
12
|
+
verification_reason: string;
|
|
13
|
+
concerns?: string[];
|
|
14
|
+
improvement_suggestions?: string[];
|
|
15
|
+
}
|
|
16
|
+
export interface ChecklistVerificationResult {
|
|
17
|
+
all_verified: boolean;
|
|
18
|
+
total_items: number;
|
|
19
|
+
confirmed_count: number;
|
|
20
|
+
rejected_count: number;
|
|
21
|
+
uncertain_count: number;
|
|
22
|
+
item_verifications: ChecklistItemVerificationResult[];
|
|
23
|
+
summary: string;
|
|
24
|
+
overall_suggestions?: string[];
|
|
25
|
+
}
|
|
26
|
+
export interface VerifyChecklistOptions {
|
|
27
|
+
checklistContext: ChecklistPhaseContext;
|
|
28
|
+
analysisContext: FeatureAnalysisContext;
|
|
29
|
+
createdUserStories: any[];
|
|
30
|
+
createdTestCases: any[];
|
|
31
|
+
verbose?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Verify checklist compliance using an independent AI agent
|
|
35
|
+
* This agent acts as a "challenger" to validate the claims made by the analysis agent
|
|
36
|
+
*/
|
|
37
|
+
export declare function verifyChecklistCompliance(options: VerifyChecklistOptions, config: EdsgerConfig): Promise<ChecklistVerificationResult>;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checklist verification agent for feature analysis
|
|
3
|
+
* This agent independently reviews checklist compliance to ensure objectivity
|
|
4
|
+
*/
|
|
5
|
+
import { query } from '@anthropic-ai/claude-code';
|
|
6
|
+
import { logInfo, logError } from '../../utils/logger.js';
|
|
7
|
+
import { createChecklistVerificationPrompt, createChecklistVerificationSystemPrompt, } from '../../prompts/checklist-verification.js';
|
|
8
|
+
function userMessage(content) {
|
|
9
|
+
return {
|
|
10
|
+
type: 'user',
|
|
11
|
+
message: { role: 'user', content: content },
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
async function* prompt(verificationPrompt) {
|
|
15
|
+
yield userMessage(verificationPrompt);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Verify checklist compliance using an independent AI agent
|
|
19
|
+
* This agent acts as a "challenger" to validate the claims made by the analysis agent
|
|
20
|
+
*/
|
|
21
|
+
export async function verifyChecklistCompliance(options, config) {
|
|
22
|
+
const { checklistContext, analysisContext, createdUserStories, createdTestCases, verbose, } = options;
|
|
23
|
+
const totalChecklistItems = checklistContext.checklists.reduce((sum, checklist) => sum + checklist.items.length, 0);
|
|
24
|
+
if (verbose) {
|
|
25
|
+
logInfo('š Starting checklist verification...');
|
|
26
|
+
logInfo(` Verifying ${totalChecklistItems} checklist items`);
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
const systemPrompt = createChecklistVerificationSystemPrompt(config);
|
|
30
|
+
const verificationPrompt = createChecklistVerificationPrompt({
|
|
31
|
+
checklistContext,
|
|
32
|
+
analysisContext,
|
|
33
|
+
createdUserStories,
|
|
34
|
+
createdTestCases,
|
|
35
|
+
});
|
|
36
|
+
let lastAssistantResponse = '';
|
|
37
|
+
let verificationResult = null;
|
|
38
|
+
if (verbose) {
|
|
39
|
+
logInfo('š¤ Starting verification agent query...');
|
|
40
|
+
}
|
|
41
|
+
// Use Claude Code SDK for verification
|
|
42
|
+
for await (const message of query({
|
|
43
|
+
prompt: prompt(verificationPrompt),
|
|
44
|
+
options: {
|
|
45
|
+
appendSystemPrompt: systemPrompt,
|
|
46
|
+
model: config.claude.model || 'sonnet',
|
|
47
|
+
maxTurns: 100,
|
|
48
|
+
permissionMode: 'bypassPermissions',
|
|
49
|
+
},
|
|
50
|
+
})) {
|
|
51
|
+
if (verbose) {
|
|
52
|
+
logInfo(` Received message type: ${message.type}`);
|
|
53
|
+
}
|
|
54
|
+
// Capture assistant responses
|
|
55
|
+
if (message.type === 'assistant' && message.message?.content) {
|
|
56
|
+
for (const content of message.message.content) {
|
|
57
|
+
if (content.type === 'text') {
|
|
58
|
+
lastAssistantResponse += content.text + '\n';
|
|
59
|
+
if (verbose) {
|
|
60
|
+
console.log(`\nš ${content.text}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (message.type === 'result') {
|
|
66
|
+
if (message.subtype === 'success') {
|
|
67
|
+
logInfo('\nā
Verification completed, parsing results...');
|
|
68
|
+
try {
|
|
69
|
+
const responseText = message.result || lastAssistantResponse;
|
|
70
|
+
// Try to extract JSON from markdown code block
|
|
71
|
+
const jsonBlockMatch = responseText.match(/```json\s*\n([\s\S]*?)\n\s*```/);
|
|
72
|
+
let jsonResult = null;
|
|
73
|
+
if (jsonBlockMatch) {
|
|
74
|
+
jsonResult = JSON.parse(jsonBlockMatch[1]);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
jsonResult = JSON.parse(responseText);
|
|
78
|
+
}
|
|
79
|
+
if (jsonResult && jsonResult.verification) {
|
|
80
|
+
verificationResult = jsonResult.verification;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
throw new Error('Invalid verification JSON structure');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
logError(`Failed to parse verification result: ${error}`);
|
|
88
|
+
// Return default "uncertain" result
|
|
89
|
+
verificationResult = createUncertainVerificationResult(checklistContext, 'Failed to parse verification response');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
logError(`\nā ļø Verification incomplete: ${message.subtype}`);
|
|
94
|
+
verificationResult = createUncertainVerificationResult(checklistContext, `Verification incomplete: ${message.subtype}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (!verificationResult) {
|
|
99
|
+
verificationResult = createUncertainVerificationResult(checklistContext, 'No verification result received');
|
|
100
|
+
}
|
|
101
|
+
if (verbose) {
|
|
102
|
+
logInfo('\nš Verification Summary:');
|
|
103
|
+
logInfo(` Total items: ${verificationResult.total_items}`);
|
|
104
|
+
logInfo(` ā
Confirmed: ${verificationResult.confirmed_count}`);
|
|
105
|
+
logInfo(` ā Rejected: ${verificationResult.rejected_count}`);
|
|
106
|
+
logInfo(` ā ļø Uncertain: ${verificationResult.uncertain_count}`);
|
|
107
|
+
logInfo(` Summary: ${verificationResult.summary}`);
|
|
108
|
+
if (verificationResult.rejected_count > 0) {
|
|
109
|
+
logInfo('\nā Rejected items:');
|
|
110
|
+
verificationResult.item_verifications
|
|
111
|
+
.filter((v) => v.verification_status === 'rejected')
|
|
112
|
+
.forEach((v) => {
|
|
113
|
+
logInfo(` - ${v.checklist_item_id}`);
|
|
114
|
+
logInfo(` Reason: ${v.verification_reason}`);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return verificationResult;
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
logError(`Checklist verification failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
122
|
+
return createUncertainVerificationResult(checklistContext, `Verification error: ${error instanceof Error ? error.message : String(error)}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Create a default "uncertain" verification result when verification fails
|
|
127
|
+
*/
|
|
128
|
+
function createUncertainVerificationResult(checklistContext, reason) {
|
|
129
|
+
// Get all checklist items
|
|
130
|
+
const allItems = checklistContext.checklists.flatMap((checklist) => checklist.items.map((item) => ({
|
|
131
|
+
checklist_item_id: item.id,
|
|
132
|
+
})));
|
|
133
|
+
return {
|
|
134
|
+
all_verified: false,
|
|
135
|
+
total_items: allItems.length,
|
|
136
|
+
confirmed_count: 0,
|
|
137
|
+
rejected_count: 0,
|
|
138
|
+
uncertain_count: allItems.length,
|
|
139
|
+
item_verifications: allItems.map((item) => ({
|
|
140
|
+
checklist_item_id: item.checklist_item_id,
|
|
141
|
+
is_satisfied: false,
|
|
142
|
+
verification_status: 'uncertain',
|
|
143
|
+
verification_reason: reason,
|
|
144
|
+
})),
|
|
145
|
+
summary: `Verification could not be completed: ${reason}`,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
@@ -55,7 +55,8 @@ const switchToMainBranch = (mainBranch = 'main', verbose) => {
|
|
|
55
55
|
const generatePRTitle = (featureName) => {
|
|
56
56
|
// Remove feature ID prefix if present (e.g., "FEAT-123: Feature Name" -> "Feature Name")
|
|
57
57
|
const cleanName = featureName.replace(/^[A-Z]+-\d+:\s*/, '');
|
|
58
|
-
|
|
58
|
+
// Convert to lowercase for conventional commits format
|
|
59
|
+
return `feat: ${cleanName.toLowerCase()}`;
|
|
59
60
|
};
|
|
60
61
|
/**
|
|
61
62
|
* Generate pull request body with feature details
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { EdsgerConfig } from '../../types/index.js';
|
|
2
|
+
import { ChecklistPhaseContext } from '../../services/checklist.js';
|
|
3
|
+
import { type ChecklistVerificationResult } from '../technical-design-verification/index.js';
|
|
4
|
+
export interface TechnicalDesignResult {
|
|
5
|
+
featureId: string;
|
|
6
|
+
technicalDesign: string | null;
|
|
7
|
+
status: 'success' | 'error';
|
|
8
|
+
summary: string;
|
|
9
|
+
verificationResult?: ChecklistVerificationResult;
|
|
10
|
+
iterations?: number;
|
|
11
|
+
data?: {
|
|
12
|
+
checklist_item_results?: any[];
|
|
13
|
+
[key: string]: any;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
interface VerificationCycleResult {
|
|
17
|
+
passed: boolean;
|
|
18
|
+
verificationResult: ChecklistVerificationResult | null;
|
|
19
|
+
nextPrompt?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Perform verification and determine if iteration should continue
|
|
23
|
+
*/
|
|
24
|
+
export declare function performVerificationCycle(technicalDesign: string, checklistContext: ChecklistPhaseContext | null, featureId: string, featureName: string, featureDescription: string | undefined, config: EdsgerConfig, currentIteration: number, maxIterations: number, mcpServerUrl: string, mcpToken: string, verbose?: boolean): Promise<VerificationCycleResult>;
|
|
25
|
+
/**
|
|
26
|
+
* Build successful design result
|
|
27
|
+
*/
|
|
28
|
+
export declare function buildDesignResult(featureId: string, technicalDesign: string, summary: string, iterations: number, checklistItemResults?: any[]): TechnicalDesignResult;
|
|
29
|
+
/**
|
|
30
|
+
* Build verification failure result
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildVerificationFailureResult(featureId: string, technicalDesign: string, verificationResult: ChecklistVerificationResult, iterations: number): TechnicalDesignResult;
|
|
33
|
+
/**
|
|
34
|
+
* Build error result when no design was generated
|
|
35
|
+
*/
|
|
36
|
+
export declare function buildNoResultsError(featureId: string): TechnicalDesignResult;
|
|
37
|
+
export {};
|