edsger 0.26.0 → 0.26.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.
Files changed (129) hide show
  1. package/.claude/settings.local.json +28 -0
  2. package/.env.local +12 -0
  3. package/dist/api/features/__tests__/regression-prevention.test.d.ts +5 -0
  4. package/dist/api/features/__tests__/regression-prevention.test.js +338 -0
  5. package/dist/api/features/__tests__/status-updater.integration.test.d.ts +5 -0
  6. package/dist/api/features/__tests__/status-updater.integration.test.js +497 -0
  7. package/dist/commands/workflow/config/phase-configs.js +5 -0
  8. package/dist/commands/workflow/executors/phase-executor.d.ts +2 -2
  9. package/dist/commands/workflow/executors/phase-executor.js +2 -2
  10. package/dist/commands/workflow/feature-coordinator.js +3 -1
  11. package/dist/commands/workflow/phase-orchestrator.js +66 -3
  12. package/dist/commands/workflow/pipeline-runner.d.ts +17 -0
  13. package/dist/commands/workflow/pipeline-runner.js +393 -0
  14. package/dist/commands/workflow/runner.d.ts +26 -0
  15. package/dist/commands/workflow/runner.js +119 -0
  16. package/dist/commands/workflow/workflow-runner.d.ts +26 -0
  17. package/dist/commands/workflow/workflow-runner.js +119 -0
  18. package/dist/config/feature-status.js +3 -0
  19. package/dist/index.js +0 -0
  20. package/dist/phases/code-implementation/analyzer-helpers.d.ts +28 -0
  21. package/dist/phases/code-implementation/analyzer-helpers.js +177 -0
  22. package/dist/phases/code-implementation/analyzer.d.ts +32 -0
  23. package/dist/phases/code-implementation/analyzer.js +629 -0
  24. package/dist/phases/code-implementation/context-fetcher.d.ts +17 -0
  25. package/dist/phases/code-implementation/context-fetcher.js +86 -0
  26. package/dist/phases/code-implementation/mcp-server.d.ts +1 -0
  27. package/dist/phases/code-implementation/mcp-server.js +93 -0
  28. package/dist/phases/code-implementation/prompts-improvement.d.ts +5 -0
  29. package/dist/phases/code-implementation/prompts-improvement.js +108 -0
  30. package/dist/phases/code-implementation-verification/verifier.d.ts +31 -0
  31. package/dist/phases/code-implementation-verification/verifier.js +196 -0
  32. package/dist/phases/code-refine/analyzer.d.ts +41 -0
  33. package/dist/phases/code-refine/analyzer.js +561 -0
  34. package/dist/phases/code-refine/context-fetcher.d.ts +94 -0
  35. package/dist/phases/code-refine/context-fetcher.js +423 -0
  36. package/dist/phases/code-refine-verification/analysis/llm-analyzer.d.ts +22 -0
  37. package/dist/phases/code-refine-verification/analysis/llm-analyzer.js +134 -0
  38. package/dist/phases/code-refine-verification/verifier.d.ts +47 -0
  39. package/dist/phases/code-refine-verification/verifier.js +597 -0
  40. package/dist/phases/code-review/analyzer.d.ts +29 -0
  41. package/dist/phases/code-review/analyzer.js +363 -0
  42. package/dist/phases/code-review/context-fetcher.d.ts +92 -0
  43. package/dist/phases/code-review/context-fetcher.js +296 -0
  44. package/dist/phases/feature-analysis/analyzer-helpers.d.ts +10 -0
  45. package/dist/phases/feature-analysis/analyzer-helpers.js +47 -0
  46. package/dist/phases/feature-analysis/analyzer.d.ts +11 -0
  47. package/dist/phases/feature-analysis/analyzer.js +208 -0
  48. package/dist/phases/feature-analysis/context-fetcher.d.ts +26 -0
  49. package/dist/phases/feature-analysis/context-fetcher.js +134 -0
  50. package/dist/phases/feature-analysis/http-fallback.d.ts +20 -0
  51. package/dist/phases/feature-analysis/http-fallback.js +95 -0
  52. package/dist/phases/feature-analysis/mcp-server.d.ts +1 -0
  53. package/dist/phases/feature-analysis/mcp-server.js +144 -0
  54. package/dist/phases/feature-analysis/prompts-improvement.d.ts +8 -0
  55. package/dist/phases/feature-analysis/prompts-improvement.js +109 -0
  56. package/dist/phases/feature-analysis-verification/verifier.d.ts +37 -0
  57. package/dist/phases/feature-analysis-verification/verifier.js +147 -0
  58. package/dist/phases/pr-execution/context.d.ts +26 -0
  59. package/dist/phases/pr-execution/context.js +156 -0
  60. package/dist/phases/pr-execution/index.d.ts +20 -0
  61. package/dist/phases/pr-execution/index.js +287 -0
  62. package/dist/phases/pr-execution/outcome.d.ts +26 -0
  63. package/dist/phases/pr-execution/outcome.js +34 -0
  64. package/dist/phases/pr-execution/pr-executor.d.ts +28 -0
  65. package/dist/phases/pr-execution/pr-executor.js +152 -0
  66. package/dist/phases/pr-execution/prompts.d.ts +17 -0
  67. package/dist/phases/pr-execution/prompts.js +208 -0
  68. package/dist/phases/pr-splitting/context.d.ts +16 -2
  69. package/dist/phases/pr-splitting/context.js +127 -4
  70. package/dist/phases/pr-splitting/index.d.ts +7 -0
  71. package/dist/phases/pr-splitting/index.js +58 -52
  72. package/dist/phases/pr-splitting/prompts.d.ts +4 -4
  73. package/dist/phases/pr-splitting/prompts.js +42 -30
  74. package/dist/phases/technical-design/analyzer-helpers.d.ts +25 -0
  75. package/dist/phases/technical-design/analyzer-helpers.js +39 -0
  76. package/dist/phases/technical-design/analyzer.d.ts +21 -0
  77. package/dist/phases/technical-design/analyzer.js +461 -0
  78. package/dist/phases/technical-design/context-fetcher.d.ts +12 -0
  79. package/dist/phases/technical-design/context-fetcher.js +39 -0
  80. package/dist/phases/technical-design/http-fallback.d.ts +17 -0
  81. package/dist/phases/technical-design/http-fallback.js +151 -0
  82. package/dist/phases/technical-design/mcp-server.d.ts +1 -0
  83. package/dist/phases/technical-design/mcp-server.js +157 -0
  84. package/dist/phases/technical-design/prompts-improvement.d.ts +5 -0
  85. package/dist/phases/technical-design/prompts-improvement.js +93 -0
  86. package/dist/phases/technical-design-verification/verifier.d.ts +53 -0
  87. package/dist/phases/technical-design-verification/verifier.js +170 -0
  88. package/dist/services/audit-logs.d.ts +2 -2
  89. package/dist/services/feature-branches.d.ts +77 -0
  90. package/dist/services/feature-branches.js +205 -0
  91. package/dist/types/index.d.ts +1 -1
  92. package/dist/types/pipeline.d.ts +1 -1
  93. package/dist/utils/github-repo-info.d.ts +14 -0
  94. package/dist/utils/github-repo-info.js +19 -0
  95. package/dist/workflow-runner/config/phase-configs.d.ts +5 -0
  96. package/dist/workflow-runner/config/phase-configs.js +120 -0
  97. package/dist/workflow-runner/core/feature-filter.d.ts +16 -0
  98. package/dist/workflow-runner/core/feature-filter.js +46 -0
  99. package/dist/workflow-runner/core/index.d.ts +8 -0
  100. package/dist/workflow-runner/core/index.js +12 -0
  101. package/dist/workflow-runner/core/pipeline-evaluator.d.ts +24 -0
  102. package/dist/workflow-runner/core/pipeline-evaluator.js +32 -0
  103. package/dist/workflow-runner/core/state-manager.d.ts +24 -0
  104. package/dist/workflow-runner/core/state-manager.js +42 -0
  105. package/dist/workflow-runner/core/workflow-logger.d.ts +20 -0
  106. package/dist/workflow-runner/core/workflow-logger.js +65 -0
  107. package/dist/workflow-runner/executors/phase-executor.d.ts +8 -0
  108. package/dist/workflow-runner/executors/phase-executor.js +248 -0
  109. package/dist/workflow-runner/feature-workflow-runner.d.ts +26 -0
  110. package/dist/workflow-runner/feature-workflow-runner.js +119 -0
  111. package/dist/workflow-runner/index.d.ts +2 -0
  112. package/dist/workflow-runner/index.js +2 -0
  113. package/dist/workflow-runner/pipeline-runner.d.ts +17 -0
  114. package/dist/workflow-runner/pipeline-runner.js +393 -0
  115. package/dist/workflow-runner/workflow-processor.d.ts +54 -0
  116. package/dist/workflow-runner/workflow-processor.js +170 -0
  117. package/package.json +1 -1
  118. package/dist/services/lifecycle-agent/__tests__/phase-criteria.test.d.ts +0 -4
  119. package/dist/services/lifecycle-agent/__tests__/phase-criteria.test.js +0 -133
  120. package/dist/services/lifecycle-agent/__tests__/transition-rules.test.d.ts +0 -4
  121. package/dist/services/lifecycle-agent/__tests__/transition-rules.test.js +0 -336
  122. package/dist/services/lifecycle-agent/index.d.ts +0 -24
  123. package/dist/services/lifecycle-agent/index.js +0 -25
  124. package/dist/services/lifecycle-agent/phase-criteria.d.ts +0 -57
  125. package/dist/services/lifecycle-agent/phase-criteria.js +0 -335
  126. package/dist/services/lifecycle-agent/transition-rules.d.ts +0 -60
  127. package/dist/services/lifecycle-agent/transition-rules.js +0 -184
  128. package/dist/services/lifecycle-agent/types.d.ts +0 -190
  129. package/dist/services/lifecycle-agent/types.js +0 -12
@@ -17,6 +17,13 @@ async function* prompt(analysisPrompt) {
17
17
  yield userMessage(analysisPrompt);
18
18
  await new Promise((res) => setTimeout(res, 10000));
19
19
  }
20
+ /**
21
+ * PR Splitting Phase: Analyze diff and create PR split plan
22
+ *
23
+ * This phase analyzes the actual code diff between dev/{featureId} and main,
24
+ * then uses AI to produce a PR split plan saved to the database.
25
+ * Human review is expected before running the pr-execution phase.
26
+ */
20
27
  export const splitFeatureIntoPRs = async (options, config) => {
21
28
  const { featureId, verbose, replaceExisting } = options;
22
29
  if (verbose) {
@@ -34,8 +41,14 @@ export const splitFeatureIntoPRs = async (options, config) => {
34
41
  timestamp: new Date().toISOString(),
35
42
  },
36
43
  }, verbose);
37
- // Fetch context
38
- const context = await fetchPRSplittingContext(featureId, verbose);
44
+ // Fetch context (includes git diff from dev/{featureId} vs main)
45
+ const context = await fetchPRSplittingContext(featureId, verbose, replaceExisting);
46
+ if (context.changedFiles.length === 0) {
47
+ if (verbose) {
48
+ logInfo('No code changes detected between dev branch and main.');
49
+ }
50
+ return buildNoChangeResult(featureId, context.existing_pull_requests.length);
51
+ }
39
52
  // Fetch feedbacks for pr-splitting phase
40
53
  let feedbacksContext = null;
41
54
  let hasFeedbacks = false;
@@ -54,32 +67,37 @@ export const splitFeatureIntoPRs = async (options, config) => {
54
67
  // Determine operation mode
55
68
  const hasExistingPRs = context.existing_pull_requests.length > 0;
56
69
  const isIncrementalUpdate = hasExistingPRs && hasFeedbacks && !replaceExisting;
57
- if (verbose) {
58
- logInfo('\n📋 PR Splitting Mode Decision:');
59
- logInfo(` - Has existing PRs: ${hasExistingPRs} (${context.existing_pull_requests.length} PRs)`);
60
- logInfo(` - Has feedbacks: ${hasFeedbacks} (${feedbacksContext?.feedbacks.length || 0} feedbacks)`);
61
- logInfo(` - Replace existing: ${replaceExisting || false}`);
62
- logInfo(` - Mode: ${isIncrementalUpdate ? '🔄 Incremental Update' : hasExistingPRs && !replaceExisting ? '✅ No Change Needed' : '✨ New Planning'}`);
63
- }
64
- // Check if there are existing PRs and no feedbacks - no change needed
70
+ // Check if existing PRs already planned and no feedback
65
71
  if (hasExistingPRs && !hasFeedbacks && !replaceExisting) {
66
72
  if (verbose) {
67
- logInfo(`Feature already has ${context.existing_pull_requests.length} PRs planned and no feedbacks to address.`);
73
+ logInfo(`Feature already has ${context.existing_pull_requests.length} PRs planned. No feedbacks to address.`);
68
74
  logInfo('Use replaceExisting: true to re-plan.');
69
75
  }
70
76
  return buildNoChangeResult(featureId, context.existing_pull_requests.length);
71
77
  }
72
- // Clear existing PRs if requested (full re-plan)
78
+ if (verbose) {
79
+ logInfo('\n📋 PR Splitting Mode:');
80
+ logInfo(` - Has existing PRs: ${hasExistingPRs}`);
81
+ logInfo(` - Has feedbacks: ${hasFeedbacks}`);
82
+ logInfo(` - Replace existing: ${replaceExisting || false}`);
83
+ logInfo(` - Mode: ${isIncrementalUpdate ? '🔄 Incremental Update' : '✨ New Planning'}`);
84
+ logInfo(` - Changed files: ${context.changedFiles.length}`);
85
+ }
86
+ // Clear existing PRs if replacing
73
87
  if (replaceExisting && hasExistingPRs) {
74
88
  if (verbose) {
75
89
  logInfo(`Clearing ${context.existing_pull_requests.length} existing PRs for full re-plan...`);
76
90
  }
77
91
  await clearPullRequests({ featureId, verbose }, true);
78
92
  }
79
- // Format context for prompt
93
+ // ==============================
94
+ // Diff Analysis with AI Agent
95
+ // ==============================
96
+ if (verbose) {
97
+ logInfo('\n🔍 Analyzing code diff with AI...');
98
+ }
80
99
  const contextInfo = formatContextForPrompt(context.feature, context.product, context.existing_branches);
81
100
  const existingPRsInfo = formatExistingPRsForPrompt(context.existing_pull_requests);
82
- // Create prompts based on mode
83
101
  const systemPrompt = createPRSplittingSystemPrompt(config, featureId);
84
102
  let userPrompt;
85
103
  if (isIncrementalUpdate && feedbacksContext) {
@@ -87,7 +105,6 @@ export const splitFeatureIntoPRs = async (options, config) => {
87
105
  logInfo('\n🎯 Feedbacks that will be addressed:');
88
106
  feedbacksContext.feedbacks.forEach((fb, idx) => {
89
107
  logInfo(` ${idx + 1}. [${fb.feedback_type}] ${fb.title}`);
90
- logInfo(` ${fb.content.substring(0, 100)}${fb.content.length > 100 ? '...' : ''}`);
91
108
  });
92
109
  }
93
110
  const feedbacksInfo = await formatFeedbacksForContext(feedbacksContext);
@@ -96,39 +113,40 @@ export const splitFeatureIntoPRs = async (options, config) => {
96
113
  userPrompt = `${userPrompt}\n\n## Feature Context\n\n${contextInfo}`;
97
114
  }
98
115
  else {
99
- userPrompt = createPRSplittingPromptWithContext(featureId, contextInfo, existingPRsInfo);
100
- }
101
- if (verbose) {
102
- logInfo('Starting Claude Code query for PR splitting...');
116
+ userPrompt = createPRSplittingPromptWithContext(featureId, contextInfo, existingPRsInfo, context.diffStat, context.changedFiles);
103
117
  }
104
- // Execute query
105
- const result = await executePRSplittingQuery(userPrompt, systemPrompt, config, verbose);
118
+ // Execute agent query
119
+ const result = await executeAgentQuery(userPrompt, systemPrompt, config, verbose);
106
120
  if (!result ||
107
121
  !result.pullRequests ||
108
122
  result.pullRequests.length === 0) {
109
- const errorMsg = 'No pull requests were generated';
110
- logError(errorMsg);
111
- return buildErrorResult(featureId, errorMsg);
123
+ return buildErrorResult(featureId, 'No pull requests were generated');
112
124
  }
113
125
  // Validate PRs
114
126
  const validation = validatePlannedPRs(result.pullRequests);
115
127
  if (!validation.valid) {
116
- const errorMsg = `Invalid PR plan: ${validation.errors.join(', ')}`;
117
- logError(errorMsg);
118
- return buildErrorResult(featureId, errorMsg);
128
+ return buildErrorResult(featureId, `Invalid PR plan: ${validation.errors.join(', ')}`);
119
129
  }
120
- // Sort PRs by dependency
121
130
  const sortedPRs = sortPRsByDependency(result.pullRequests);
122
- // For incremental update mode, clear existing PRs before creating new ones
131
+ if (verbose) {
132
+ logInfo(`\n✅ ${sortedPRs.length} PRs planned:`);
133
+ sortedPRs.forEach((pr) => {
134
+ logInfo(` ${pr.sequence}. ${pr.name} (${pr.branch_name})`);
135
+ if (pr.files) {
136
+ logInfo(` Files: ${pr.files.map((f) => f.path).join(', ')}`);
137
+ }
138
+ });
139
+ }
140
+ // Clear existing PRs for incremental update before creating new ones
123
141
  if (isIncrementalUpdate && hasExistingPRs) {
124
142
  if (verbose) {
125
- logInfo(`🔄 Clearing ${context.existing_pull_requests.length} existing PRs for incremental update...`);
143
+ logInfo(`🔄 Clearing existing PRs for incremental update...`);
126
144
  }
127
145
  await clearPullRequests({ featureId, verbose: false }, true);
128
146
  }
129
- // Create PRs in database with proper dependencies
130
- const prBranchNameToId = new Map();
147
+ // Save PR plan to database
131
148
  const prInputs = [];
149
+ const prBranchNameToId = new Map();
132
150
  for (const pr of sortedPRs) {
133
151
  let basePrId;
134
152
  if (pr.depends_on_branch_name) {
@@ -143,22 +161,14 @@ export const splitFeatureIntoPRs = async (options, config) => {
143
161
  files: pr.files,
144
162
  });
145
163
  }
146
- const created = await createPullRequests({ featureId, verbose: false }, prInputs);
147
- // Build the branch name to ID mapping from created PRs
148
- created.forEach((createdPR) => {
164
+ const createdPRRecords = await createPullRequests({ featureId, verbose: false }, prInputs);
165
+ createdPRRecords.forEach((createdPR) => {
149
166
  if (createdPR.branch_name) {
150
167
  prBranchNameToId.set(createdPR.branch_name, createdPR.id);
151
168
  }
152
169
  });
153
170
  if (verbose) {
154
- const modeLabel = isIncrementalUpdate ? '🔄 Updated' : '✅ Created';
155
- logInfo(`${modeLabel} ${sortedPRs.length} pull requests`);
156
- sortedPRs.forEach((pr) => {
157
- logInfo(` ${pr.sequence}. ${pr.name}`);
158
- if (pr.files) {
159
- logInfo(` Files: ${pr.files.map((f) => f.path).join(', ')}`);
160
- }
161
- });
171
+ logInfo(`\n📝 Saved ${createdPRRecords.length} PR records to database. Awaiting human review before execution.`);
162
172
  }
163
173
  // Log phase completion
164
174
  await logFeaturePhaseEvent({
@@ -167,23 +177,19 @@ export const splitFeatureIntoPRs = async (options, config) => {
167
177
  phase: 'pr_splitting',
168
178
  result: 'success',
169
179
  metadata: {
170
- prs_created: sortedPRs.length,
180
+ prs_planned: sortedPRs.length,
171
181
  pr_names: sortedPRs.map((pr) => pr.name),
172
182
  mode: isIncrementalUpdate ? 'incremental_update' : 'new_planning',
173
183
  feedbacks_addressed: feedbacksContext?.feedbacks.length || 0,
174
184
  timestamp: new Date().toISOString(),
175
185
  },
176
186
  }, verbose);
177
- const summaryPrefix = isIncrementalUpdate
178
- ? `Updated PR plan based on ${feedbacksContext?.feedbacks.length || 0} feedbacks.`
179
- : '';
180
187
  return buildSuccessResult(featureId, sortedPRs, result.summary ||
181
- `${summaryPrefix} ${sortedPRs.length} pull requests for incremental review`.trim(), result.rationale);
188
+ `Split feature into ${sortedPRs.length} pull requests for review`, result.rationale);
182
189
  }
183
190
  catch (error) {
184
191
  const errorMessage = error instanceof Error ? error.message : String(error);
185
192
  logError(`PR splitting failed: ${errorMessage}`);
186
- // Log phase failure
187
193
  await logFeaturePhaseEvent({
188
194
  featureId,
189
195
  eventType: 'phase_failed',
@@ -198,9 +204,9 @@ export const splitFeatureIntoPRs = async (options, config) => {
198
204
  }
199
205
  };
200
206
  /**
201
- * Execute the PR splitting query and parse the result
207
+ * Execute an agent query and parse the result
202
208
  */
203
- async function executePRSplittingQuery(userPrompt, systemPrompt, config, verbose) {
209
+ async function executeAgentQuery(userPrompt, systemPrompt, config, verbose) {
204
210
  let lastAssistantResponse = '';
205
211
  let structuredResult = null;
206
212
  if (verbose) {
@@ -245,7 +251,7 @@ async function executePRSplittingQuery(userPrompt, systemPrompt, config, verbose
245
251
  structuredResult = parseJsonResponse(responseText, verbose);
246
252
  }
247
253
  catch (error) {
248
- logError(`Failed to parse PR splitting result: ${error}`);
254
+ logError(`Failed to parse result: ${error}`);
249
255
  }
250
256
  }
251
257
  else {
@@ -1,12 +1,12 @@
1
1
  import { EdsgerConfig } from '../../types/index.js';
2
2
  /**
3
- * Create the system prompt for PR splitting
3
+ * Create the system prompt for PR splitting: Diff analysis and PR planning
4
4
  */
5
5
  export declare function createPRSplittingSystemPrompt(config: EdsgerConfig, featureId: string): string;
6
6
  /**
7
- * Create the user prompt with context for PR splitting
7
+ * Create the user prompt with diff context for PR splitting
8
8
  */
9
- export declare function createPRSplittingPromptWithContext(featureId: string, contextInfo: string, existingPRsInfo: string): string;
9
+ export declare function createPRSplittingPromptWithContext(featureId: string, contextInfo: string, existingPRsInfo: string, diffStat: string, changedFiles: string[]): string;
10
10
  /**
11
11
  * Format the context information for the prompt
12
12
  */
@@ -38,6 +38,6 @@ export declare function formatExistingPRsForPrompt(pullRequests: Array<{
38
38
  }> | null;
39
39
  }>): string;
40
40
  /**
41
- * Create improvement prompt for re-planning
41
+ * Create improvement prompt for re-planning based on feedback
42
42
  */
43
43
  export declare function createImprovementPrompt(feedback: string, currentPlan: string): string;
@@ -1,10 +1,18 @@
1
1
  /**
2
- * Create the system prompt for PR splitting
2
+ * Create the system prompt for PR splitting: Diff analysis and PR planning
3
3
  */
4
4
  export function createPRSplittingSystemPrompt(config, featureId) {
5
- return `You are an expert software architect specialized in splitting completed feature implementations into multiple smaller, reviewable pull requests.
5
+ return `You are an expert software architect specialized in splitting feature implementations into multiple smaller, reviewable pull requests.
6
6
 
7
- Your task is to analyze the completed code on the feature branch and split it into multiple logical PRs that can be reviewed and merged incrementally.
7
+ Your task is to analyze the actual code changes on the \`dev/${featureId}\` branch compared to \`main\` and create a plan to split them into multiple logical PRs.
8
+
9
+ ## Your Environment
10
+
11
+ You have full access to the repository. You can:
12
+ - Run \`git diff main...dev/${featureId}\` to see all changes
13
+ - Run \`git diff main...dev/${featureId} -- <path>\` to see changes for specific files
14
+ - Read any file in the repository to understand context
15
+ - Run \`git log main...dev/${featureId} --oneline\` to see commit history
8
16
 
9
17
  ## Key Principles
10
18
 
@@ -13,16 +21,15 @@ Your task is to analyze the completed code on the feature branch and split it in
13
21
  3. **Dependency Order**: PRs should be ordered so each one builds on the previous (database first, then API, then UI)
14
22
  4. **Independent Testability**: Each PR should not break the build when merged independently in order
15
23
  5. **Clear Boundaries**: Each PR should have clear scope — one concern per PR
24
+ 6. **Module-Based Splitting**: Split by module/domain, not just by layer. If a feature touches auth, payments, and notifications — those are separate PRs even if they're all "backend"
16
25
 
17
26
  ## PR Branch Naming Convention
18
27
 
19
- Branch names should follow this pattern:
20
- - \`pr/{featureId}/1-{short-description}\`
21
- - \`pr/{featureId}/2-{short-description}\`
28
+ Branch names MUST follow this pattern:
29
+ - \`pr/${featureId}/1-{short-description}\`
30
+ - \`pr/${featureId}/2-{short-description}\`
22
31
  - etc.
23
32
 
24
- Feature ID for this feature: ${featureId}
25
-
26
33
  ## Output Format
27
34
 
28
35
  You MUST respond with a valid JSON object in this exact format:
@@ -57,72 +64,77 @@ You MUST respond with a valid JSON object in this exact format:
57
64
  ## PR Dependencies
58
65
 
59
66
  Use \`depends_on_branch_name\` to specify which PR must be merged before this one:
60
- - First PR: \`"depends_on_branch_name": null\` (merges to main or feat branch)
67
+ - First PR: \`"depends_on_branch_name": null\` (merges directly to main)
61
68
  - Subsequent PRs: \`"depends_on_branch_name": "pr/${featureId}/1-database"\` (depends on previous PR)
62
69
 
63
70
  ## File Assignment Guidelines
64
71
 
65
72
  - **Database migrations and schema**: First PR
66
73
  - **Types and interfaces**: Can go with the PR that introduces them, or in a foundational PR
67
- - **Backend services and API routes**: Middle PRs
68
- - **Frontend components**: Later PRs that depend on backend
74
+ - **Backend services and API routes**: Middle PRs, split by module/domain
75
+ - **Frontend components**: Later PRs that depend on backend, split by feature area
69
76
  - **Tests**: Include with the code they test
70
77
  - **Configuration files**: Group with the feature they configure
71
78
 
72
79
  ## Handling Existing PRs During Re-sync
73
80
 
74
81
  When existing PRs are provided, respect their status:
75
-
76
- - **merged / closed**: NEVER modify these PRs. Do not reassign their files or change their plan.
77
- - If new changes affect files that belong to a merged/closed PR, assign those changes to an existing open PR or create a new PR.
78
- - **pending / branch_created / pr_opened / in_review / approved**: These can be updated — you may reassign files, update descriptions, or adjust scope.
79
- - When creating new PRs to accommodate changes that can't go into merged/closed PRs, use the next available sequence number.
82
+ - **merged / closed**: NEVER modify these PRs. Do not reassign their files.
83
+ - **pending / branch_created / pr_opened / in_review / approved**: These can be updated.
84
+ - When creating new PRs to accommodate changes, use the next available sequence number.
80
85
 
81
86
  ## Important Notes
82
87
 
83
88
  - Every changed file must be assigned to exactly one PR
84
89
  - If the feature is small enough, a single PR is fine
85
90
  - Consider the reviewer's perspective: each PR should tell a coherent story
86
- - Database migrations should be in the earliest PR possible`;
91
+ - Database migrations should be in the earliest PR possible
92
+ - Analyze the ACTUAL diff, not just file names — understand what the changes do`;
87
93
  }
88
94
  /**
89
- * Create the user prompt with context for PR splitting
95
+ * Create the user prompt with diff context for PR splitting
90
96
  */
91
- export function createPRSplittingPromptWithContext(featureId, contextInfo, existingPRsInfo) {
97
+ export function createPRSplittingPromptWithContext(featureId, contextInfo, existingPRsInfo, diffStat, changedFiles) {
92
98
  let prompt = `# PR Splitting Task
93
99
 
94
- Please analyze the completed feature implementation and create a plan to split it into multiple smaller PRs for review.
100
+ Please analyze the actual code changes on the \`dev/${featureId}\` branch and create a plan to split them into multiple smaller PRs for review.
95
101
 
96
102
  ## Feature Context
97
103
 
98
104
  ${contextInfo}
99
105
 
106
+ ## Code Changes Summary
107
+
108
+ ### Diff Stat
109
+ \`\`\`
110
+ ${diffStat || 'No changes detected'}
111
+ \`\`\`
112
+
113
+ ### Changed Files (${changedFiles.length} files)
114
+ ${changedFiles.map((f) => `- ${f}`).join('\n') || 'No files changed'}
115
+
100
116
  `;
101
117
  if (existingPRsInfo) {
102
118
  prompt += `## Existing Pull Requests
103
119
 
104
120
  ${existingPRsInfo}
105
121
 
106
- **Note**: There are already PRs for this feature. Merged/closed PRs are frozen — do not modify them or reassign their files. If new changes touch files from a frozen PR, assign those changes to an active PR or create a new one.
122
+ **Note**: Merged/closed PRs are frozen — do not modify them or reassign their files.
107
123
 
108
124
  `;
109
125
  }
110
126
  prompt += `## Your Task
111
127
 
112
- Based on the feature implementation above:
113
-
114
- 1. **Analyze all changed files**: Understand what was implemented
115
- 2. **Identify logical groupings**: Find files that change together for the same reason
128
+ 1. **Examine the actual diff**: Run \`git diff main...dev/${featureId}\` or targeted diffs for specific files to understand what changed
129
+ 2. **Identify logical groupings**: Find files that change together for the same reason, considering module boundaries
116
130
  3. **Determine dependencies**: Figure out which groups must be merged first
117
131
  4. **Create PR plan**: Define PRs with clear scope, file assignments, and acceptance criteria
118
132
 
119
- Please provide your PR splitting plan as a JSON response following the format specified in the system prompt.
120
-
121
133
  **Remember**:
122
134
  - Every changed file must be assigned to exactly one PR
123
135
  - Smaller, focused PRs are better than large ones
124
- - Order PRs by their dependencies
125
- - Database schema backend services → frontend components is a common pattern`;
136
+ - Split by module/domain, not just by layer
137
+ - Order PRs by their dependencies`;
126
138
  return prompt;
127
139
  }
128
140
  /**
@@ -181,7 +193,7 @@ export function formatExistingPRsForPrompt(pullRequests) {
181
193
  return result;
182
194
  }
183
195
  /**
184
- * Create improvement prompt for re-planning
196
+ * Create improvement prompt for re-planning based on feedback
185
197
  */
186
198
  export function createImprovementPrompt(feedback, currentPlan) {
187
199
  return `# PR Splitting Improvement
@@ -0,0 +1,25 @@
1
+ import { type ChecklistVerificationResult } from '../technical-design-verification/agent.js';
2
+ export interface TechnicalDesignResult {
3
+ featureId: string;
4
+ technicalDesign: string | null;
5
+ status: 'success' | 'error';
6
+ summary: string;
7
+ verificationResult?: ChecklistVerificationResult;
8
+ iterations?: number;
9
+ data?: {
10
+ checklist_item_results?: any[];
11
+ [key: string]: any;
12
+ };
13
+ }
14
+ /**
15
+ * Build successful design result
16
+ */
17
+ export declare function buildDesignResult(featureId: string, technicalDesign: string, summary: string, iterations: number, checklistItemResults?: any[]): TechnicalDesignResult;
18
+ /**
19
+ * Build verification failure result
20
+ */
21
+ export declare function buildVerificationFailureResult(featureId: string, technicalDesign: string, verificationResult: ChecklistVerificationResult, iterations: number): TechnicalDesignResult;
22
+ /**
23
+ * Build error result when no design was generated
24
+ */
25
+ export declare function buildNoResultsError(featureId: string): TechnicalDesignResult;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Build successful design result
3
+ */
4
+ export function buildDesignResult(featureId, technicalDesign, summary, iterations, checklistItemResults) {
5
+ return {
6
+ featureId,
7
+ technicalDesign,
8
+ status: 'success',
9
+ summary,
10
+ iterations,
11
+ data: checklistItemResults
12
+ ? { checklist_item_results: checklistItemResults }
13
+ : undefined,
14
+ };
15
+ }
16
+ /**
17
+ * Build verification failure result
18
+ */
19
+ export function buildVerificationFailureResult(featureId, technicalDesign, verificationResult, iterations) {
20
+ return {
21
+ featureId,
22
+ technicalDesign,
23
+ status: 'error',
24
+ summary: `Checklist verification failed after ${iterations} iterations: ${verificationResult.summary}`,
25
+ verificationResult,
26
+ iterations,
27
+ };
28
+ }
29
+ /**
30
+ * Build error result when no design was generated
31
+ */
32
+ export function buildNoResultsError(featureId) {
33
+ return {
34
+ featureId,
35
+ technicalDesign: null,
36
+ status: 'error',
37
+ summary: 'Failed to generate technical design - no valid result received',
38
+ };
39
+ }
@@ -0,0 +1,21 @@
1
+ import { EdsgerConfig } from '../../types/index.js';
2
+ import { ChecklistPhaseContext } from '../../services/checklist.js';
3
+ export interface TechnicalDesignOptions {
4
+ featureId: string;
5
+ verbose?: boolean;
6
+ maxVerificationIterations?: number;
7
+ }
8
+ export interface TechnicalDesignResult {
9
+ featureId: string;
10
+ technicalDesign: string | null;
11
+ status: 'success' | 'error';
12
+ summary: string;
13
+ verificationResult?: any;
14
+ iterations?: number;
15
+ savedViaHttp?: boolean;
16
+ data?: {
17
+ checklist_item_results?: any[];
18
+ [key: string]: any;
19
+ };
20
+ }
21
+ export declare const generateTechnicalDesign: (options: TechnicalDesignOptions, config: EdsgerConfig, checklistContext?: ChecklistPhaseContext | null) => Promise<TechnicalDesignResult>;