edsger 0.2.4 → 0.2.6

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.
@@ -5,9 +5,9 @@ export const runRefactor = async (options) => {
5
5
  // Load and validate configuration
6
6
  const config = validateConfiguration(options);
7
7
  if (options.verbose) {
8
- logInfo('Starting code refactoring for current directory...');
8
+ logInfo('Starting automatic code refactoring for current directory...');
9
9
  }
10
- logInfo('Analyzing code for refactoring opportunities...');
10
+ logInfo('Analyzing and refactoring code...');
11
11
  try {
12
12
  const refactorPrompt = createRefactorPrompt(config);
13
13
  const systemPrompt = createSystemPrompt();
@@ -17,6 +17,7 @@ export const runRefactor = async (options) => {
17
17
  options: {
18
18
  appendSystemPrompt: systemPrompt,
19
19
  model: config.claude.model || 'sonnet',
20
+ permissionMode: 'bypassPermissions',
20
21
  },
21
22
  })) {
22
23
  // Stream the refactoring process
@@ -33,7 +34,7 @@ export const runRefactor = async (options) => {
33
34
  if (message.type === 'result') {
34
35
  hasCompleted = true;
35
36
  if (message.subtype === 'success') {
36
- logSuccess('Refactoring analysis completed successfully');
37
+ logSuccess('Code refactoring completed successfully');
37
38
  process.exit(0);
38
39
  }
39
40
  else {
@@ -56,52 +57,67 @@ export const runRefactor = async (options) => {
56
57
  }
57
58
  };
58
59
  const createSystemPrompt = () => {
59
- return `You are an expert code refactoring assistant using Claude Code. Your task is to analyze code in the current directory and suggest or perform refactoring improvements.
60
+ return `You are an expert code refactoring assistant using Claude Code. Your task is to analyze and AUTOMATICALLY REFACTOR code in the current directory to improve quality and maintainability.
60
61
 
61
62
  IMPORTANT INSTRUCTIONS:
62
63
  1. Use the Bash tool to explore the project structure (ls, find, etc.)
63
64
  2. Use the Read tool to examine file contents
64
65
  3. Use the Grep tool to search for patterns, code smells, or duplications
65
- 4. Use the Edit tool to apply refactoring changes when appropriate
66
- 5. Analyze code quality, identify technical debt, and suggest improvements
66
+ 4. Use the Edit tool to DIRECTLY apply refactoring changes
67
+ 5. You have FULL PERMISSION to make refactoring changes without asking for confirmation
67
68
 
68
- REFACTORING FOCUS AREAS:
69
- - Code duplication and DRY violations
70
- - Complex functions that need simplification
71
- - Poor naming conventions
72
- - Missing type annotations (TypeScript)
73
- - Inefficient algorithms or data structures
74
- - Code organization and structure
75
- - Missing or inadequate error handling
76
- - Opportunities for better abstraction
77
- - Performance improvements
69
+ REFACTORING FOCUS AREAS (prioritized):
70
+ 1. Code duplication and DRY violations (HIGHEST PRIORITY)
71
+ 2. Complex functions that need simplification
72
+ 3. Poor naming conventions
73
+ 4. Missing type annotations (TypeScript)
74
+ 5. Inefficient algorithms or data structures
75
+ 6. Code organization and structure
76
+ 7. Missing or inadequate error handling
77
+ 8. Opportunities for better abstraction
78
+ 9. Performance improvements
78
79
 
79
80
  WORKFLOW:
80
81
  1. First, explore the project structure to understand the codebase
81
82
  2. Identify files that would benefit from refactoring
82
- 3. Analyze specific issues and patterns
83
- 4. Suggest improvements with clear explanations
84
- 5. Ask for user confirmation before making significant changes
85
- 6. Apply changes using the Edit tool if approved
83
+ 3. Analyze specific issues and patterns (focus on high-impact issues first)
84
+ 4. For each issue found:
85
+ - Explain what you're refactoring and why
86
+ - Apply the refactoring using the Edit tool
87
+ - Verify the change is correct
88
+ 5. Continue until all high-priority refactorings are complete or you run out of turns
86
89
 
87
- Be thorough but focused. Prioritize high-impact refactorings that improve code maintainability and quality.`;
90
+ SAFETY GUIDELINES:
91
+ - Start with safest refactorings (extract utilities, remove duplication)
92
+ - Preserve existing functionality - only improve structure/quality
93
+ - Focus on non-breaking changes
94
+ - Make atomic commits for each logical refactoring
95
+
96
+ IMPORTANT: You should DIRECTLY perform refactorings without asking for permission. The user has granted you full bypass authority to improve the code.`;
88
97
  };
89
98
  const createRefactorPrompt = (config) => {
90
- return `Please analyze the code in the current directory for refactoring opportunities.
99
+ return `Please analyze and REFACTOR the code in the current directory. You have full permission to make improvements directly.
100
+
101
+ TASK: Automatically identify and fix code quality issues
91
102
 
92
103
  Steps to follow:
93
104
  1. Start by running "ls -la" to see the project structure
94
105
  2. Identify key source files (focus on patterns: ${config.patterns.join(', ')})
95
106
  3. Read and analyze relevant source files
96
- 4. Look for:
97
- - Code duplication
107
+ 4. Look for HIGH-PRIORITY issues:
108
+ - Code duplication (MOST IMPORTANT - fix first)
98
109
  - Complex or long functions
99
110
  - Poor naming
100
111
  - Missing types or documentation
101
112
  - Error handling issues
102
113
  - Performance bottlenecks
103
- 5. Provide a summary of findings with prioritized recommendations
104
- 6. Ask if I'd like you to proceed with any specific refactoring
114
+ 5. For EACH issue found, IMMEDIATELY apply the fix:
115
+ - Use the Edit tool to make the change
116
+ - Explain what you changed and why
117
+ - Move to the next issue
118
+ 6. Continue refactoring until all high-priority issues are addressed
119
+
120
+ IMPORTANT: Do NOT ask for permission. You have full bypass authority to improve the code directly. Start refactoring immediately after analysis.
105
121
 
106
- Start by exploring the current directory structure.`;
122
+ Start by exploring the current directory structure and begin refactoring.`;
107
123
  };
@@ -0,0 +1,23 @@
1
+ export interface FormatterSection {
2
+ title: string;
3
+ content?: string;
4
+ items?: Array<{
5
+ label: string;
6
+ value?: string | number;
7
+ type?: 'success' | 'error' | 'info';
8
+ }>;
9
+ list?: Array<{
10
+ text: string;
11
+ isHighlight?: boolean;
12
+ }>;
13
+ }
14
+ export interface FormatterOptions {
15
+ title: string;
16
+ icon: string;
17
+ sections: FormatterSection[];
18
+ verbose?: boolean;
19
+ }
20
+ export declare const formatResults: (options: FormatterOptions) => void;
21
+ export declare const createStatusSection: (featureId: string, status: string) => FormatterSection;
22
+ export declare const createSummarySection: (summary?: string) => FormatterSection | null;
23
+ export declare const createContentPreview: (content: string, title: string, maxLines?: number) => FormatterSection;
@@ -0,0 +1,67 @@
1
+ export const formatResults = (options) => {
2
+ const { title, icon, sections } = options;
3
+ console.log('\n' + '='.repeat(60));
4
+ console.log(`${icon} ${title}`);
5
+ console.log('='.repeat(60));
6
+ sections.forEach((section) => {
7
+ if (section.title) {
8
+ console.log(`\n${section.title}`);
9
+ }
10
+ if (section.content) {
11
+ console.log(section.content);
12
+ }
13
+ if (section.items) {
14
+ section.items.forEach((item) => {
15
+ const statusIcon = getStatusIcon(item.type);
16
+ const value = item.value !== undefined ? `: ${item.value}` : '';
17
+ console.log(`${statusIcon} ${item.label}${value}`);
18
+ });
19
+ }
20
+ if (section.list) {
21
+ section.list.forEach((listItem, index) => {
22
+ const prefix = listItem.isHighlight ? ' ⭐' : ' ';
23
+ console.log(`${prefix}${index + 1}. ${listItem.text}`);
24
+ });
25
+ }
26
+ });
27
+ console.log('\n' + '='.repeat(60));
28
+ };
29
+ export const createStatusSection = (featureId, status) => ({
30
+ title: '',
31
+ items: [
32
+ { label: '📋 Feature ID', value: featureId },
33
+ {
34
+ label: '📊 Status',
35
+ value: status === 'success' || status === 'testing_passed' ? '✅ Success' : '❌ Failed',
36
+ type: status === 'success' || status === 'testing_passed' ? 'success' : 'error'
37
+ }
38
+ ]
39
+ });
40
+ export const createSummarySection = (summary) => {
41
+ if (!summary)
42
+ return null;
43
+ return {
44
+ title: '📝 Summary:',
45
+ content: summary
46
+ };
47
+ };
48
+ export const createContentPreview = (content, title, maxLines = 10) => {
49
+ const lines = content.split('\n');
50
+ const previewLines = lines.slice(0, maxLines);
51
+ let displayContent = previewLines.join('\n');
52
+ if (lines.length > maxLines) {
53
+ displayContent += `\n... (${lines.length - maxLines} more lines)`;
54
+ }
55
+ return {
56
+ title: `${title}:`,
57
+ content: '─'.repeat(40) + '\n' + displayContent + '\n' + '─'.repeat(40)
58
+ };
59
+ };
60
+ function getStatusIcon(type) {
61
+ switch (type) {
62
+ case 'success': return '✅';
63
+ case 'error': return '❌';
64
+ case 'info': return 'ℹ️';
65
+ default: return '📊';
66
+ }
67
+ }
@@ -0,0 +1,23 @@
1
+ import { CliOptions } from '../../types/index.js';
2
+ export interface CommandOptions {
3
+ featureId: string;
4
+ mcpServerUrl: string;
5
+ mcpToken: string;
6
+ verbose?: boolean;
7
+ }
8
+ export interface CommandResult {
9
+ status: 'success' | 'error' | 'testing_passed' | 'testing_failed' | 'pending';
10
+ [key: string]: any;
11
+ }
12
+ export interface CommandConfig<TResult extends CommandResult> {
13
+ name: string;
14
+ getFeatureId: (options: CliOptions) => string | undefined;
15
+ checkRequirements: () => Promise<boolean>;
16
+ requirementsError: string;
17
+ startMessage: string;
18
+ successMessage: string;
19
+ errorMessage: string;
20
+ analyzer: (commandOptions: CommandOptions, config: any, ...args: any[]) => Promise<TResult>;
21
+ formatter: (result: TResult, verbose?: boolean) => void;
22
+ }
23
+ export declare function executeCommand<TResult extends CommandResult>(options: CliOptions, config: CommandConfig<TResult>): Promise<void>;
@@ -0,0 +1,39 @@
1
+ import { logInfo, logError, logSuccess } from '../../utils/logger.js';
2
+ import { validateCommandEnvironment, validateRequirements, } from './validation.js';
3
+ export async function executeCommand(options, config) {
4
+ await validateRequirements(config.checkRequirements, config.requirementsError);
5
+ const { config: edsgerConfig, mcpServerUrl, mcpToken } = validateCommandEnvironment(options);
6
+ const featureId = config.getFeatureId(options);
7
+ if (!featureId) {
8
+ throw new Error(`Feature ID is required for ${config.name}`);
9
+ }
10
+ if (options.verbose) {
11
+ logInfo(`${config.startMessage}: ${featureId}`);
12
+ }
13
+ logInfo(`Starting ${config.name}...`);
14
+ try {
15
+ const result = await config.analyzer({
16
+ featureId,
17
+ mcpServerUrl,
18
+ mcpToken,
19
+ verbose: options.verbose,
20
+ }, edsgerConfig);
21
+ config.formatter(result, options.verbose);
22
+ if (result.status === 'success' || result.status === 'testing_passed') {
23
+ logSuccess(config.successMessage);
24
+ process.exit(0);
25
+ }
26
+ else if (result.status === 'error' || result.status === 'testing_failed') {
27
+ logError(config.errorMessage);
28
+ process.exit(1);
29
+ }
30
+ else {
31
+ logError(`${config.errorMessage}: ${result.status}`);
32
+ process.exit(1);
33
+ }
34
+ }
35
+ catch (error) {
36
+ logError(`${config.errorMessage}: ${error instanceof Error ? error.message : String(error)}`);
37
+ process.exit(1);
38
+ }
39
+ }
@@ -1,5 +1,6 @@
1
1
  import { query } from '@anthropic-ai/claude-code';
2
2
  import { logInfo, logError } from '../../utils/logger.js';
3
+ import { getFeedbacksForPhase, formatFeedbacksForContext, } from '../../services/feedbacks.js';
3
4
  import { fetchBugFixingContext, formatContextForPrompt, } from './context-fetcher.js';
4
5
  function userMessage(content) {
5
6
  return {
@@ -23,8 +24,25 @@ export const fixTestFailures = async (options, config) => {
23
24
  logInfo('Fetching bug fixing context via MCP endpoints...');
24
25
  }
25
26
  const context = await fetchBugFixingContext(mcpServerUrl, mcpToken, featureId, verbose);
27
+ // Fetch feedbacks for bug fixing phase
28
+ let feedbacksInfo;
29
+ try {
30
+ const feedbacksContext = await getFeedbacksForPhase({ featureId, mcpServerUrl, mcpToken, verbose }, 'bug_fixing');
31
+ if (feedbacksContext.feedbacks.length > 0) {
32
+ feedbacksInfo = formatFeedbacksForContext(feedbacksContext);
33
+ if (verbose) {
34
+ logInfo(`Added ${feedbacksContext.feedbacks.length} human feedbacks to bug fixing context`);
35
+ }
36
+ }
37
+ }
38
+ catch (error) {
39
+ // Don't fail if feedbacks fetch fails - just log and continue
40
+ if (verbose) {
41
+ logInfo(`Note: Could not fetch feedbacks (${error instanceof Error ? error.message : String(error)})`);
42
+ }
43
+ }
26
44
  const systemPrompt = createSystemPrompt(config);
27
- const bugFixPrompt = createBugFixPromptWithContext(featureId, testErrors, attemptNumber, context);
45
+ const bugFixPrompt = createBugFixPromptWithContext(featureId, testErrors, attemptNumber, context, feedbacksInfo);
28
46
  let lastAssistantResponse = '';
29
47
  let structuredFixResult = null;
30
48
  if (verbose) {
@@ -208,8 +226,12 @@ You MUST end your response with a JSON object containing the bug fix results in
208
226
 
209
227
  Focus on systematic bug fixing based on the provided context and test failure information.`;
210
228
  }
211
- function createBugFixPromptWithContext(featureId, testErrors, attemptNumber, context) {
212
- const contextInfo = formatContextForPrompt(context, testErrors);
229
+ function createBugFixPromptWithContext(featureId, testErrors, attemptNumber, context, feedbacksInfo) {
230
+ let contextInfo = formatContextForPrompt(context, testErrors);
231
+ // Add feedbacks context to the bug fixing prompt
232
+ if (feedbacksInfo) {
233
+ contextInfo = contextInfo + '\n\n' + feedbacksInfo;
234
+ }
213
235
  return `Fix the test failures for feature: ${featureId} (Attempt ${attemptNumber})
214
236
 
215
237
  ${contextInfo}
@@ -1,6 +1,7 @@
1
1
  import { query } from '@anthropic-ai/claude-code';
2
2
  import { logInfo, logError } from '../../utils/logger.js';
3
3
  import { formatChecklistsForContext, } from '../../services/checklist.js';
4
+ import { getFeedbacksForPhase, formatFeedbacksForContext, } from '../../services/feedbacks.js';
4
5
  import { fetchCodeImplementationContext, formatContextForPrompt, } from './context-fetcher.js';
5
6
  import { logFeaturePhaseEvent } from '../../services/audit-logs.js';
6
7
  import { performVerificationCycle, buildImplementationResult, buildVerificationFailureResult, buildNoResultsError, } from './analyzer-helpers.js';
@@ -27,8 +28,25 @@ export const implementFeatureCode = async (options, config, checklistContext) =>
27
28
  logInfo('Fetching code implementation context via MCP endpoints...');
28
29
  }
29
30
  const context = await fetchCodeImplementationContext(mcpServerUrl, mcpToken, featureId, verbose);
31
+ // Fetch feedbacks for code implementation phase
32
+ let feedbacksInfo;
33
+ try {
34
+ const feedbacksContext = await getFeedbacksForPhase({ featureId, mcpServerUrl, mcpToken, verbose }, 'code_implementation');
35
+ if (feedbacksContext.feedbacks.length > 0) {
36
+ feedbacksInfo = formatFeedbacksForContext(feedbacksContext);
37
+ if (verbose) {
38
+ logInfo(`Added ${feedbacksContext.feedbacks.length} human feedbacks to implementation context`);
39
+ }
40
+ }
41
+ }
42
+ catch (error) {
43
+ // Don't fail if feedbacks fetch fails - just log and continue
44
+ if (verbose) {
45
+ logInfo(`Note: Could not fetch feedbacks (${error instanceof Error ? error.message : String(error)})`);
46
+ }
47
+ }
30
48
  const systemPrompt = createSystemPrompt(config, baseBranch, mcpServerUrl, mcpToken, featureId);
31
- const initialImplementationPrompt = createImplementationPromptWithContext(featureId, context, baseBranch, checklistContext, verbose);
49
+ const initialImplementationPrompt = createImplementationPromptWithContext(featureId, context, baseBranch, checklistContext, verbose, feedbacksInfo);
32
50
  const maxIterations = options.maxVerificationIterations || 10;
33
51
  let currentIteration = 0;
34
52
  let currentPrompt = initialImplementationPrompt;
@@ -429,14 +447,18 @@ IMPORTANT: In the checklist context, look for lines that say "ID: [UUID]" in the
429
447
 
430
448
  Remember: Quality over speed. It's better to implement correctly than to rush and create bugs.`;
431
449
  };
432
- const createImplementationPromptWithContext = (featureId, context, baseBranch, checklistContext, verbose) => {
450
+ const createImplementationPromptWithContext = (featureId, context, baseBranch, checklistContext, verbose, feedbacksInfo) => {
433
451
  const contextInfo = formatContextForPrompt(context);
434
- // Add checklist context to the implementation prompt
435
452
  let finalContextInfo = contextInfo;
453
+ // Add feedbacks context to the implementation prompt
454
+ if (feedbacksInfo) {
455
+ finalContextInfo = finalContextInfo + '\n\n' + feedbacksInfo;
456
+ }
457
+ // Add checklist context to the implementation prompt
436
458
  let checklistInstructions = '';
437
459
  if (checklistContext && checklistContext.checklists.length > 0) {
438
460
  const checklistInfo = formatChecklistsForContext(checklistContext);
439
- finalContextInfo = contextInfo + '\n\n' + checklistInfo;
461
+ finalContextInfo = finalContextInfo + '\n\n' + checklistInfo;
440
462
  // DEBUG: Log checklist context details
441
463
  console.log('=== DEBUG: Checklist Context for Code Implementation ===');
442
464
  console.log('Number of checklists:', checklistContext.checklists.length);
@@ -601,9 +623,7 @@ async function pushToRemote(branchName, verbose) {
601
623
  return { success: true };
602
624
  }
603
625
  catch (retryError) {
604
- const errorMessage = retryError instanceof Error
605
- ? retryError.message
606
- : String(retryError);
626
+ const errorMessage = retryError instanceof Error ? retryError.message : String(retryError);
607
627
  return {
608
628
  success: false,
609
629
  error: errorMessage,
@@ -3,6 +3,7 @@ import { fetchFeatureAnalysisContext, } from './context-fetcher.js';
3
3
  import { formatFeatureAnalysisContext } from '../../prompts/formatters.js';
4
4
  import { createFeatureAnalysisSystemPrompt, createFeatureAnalysisPromptWithContext, } from '../../prompts/feature-analysis.js';
5
5
  import { formatChecklistsForContext, } from '../../services/checklist.js';
6
+ import { getFeedbacksForPhase, formatFeedbacksForContext, } from '../../services/feedbacks.js';
6
7
  import { executeAnalysisQuery, performVerificationCycle, saveAnalysisArtifactsAsDraft, updateArtifactsToReady, deleteArtifacts, buildAnalysisResult, buildVerificationFailureResult, buildNoResultsError, } from './analyzer-helpers.js';
7
8
  import { logFeaturePhaseEvent } from '../../services/audit-logs.js';
8
9
  export const analyzeFeatureWithMCP = async (options, config, checklistContext) => {
@@ -165,11 +166,28 @@ async function prepareAnalysisContext(mcpServerUrl, mcpToken, featureId, checkli
165
166
  logInfo(` - ${img.url} -> ${img.localPath}`);
166
167
  });
167
168
  }
168
- // Add checklist context to the analysis prompt
169
169
  let finalContextInfo = contextInfo;
170
+ // Add feedbacks context to the analysis prompt
171
+ try {
172
+ const feedbacksContext = await getFeedbacksForPhase({ featureId, mcpServerUrl, mcpToken, verbose }, 'feature-analysis');
173
+ if (feedbacksContext.feedbacks.length > 0) {
174
+ const feedbacksInfo = formatFeedbacksForContext(feedbacksContext);
175
+ finalContextInfo = finalContextInfo + '\n\n' + feedbacksInfo;
176
+ if (verbose) {
177
+ logInfo(`Added ${feedbacksContext.feedbacks.length} human feedbacks to context`);
178
+ }
179
+ }
180
+ }
181
+ catch (error) {
182
+ // Don't fail if feedbacks fetch fails - just log and continue
183
+ if (verbose) {
184
+ logInfo(`Note: Could not fetch feedbacks (${error instanceof Error ? error.message : String(error)})`);
185
+ }
186
+ }
187
+ // Add checklist context to the analysis prompt
170
188
  if (checklistContext && checklistContext.checklists.length > 0) {
171
189
  const checklistInfo = formatChecklistsForContext(checklistContext);
172
- finalContextInfo = contextInfo + '\n\n' + checklistInfo;
190
+ finalContextInfo = finalContextInfo + '\n\n' + checklistInfo;
173
191
  if (verbose) {
174
192
  logInfo(`Added ${checklistContext.checklists.length} checklists to analysis context`);
175
193
  }
@@ -1,6 +1,7 @@
1
1
  import { query } from '@anthropic-ai/claude-code';
2
2
  import { logInfo, logError } from '../../utils/logger.js';
3
3
  import { formatChecklistsForContext, } from '../../services/checklist.js';
4
+ import { getFeedbacksForPhase, formatFeedbacksForContext, } from '../../services/feedbacks.js';
4
5
  import { saveFunctionalTestResultsWithRetry } from './http-fallback.js';
5
6
  import { fetchFunctionalTestingContext, formatContextForPrompt, } from './context-fetcher.js';
6
7
  import { updateFeatureStatus } from '../../api/features/index.js';
@@ -27,8 +28,25 @@ export const runFunctionalTesting = async (options, config, checklistContext) =>
27
28
  logInfo('Fetching feature testing context via MCP endpoints...');
28
29
  }
29
30
  const context = await fetchFunctionalTestingContext(mcpServerUrl, mcpToken, featureId, verbose);
31
+ // Fetch feedbacks for functional testing phase
32
+ let feedbacksInfo;
33
+ try {
34
+ const feedbacksContext = await getFeedbacksForPhase({ featureId, mcpServerUrl, mcpToken, verbose }, 'functional_testing');
35
+ if (feedbacksContext.feedbacks.length > 0) {
36
+ feedbacksInfo = formatFeedbacksForContext(feedbacksContext);
37
+ if (verbose) {
38
+ logInfo(`Added ${feedbacksContext.feedbacks.length} human feedbacks to testing context`);
39
+ }
40
+ }
41
+ }
42
+ catch (error) {
43
+ // Don't fail if feedbacks fetch fails - just log and continue
44
+ if (verbose) {
45
+ logInfo(`Note: Could not fetch feedbacks (${error instanceof Error ? error.message : String(error)})`);
46
+ }
47
+ }
30
48
  const systemPrompt = createSystemPrompt(config, mcpServerUrl, mcpToken, featureId);
31
- const testingPrompt = createTestingPromptWithContext(featureId, context, checklistContext, verbose);
49
+ const testingPrompt = createTestingPromptWithContext(featureId, context, checklistContext, verbose, feedbacksInfo);
32
50
  let lastAssistantResponse = '';
33
51
  let structuredTestResult = null;
34
52
  let testStatus = 'testing_failed';
@@ -475,13 +493,17 @@ IMPORTANT: In the checklist context, look for lines that say "ID: [UUID]" in the
475
493
 
476
494
  Focus on systematic testing based on the provided context information.${mcpInstructions}`;
477
495
  };
478
- const createTestingPromptWithContext = (featureId, context, checklistContext, verbose) => {
496
+ const createTestingPromptWithContext = (featureId, context, checklistContext, verbose, feedbacksInfo) => {
479
497
  const contextInfo = formatContextForPrompt(context);
480
- // Add checklist context to the testing prompt
481
498
  let finalContextInfo = contextInfo;
499
+ // Add feedbacks context to the testing prompt
500
+ if (feedbacksInfo) {
501
+ finalContextInfo = finalContextInfo + '\n\n' + feedbacksInfo;
502
+ }
503
+ // Add checklist context to the testing prompt
482
504
  if (checklistContext && checklistContext.checklists.length > 0) {
483
505
  const checklistInfo = formatChecklistsForContext(checklistContext);
484
- finalContextInfo = contextInfo + '\n\n' + checklistInfo;
506
+ finalContextInfo = finalContextInfo + '\n\n' + checklistInfo;
485
507
  if (verbose) {
486
508
  logInfo(`Added ${checklistContext.checklists.length} checklists to testing context`);
487
509
  }
@@ -6,6 +6,7 @@ import { updateTechnicalDesign } from '../../api/features/index.js';
6
6
  import { formatTechnicalDesignContext } from '../../prompts/formatters.js';
7
7
  import { createTechnicalDesignSystemPrompt, createTechnicalDesignPromptWithContext, } from '../../prompts/technical-design.js';
8
8
  import { formatChecklistsForContext, } from '../../services/checklist.js';
9
+ import { getFeedbacksForPhase, formatFeedbacksForContext, } from '../../services/feedbacks.js';
9
10
  import { performVerificationCycle, buildDesignResult, buildVerificationFailureResult, buildNoResultsError, } from './analyzer-helpers.js';
10
11
  import { logFeaturePhaseEvent } from '../../services/audit-logs.js';
11
12
  function userMessage(content) {
@@ -155,20 +156,59 @@ async function prepareDesignContext(mcpServerUrl, mcpToken, featureId, checklist
155
156
  logInfo(` - ${img.url} -> ${img.localPath}`);
156
157
  });
157
158
  }
158
- // Add checklist context to the design prompt
159
159
  let finalContextInfo = contextInfo;
160
+ let hasFeedbacks = false;
161
+ // Check if there's existing technical design
162
+ const existingTechnicalDesign = context.feature.technical_design;
163
+ const hasExistingDesign = !!existingTechnicalDesign && existingTechnicalDesign.trim().length > 0;
164
+ // Add existing technical design to context if it exists
165
+ if (hasExistingDesign && verbose) {
166
+ logInfo('📋 Found existing technical design - will perform incremental update if feedbacks exist');
167
+ }
168
+ // Add feedbacks context to the design prompt
169
+ try {
170
+ const feedbacksContext = await getFeedbacksForPhase({ featureId, mcpServerUrl, mcpToken, verbose }, 'technical-design');
171
+ if (feedbacksContext.feedbacks.length > 0) {
172
+ hasFeedbacks = true;
173
+ const feedbacksInfo = formatFeedbacksForContext(feedbacksContext);
174
+ finalContextInfo = finalContextInfo + '\n\n' + feedbacksInfo;
175
+ if (verbose) {
176
+ logInfo(`Added ${feedbacksContext.feedbacks.length} human feedbacks to design context`);
177
+ }
178
+ }
179
+ }
180
+ catch (error) {
181
+ // Don't fail if feedbacks fetch fails - just log and continue
182
+ if (verbose) {
183
+ logInfo(`Note: Could not fetch feedbacks (${error instanceof Error ? error.message : String(error)})`);
184
+ }
185
+ }
186
+ // Add checklist context to the design prompt
160
187
  if (checklistContext && checklistContext.checklists.length > 0) {
161
188
  const checklistInfo = formatChecklistsForContext(checklistContext);
162
- finalContextInfo = contextInfo + '\n\n' + checklistInfo;
189
+ finalContextInfo = finalContextInfo + '\n\n' + checklistInfo;
163
190
  if (verbose) {
164
191
  logInfo(`Added ${checklistContext.checklists.length} checklists to design context`);
165
192
  }
166
193
  }
167
- const designPrompt = createTechnicalDesignPromptWithContext(featureId, finalContextInfo);
194
+ // Decide on prompt mode based on existing design and feedbacks
195
+ const isIncrementalUpdate = hasExistingDesign && hasFeedbacks;
196
+ if (isIncrementalUpdate && verbose) {
197
+ logInfo('🔄 Mode: Incremental Update (existing design + feedbacks)');
198
+ }
199
+ else if (hasExistingDesign && verbose) {
200
+ logInfo('🔄 Mode: Full Redesign (existing design but no feedbacks)');
201
+ }
202
+ else if (verbose) {
203
+ logInfo('✨ Mode: New Design (no existing design)');
204
+ }
205
+ const designPrompt = createTechnicalDesignPromptWithContext(featureId, finalContextInfo, existingTechnicalDesign, isIncrementalUpdate);
168
206
  return {
169
207
  featureName: context.feature.name,
170
208
  featureDescription: context.feature.description || undefined,
171
209
  designPrompt,
210
+ hasExistingDesign,
211
+ hasFeedbacks,
172
212
  };
173
213
  }
174
214
  /**
@@ -1,3 +1,3 @@
1
1
  import { EdsgerConfig } from '../types/index.js';
2
2
  export declare const createTechnicalDesignSystemPrompt: (_config: EdsgerConfig, mcpServerUrl?: string, mcpToken?: string, featureId?: string) => string;
3
- export declare const createTechnicalDesignPromptWithContext: (featureId: string, contextInfo: string) => string;
3
+ export declare const createTechnicalDesignPromptWithContext: (featureId: string, contextInfo: string, existingTechnicalDesign?: string | null, isIncrementalUpdate?: boolean) => string;
@@ -89,11 +89,58 @@ IMPORTANT: In the checklist context, look for lines that say "ID: [UUID]" in the
89
89
 
90
90
  Focus on systematic technical design based on the provided context information.${mcpInstructions}`;
91
91
  };
92
- export const createTechnicalDesignPromptWithContext = (featureId, contextInfo) => {
93
- return `Generate a comprehensive technical design for feature ID: ${featureId}
92
+ export const createTechnicalDesignPromptWithContext = (featureId, contextInfo, existingTechnicalDesign, isIncrementalUpdate = false) => {
93
+ // If incremental update mode (existing design + feedbacks), create focused update prompt
94
+ if (isIncrementalUpdate && existingTechnicalDesign) {
95
+ return `Update the technical design for feature ID: ${featureId} based on human feedbacks
96
+
97
+ ## Current Technical Design
98
+
99
+ ${existingTechnicalDesign}
100
+
101
+ ## Context and Feedbacks
94
102
 
95
103
  ${contextInfo}
96
104
 
105
+ ## IMPORTANT: Incremental Update Mode
106
+
107
+ You are in **INCREMENTAL UPDATE** mode. This means:
108
+
109
+ 1. **DO NOT redesign from scratch** - The current technical design is already approved
110
+ 2. **Focus ONLY on the feedbacks provided** - Address each feedback point specifically
111
+ 3. **Make targeted modifications** - Only change the sections that feedbacks request
112
+ 4. **Preserve approved sections** - Keep all parts of the design that are not mentioned in feedbacks
113
+ 5. **Track changes** - In your summary, clearly indicate what was modified and why
114
+
115
+ ## Update Instructions
116
+
117
+ Follow this focused approach:
118
+
119
+ 1. **Review Current Design**: Carefully read the existing technical design above
120
+ 2. **Analyze Feedbacks**: Understand what specific changes are requested in the feedbacks
121
+ 3. **Make Targeted Updates**:
122
+ - Address each feedback point one by one
123
+ - Modify only the relevant sections of the design
124
+ - Preserve the structure and content of unchanged sections
125
+ 4. **Verify Completeness**: Ensure all feedbacks have been addressed
126
+
127
+ ## Important Notes
128
+ - This is NOT a full redesign - only update what feedbacks specifically request
129
+ - Maintain consistency with the existing design's structure and style
130
+ - If a feedback is unclear, make your best interpretation and note it in recommendations
131
+ - Keep all approved content that is not mentioned in feedbacks
132
+ - Your summary should list each feedback addressed and the changes made
133
+
134
+ Begin by reviewing the feedbacks and identifying which sections of the current design need updates.`;
135
+ }
136
+ // Default mode: full design (either new design or full redesign)
137
+ const designMode = existingTechnicalDesign
138
+ ? 'Update and improve the existing technical design, or redesign if needed'
139
+ : 'Generate a comprehensive technical design';
140
+ return `${designMode} for feature ID: ${featureId}
141
+
142
+ ${existingTechnicalDesign ? `## Existing Technical Design\n\n${existingTechnicalDesign}\n\n` : ''}${contextInfo}
143
+
97
144
  ## Technical Design Instructions
98
145
 
99
146
  Follow this systematic approach:
@@ -122,7 +169,7 @@ Follow this systematic approach:
122
169
  - Focus on creating detailed architectural specifications
123
170
  - Consider the user stories to understand what needs to be built
124
171
  - Use test cases to inform testing strategy and validation approaches
125
- - Build upon existing technical design if available, or create from scratch
172
+ ${existingTechnicalDesign ? '- Build upon or improve the existing technical design provided above' : '- Create a complete new technical design from scratch'}
126
173
  - Consider integration with existing systems and architecture
127
174
  - Address scalability, maintainability, and performance requirements
128
175
 
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Feedbacks service for pipeline integration
3
+ * Provides human-guided feedbacks to influence Claude Code behavior in each phase
4
+ */
5
+ import { PipelinePhaseOptions } from '../types/pipeline.js';
6
+ export type { PipelinePhaseOptions };
7
+ export type FeedbackType = 'requirement' | 'constraint' | 'preference' | 'context' | 'quality_criteria';
8
+ export interface Feedback {
9
+ id: string;
10
+ feature_id: string | null;
11
+ product_id: string | null;
12
+ phase: string;
13
+ feedback_type: FeedbackType;
14
+ title: string;
15
+ content: string;
16
+ priority: number;
17
+ is_active: boolean;
18
+ created_by: string;
19
+ created_at: string;
20
+ updated_at: string;
21
+ }
22
+ export interface FeedbacksContext {
23
+ phase: string;
24
+ feature_id: string;
25
+ feedbacks: Feedback[];
26
+ }
27
+ /**
28
+ * Fetch feedbacks for a specific phase
29
+ * Includes both feature-level and product-level feedbacks
30
+ */
31
+ export declare function getFeedbacksForPhase(options: PipelinePhaseOptions, phase: string): Promise<FeedbacksContext>;
32
+ /**
33
+ * Format feedbacks context as string for LLM consumption
34
+ */
35
+ export declare function formatFeedbacksForContext(context: FeedbacksContext): string;
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Feedbacks service for pipeline integration
3
+ * Provides human-guided feedbacks to influence Claude Code behavior in each phase
4
+ */
5
+ /**
6
+ * Fetch feedbacks for a specific phase
7
+ * Includes both feature-level and product-level feedbacks
8
+ */
9
+ export async function getFeedbacksForPhase(options, phase) {
10
+ const { mcpServerUrl, mcpToken, featureId, verbose } = options;
11
+ // Convert phase name from hyphen to underscore format for database compatibility
12
+ // e.g., 'feature-analysis' -> 'feature_analysis'
13
+ const dbPhase = phase.replace(/-/g, '_');
14
+ if (verbose) {
15
+ console.log(`🔍 Fetching feedbacks: phase="${phase}" (db: "${dbPhase}"), feature_id="${featureId}"`);
16
+ }
17
+ const response = await fetch(`${mcpServerUrl}/mcp`, {
18
+ method: 'POST',
19
+ headers: {
20
+ 'Content-Type': 'application/json',
21
+ Authorization: `Bearer ${mcpToken}`,
22
+ },
23
+ body: JSON.stringify({
24
+ jsonrpc: '2.0',
25
+ id: 1,
26
+ method: 'feedbacks/get',
27
+ params: {
28
+ feature_id: featureId,
29
+ phase: dbPhase,
30
+ },
31
+ }),
32
+ });
33
+ if (!response.ok) {
34
+ const errorText = await response.text();
35
+ throw new Error(`Failed to fetch feedbacks for phase "${phase}": ${response.status} ${response.statusText}. Response: ${errorText}`);
36
+ }
37
+ const data = await response.json();
38
+ if (data.error) {
39
+ throw new Error(`MCP Error for phase "${phase}": ${data.error.message || JSON.stringify(data.error)}`);
40
+ }
41
+ // Handle empty result gracefully
42
+ if (!data.result) {
43
+ return {
44
+ phase,
45
+ feature_id: featureId,
46
+ feedbacks: [],
47
+ };
48
+ }
49
+ return data.result;
50
+ }
51
+ /**
52
+ * Format feedbacks context as string for LLM consumption
53
+ */
54
+ export function formatFeedbacksForContext(context) {
55
+ if (!context.feedbacks || context.feedbacks.length === 0) {
56
+ return '';
57
+ }
58
+ // Group feedbacks by type
59
+ const grouped = groupBy(context.feedbacks, 'feedback_type');
60
+ const sections = [];
61
+ // Requirements
62
+ if (grouped.requirement && grouped.requirement.length > 0) {
63
+ sections.push(`## 📋 Additional Requirements\n\n${formatFeedbacksList(grouped.requirement)}`);
64
+ }
65
+ // Constraints
66
+ if (grouped.constraint && grouped.constraint.length > 0) {
67
+ sections.push(`## 🚫 Constraints\n\n${formatFeedbacksList(grouped.constraint)}`);
68
+ }
69
+ // Preferences
70
+ if (grouped.preference && grouped.preference.length > 0) {
71
+ sections.push(`## 💡 Preferences\n\n${formatFeedbacksList(grouped.preference)}`);
72
+ }
73
+ // Context
74
+ if (grouped.context && grouped.context.length > 0) {
75
+ sections.push(`## 🔍 Additional Context\n\n${formatFeedbacksList(grouped.context)}`);
76
+ }
77
+ // Quality Criteria
78
+ if (grouped.quality_criteria && grouped.quality_criteria.length > 0) {
79
+ sections.push(`## ✅ Quality Criteria\n\n${formatFeedbacksList(grouped.quality_criteria)}`);
80
+ }
81
+ const phaseDisplay = context.phase.replace(/_/g, '-');
82
+ return `
83
+ ---
84
+
85
+ # 🎯 Human Feedbacks for ${phaseDisplay} Phase
86
+
87
+ These feedbacks have been provided by the development team to guide your work in this phase. They represent domain knowledge, business constraints, and quality expectations that must be incorporated into your analysis and outputs.
88
+
89
+ ${sections.join('\n\n')}
90
+
91
+ ---
92
+
93
+ **IMPORTANT**: These human-provided feedbacks are MANDATORY and take precedence over default behavior. Please ensure your work:
94
+ - ✅ Satisfies ALL requirements listed above
95
+ - ✅ Respects ALL constraints
96
+ - ✅ Incorporates stated preferences where possible
97
+ - ✅ Considers the additional context provided
98
+ - ✅ Meets ALL quality criteria
99
+
100
+ If any feedback conflicts with checklist items or other requirements, prioritize the human feedbacks and note any conflicts in your response.
101
+ `;
102
+ }
103
+ /**
104
+ * Format a list of feedbacks with priority ordering
105
+ */
106
+ function formatFeedbacksList(feedbacks) {
107
+ // Sort by priority (highest first)
108
+ const sorted = [...feedbacks].sort((a, b) => b.priority - a.priority);
109
+ return sorted
110
+ .map((feedback, idx) => {
111
+ const priorityBadge = getPriorityBadge(feedback.priority);
112
+ return `### ${idx + 1}. ${feedback.title} ${priorityBadge}\n\n${feedback.content}\n`;
113
+ })
114
+ .join('\n');
115
+ }
116
+ /**
117
+ * Get priority badge emoji
118
+ */
119
+ function getPriorityBadge(priority) {
120
+ if (priority >= 9)
121
+ return '🔴'; // Critical
122
+ if (priority >= 7)
123
+ return '🟠'; // High
124
+ if (priority >= 5)
125
+ return '🟡'; // Medium
126
+ if (priority >= 3)
127
+ return '🟢'; // Low
128
+ return '⚪'; // Minimal
129
+ }
130
+ /**
131
+ * Group array of objects by a key
132
+ */
133
+ function groupBy(array, key) {
134
+ return array.reduce((result, item) => {
135
+ const groupKey = String(item[key]);
136
+ if (!result[groupKey]) {
137
+ result[groupKey] = [];
138
+ }
139
+ result[groupKey].push(item);
140
+ return result;
141
+ }, {});
142
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "edsger",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {